diff --git a/.github/workflows/ios_review_pull_request.yml b/.github/workflows/ios_review_pull_request.yml index e9d43880..62ab844e 100644 --- a/.github/workflows/ios_review_pull_request.yml +++ b/.github/workflows/ios_review_pull_request.yml @@ -26,7 +26,7 @@ jobs: review_pull_request: name: Review pull request runs-on: macOS-latest - timeout-minutes: 30 + timeout-minutes: 60 steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.10.0 diff --git a/build.gradle.kts b/build.gradle.kts index 0631326c..6f8b7625 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ buildscript { classpath(Dependency.KOVER) classpath(Dependency.KOTLIN_SERIALIZATION) classpath(Dependency.BUILD_KONFIG) + classpath(Dependency.MOKO_RESOURCES_GENERATOR) } } diff --git a/buildSrc/src/main/kotlin/appPackage/Dependency.kt b/buildSrc/src/main/kotlin/appPackage/Dependency.kt index 103c9c3f..53b7a6c0 100644 --- a/buildSrc/src/main/kotlin/appPackage/Dependency.kt +++ b/buildSrc/src/main/kotlin/appPackage/Dependency.kt @@ -40,4 +40,8 @@ object Dependency { const val KOTEST_FRAMEWORK = "io.kotest:kotest-framework-engine:${Version.KOTEST}" const val KOTEST_ASSERTIONS = "io.kotest:kotest-assertions-core:${Version.KOTEST}" const val KOTEST_PROPERTY = "io.kotest:kotest-property:${Version.KOTEST}" + + // Resources + const val MOKO_RESOURCES_GENERATOR = "dev.icerock.moko:resources-generator:${Version.MOKO_RESOURCES}" + const val MOKO_RESOURCES = "dev.icerock.moko:resources:${Version.MOKO_RESOURCES}" } diff --git a/buildSrc/src/main/kotlin/appPackage/Plugin.kt b/buildSrc/src/main/kotlin/appPackage/Plugin.kt index 2315c886..0f223195 100644 --- a/buildSrc/src/main/kotlin/appPackage/Plugin.kt +++ b/buildSrc/src/main/kotlin/appPackage/Plugin.kt @@ -16,4 +16,6 @@ object Plugin { const val MOCKMP = "org.kodein.mock.mockmp" const val KSP = "com.google.devtools.ksp" + + const val MOKO_RESOURCES = "dev.icerock.mobile.multiplatform-resources" } diff --git a/buildSrc/src/main/kotlin/appPackage/Versions.kt b/buildSrc/src/main/kotlin/appPackage/Versions.kt index 08b8dae9..6e2f3d44 100644 --- a/buildSrc/src/main/kotlin/appPackage/Versions.kt +++ b/buildSrc/src/main/kotlin/appPackage/Versions.kt @@ -18,4 +18,5 @@ object Version { const val MOCKMP = "1.6.0" const val KSP = "1.7.10-1.0.6" const val KOTEST = "5.5.1" + const val MOKO_RESOURCES = "0.20.1" } diff --git a/iosApp/Survey.xcodeproj/project.pbxproj b/iosApp/Survey.xcodeproj/project.pbxproj index 50370500..63ddddbc 100644 --- a/iosApp/Survey.xcodeproj/project.pbxproj +++ b/iosApp/Survey.xcodeproj/project.pbxproj @@ -30,7 +30,6 @@ 09636AFD28D484CA00A5CB97 /* R+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09636AFC28D484CA00A5CB97 /* R+SwiftUI.swift */; }; 09636B0028D4860F00A5CB97 /* PrimaryTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09636AFF28D4860F00A5CB97 /* PrimaryTextField.swift */; }; 09636B0228D4876100A5CB97 /* PrimaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09636B0128D4876100A5CB97 /* PrimaryButton.swift */; }; - 09636B0628D48C3C00A5CB97 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 09636B0828D48C3C00A5CB97 /* Localizable.strings */; }; 09636B0B28D48CFD00A5CB97 /* Typealiases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09636B0A28D48CFD00A5CB97 /* Typealiases.swift */; }; 09636B0D28D4917200A5CB97 /* OverlayButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09636B0C28D4917200A5CB97 /* OverlayButton.swift */; }; 09636B0F28D80B8500A5CB97 /* View+KeyboardDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09636B0E28D80B8500A5CB97 /* View+KeyboardDismiss.swift */; }; @@ -47,11 +46,11 @@ 0982A7A62921DFE800FC1976 /* ResetPasswordSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A7A32921DFE700FC1976 /* ResetPasswordSpec.swift */; }; 0982A7DF29222EA800FC1976 /* LoginSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A7DE29222EA800FC1976 /* LoginSpec.swift */; }; 0982A7E029222EB900FC1976 /* ViewId+Splash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09495F6728FF97490036BDFB /* ViewId+Splash.swift */; }; - 0982A7E129222EBB00FC1976 /* ViewId+Splash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09495F6728FF97490036BDFB /* ViewId+Splash.swift */; }; 0982A7E329222EFF00FC1976 /* RouteCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A7E229222EFF00FC1976 /* RouteCoordinator.swift */; }; 0982A7E429222F2000FC1976 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09495F6E28FFDE8C0036BDFB /* LoginView.swift */; }; 0982A7E72922357C00FC1976 /* Font+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09636AF828D47A4D00A5CB97 /* Font+Extensions.swift */; }; 0982A7EB29223C3A00FC1976 /* SurveyLoadingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A7EA29223C3A00FC1976 /* SurveyLoadingSpec.swift */; }; + 0982A7F6292371F000FC1976 /* StringResource+Localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A7F5292371F000FC1976 /* StringResource+Localized.swift */; }; 0982A80229278E9000FC1976 /* SurveySelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A7FA29278E9000FC1976 /* SurveySelectionView.swift */; }; 0982A80329278E9000FC1976 /* SurveyHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A7FC29278E9000FC1976 /* SurveyHeaderView.swift */; }; 0982A80429278E9000FC1976 /* SurveyItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A7FD29278E9000FC1976 /* SurveyItemView.swift */; }; @@ -59,6 +58,7 @@ 0982A80629278E9000FC1976 /* SurveyItemLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A80029278E9000FC1976 /* SurveyItemLoading.swift */; }; 0982A80729278E9000FC1976 /* SurveyHeaderLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A80129278E9000FC1976 /* SurveyHeaderLoading.swift */; }; 0982A80829278EC200FC1976 /* LoginFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0982A7EE29223DA600FC1976 /* LoginFlow.swift */; }; + 0982A809292B6BCA00FC1976 /* ViewId+Splash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09495F6728FF97490036BDFB /* ViewId+Splash.swift */; }; 09CE770C28E191B400EAA9EE /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CE770B28E191B400EAA9EE /* AppCoordinator.swift */; }; 09CE770F28E1922000EAA9EE /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CE770E28E1922000EAA9EE /* Screen.swift */; }; 09CE771328E1A94600EAA9EE /* ResetPasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CE771228E1A94600EAA9EE /* ResetPasswordView.swift */; }; @@ -158,7 +158,6 @@ 09636AFC28D484CA00A5CB97 /* R+SwiftUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "R+SwiftUI.swift"; sourceTree = ""; }; 09636AFF28D4860F00A5CB97 /* PrimaryTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryTextField.swift; sourceTree = ""; }; 09636B0128D4876100A5CB97 /* PrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryButton.swift; sourceTree = ""; }; - 09636B0728D48C3C00A5CB97 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 09636B0A28D48CFD00A5CB97 /* Typealiases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typealiases.swift; sourceTree = ""; }; 09636B0C28D4917200A5CB97 /* OverlayButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayButton.swift; sourceTree = ""; }; 09636B0E28D80B8500A5CB97 /* View+KeyboardDismiss.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+KeyboardDismiss.swift"; sourceTree = ""; }; @@ -174,6 +173,7 @@ 0982A7E229222EFF00FC1976 /* RouteCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteCoordinator.swift; sourceTree = ""; }; 0982A7EA29223C3A00FC1976 /* SurveyLoadingSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SurveyLoadingSpec.swift; sourceTree = ""; }; 0982A7EE29223DA600FC1976 /* LoginFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginFlow.swift; sourceTree = ""; }; + 0982A7F5292371F000FC1976 /* StringResource+Localized.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StringResource+Localized.swift"; sourceTree = ""; }; 0982A7FA29278E9000FC1976 /* SurveySelectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SurveySelectionView.swift; sourceTree = ""; }; 0982A7FC29278E9000FC1976 /* SurveyHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SurveyHeaderView.swift; sourceTree = ""; }; 0982A7FD29278E9000FC1976 /* SurveyItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SurveyItemView.swift; sourceTree = ""; }; @@ -340,14 +340,6 @@ path = ViewModifiers; sourceTree = ""; }; - 09636B0328D48BA100A5CB97 /* Localizations */ = { - isa = PBXGroup; - children = ( - 09636B0828D48C3C00A5CB97 /* Localizable.strings */, - ); - path = Localizations; - sourceTree = ""; - }; 09636B0928D48CE800A5CB97 /* Typealiases */ = { isa = PBXGroup; children = ( @@ -459,6 +451,14 @@ path = Flows; sourceTree = ""; }; + 0982A7F4292371F000FC1976 /* MokoResources */ = { + isa = PBXGroup; + children = ( + 0982A7F5292371F000FC1976 /* StringResource+Localized.swift */, + ); + path = MokoResources; + sourceTree = ""; + }; 0982A7F929278E9000FC1976 /* SurveySelection */ = { isa = PBXGroup; children = ( @@ -576,6 +576,7 @@ 0F3BE49893E55BC4638B90F0 /* Extensions */ = { isa = PBXGroup; children = ( + 0982A7F4292371F000FC1976 /* MokoResources */, 09636AF728D47A3800A5CB97 /* SwiftUI */, 0F4C0078B1B4D7C1871DDE54 /* Foundation */, ); @@ -785,7 +786,6 @@ A60D6F6D69B2571A295C0A9E /* Resources */ = { isa = PBXGroup; children = ( - 09636B0328D48BA100A5CB97 /* Localizations */, 09636AEF28D4777E00A5CB97 /* Fonts */, 2CF27D885DD8507482E726E1 /* Assets */, 6FD75B0789EE724AD6F6C58D /* LaunchScreen */, @@ -971,6 +971,7 @@ FF76644469CE368D62BA827C /* Frameworks */, CC99E871FA1DDA8C64075BC0 /* Copy GoogleService-Info.plist */, 69E0E5555896889BE38A4490 /* [CP] Embed Pods Frameworks */, + 0982A7F72923731100FC1976 /* Moko Resources */, ); buildRules = ( ); @@ -1050,7 +1051,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 09636B0628D48C3C00A5CB97 /* Localizable.strings in Resources */, 09636AF228D4779400A5CB97 /* NeuzeitSLTStd-Book.otf in Resources */, 09636AF328D4779400A5CB97 /* NeuzeitSLTStd-BookHeavy.otf in Resources */, CA1F500707F2936D39825740 /* Assets.xcassets in Resources */, @@ -1090,6 +1090,24 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 0982A7F72923731100FC1976 /* Moko Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Moko Resources"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$SRCROOT/../gradlew\" -p \"$SRCROOT/../\" :Shared:copyFrameworkResourcesToApp \\\n -Pmoko.resources.PLATFORM_NAME=$PLATFORM_NAME \\\n -Pmoko.resources.CONFIGURATION=\"${CONFIGURATION}\" \\\n -Pmoko.resources.BUILT_PRODUCTS_DIR=\"${BUILT_PRODUCTS_DIR}\" \\\n -Pmoko.resources.CONTENTS_FOLDER_PATH=$CONTENTS_FOLDER_PATH\n"; + }; 364701D01D6D730E26E263DF /* R.swift */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -1293,7 +1311,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "PATH_TO_GOOGLE_PLISTS=\"$SRCROOT/$PROJECT_NAME/Configurations/Plists/GoogleService\"\n\ncase \"${CONFIGURATION}\" in\n\"Debug Staging\" | \"Release Staging\" )\ncp -r \"$PATH_TO_GOOGLE_PLISTS/Staging/GoogleService-Info.plist\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\n;;\n\"Debug Production\" | \"Release Production\" )\ncp -r \"$PATH_TO_GOOGLE_PLISTS/Production/GoogleService-Info.plist\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\n;;\n*)\n;;\nesac"; + shellScript = "PATH_TO_GOOGLE_PLISTS=\"$SRCROOT/$PROJECT_NAME/Configurations/Plists/GoogleService\"\n\ncase \"${CONFIGURATION}\" in\n\"Debug Staging\" | \"Release Staging\" )\ncp -r \"$PATH_TO_GOOGLE_PLISTS/Staging/GoogleService-Info.plist\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\n;;\n\"Debug Production\" | \"Release Production\" )\ncp -r \"$PATH_TO_GOOGLE_PLISTS/Production/GoogleService-Info.plist\" \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist\"\n;;\n*)\n;;\nesac\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -1341,6 +1359,7 @@ 09CE772528E2C1C600EAA9EE /* View+HideBackButtonTitle.swift in Sources */, 09CE771528E1AB0B00EAA9EE /* ViewId+ResetPassword.swift in Sources */, 09636B0B28D48CFD00A5CB97 /* Typealiases.swift in Sources */, + 0982A7F6292371F000FC1976 /* StringResource+Localized.swift in Sources */, 09636B0028D4860F00A5CB97 /* PrimaryTextField.swift in Sources */, 09495F6628FF97080036BDFB /* SplashView.swift in Sources */, 870924447B8C177450E77414 /* Optional+Unwrap.swift in Sources */, @@ -1358,6 +1377,7 @@ 0982A7A62921DFE800FC1976 /* ResetPasswordSpec.swift in Sources */, 09CE772028E1BBC400EAA9EE /* ViewId+ResetPassword.swift in Sources */, 09495F3328EC44330036BDFB /* SurveyLoadingScreen.swift in Sources */, + 0982A809292B6BCA00FC1976 /* ViewId+Splash.swift in Sources */, 09495F1828E4461C0036BDFB /* SurveySelectionScreen.swift in Sources */, 09636B3228D8272200A5CB97 /* ScreenProtocol.swift in Sources */, 0982A79E2921DF7E00FC1976 /* SurveySelectionSpec.swift in Sources */, @@ -1365,7 +1385,6 @@ 09636B2128D821EA00A5CB97 /* ViewId.swift in Sources */, 0982A7DF29222EA800FC1976 /* LoginSpec.swift in Sources */, 09CE771F28E1BB4700EAA9EE /* ResetPasswordScreen.swift in Sources */, - 0982A7E129222EBB00FC1976 /* ViewId+Splash.swift in Sources */, 09636B2128D821EA00A5CB97 /* ViewId.swift in Sources */, 09495F7F2900017E0036BDFB /* ViewId+SurveySelection.swift in Sources */, 09495F0C28E441900036BDFB /* ViewId+SurveySelection.swift in Sources */, @@ -1403,17 +1422,6 @@ }; /* End PBXTargetDependency section */ -/* Begin PBXVariantGroup section */ - 09636B0828D48C3C00A5CB97 /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 09636B0728D48C3C00A5CB97 /* en */, - ); - name = Localizable.strings; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ 03C0597282ACB0F090BCE8C5 /* Debug Production */ = { isa = XCBuildConfiguration; diff --git a/iosApp/Survey/Configurations/Plists/Info.plist b/iosApp/Survey/Configurations/Plists/Info.plist index c505504a..1a31ca66 100644 --- a/iosApp/Survey/Configurations/Plists/Info.plist +++ b/iosApp/Survey/Configurations/Plists/Info.plist @@ -44,5 +44,9 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + CFBundleLocalizations + + en + diff --git a/iosApp/Survey/Resources/Localizations/en.lproj/Localizable.strings b/iosApp/Survey/Resources/Localizations/en.lproj/Localizable.strings deleted file mode 100644 index 5eedc0b7..00000000 --- a/iosApp/Survey/Resources/Localizations/en.lproj/Localizable.strings +++ /dev/null @@ -1,15 +0,0 @@ -/* - Localizable.strings - Survey - - Created by Bliss on 16/9/22. - Copyright © 2022 Nimble. All rights reserved. -*/ - -"login.field.email" = "Email"; -"login.field.password" = "Password"; -"login.button.login" = "Log in"; -"login.button.forgot" = "Forgot?"; -"reset.password.field.email" = "Email"; -"reset.password.text.instruction" = "Enter your email to receive instructions for resetting your password."; -"reset.password.button.reset" = "Reset"; diff --git a/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView.swift b/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView.swift index d430beef..8c7c4766 100644 --- a/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView.swift +++ b/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView.swift @@ -77,7 +77,7 @@ struct LoginView: View { } var loginField: some View { - TextField(Localize.loginFieldEmail(), text: $email) + TextField(String.localizeId.login_fields_email(), text: $email) .keyboardType(.emailAddress) .primaryTextField() .accessibility(.login(.emailField)) @@ -85,10 +85,10 @@ struct LoginView: View { var passwordField: some View { HStack { - SecureField(Localize.loginFieldPassword(), text: $password) + SecureField(String.localizeId.login_fields_password(), text: $password) .accessibility(.login(.passwordField)) if password.isEmpty { - Button(Localize.loginButtonForgot()) { + Button(String.localizeId.login_button_forgot()) { coordinator.showResetPassword() } .overlayButton() @@ -106,7 +106,7 @@ struct LoginView: View { coordinator.showHomeLoading() } } label: { - Text(Localize.loginButtonLogin()) + Text(String.localizeId.login_button_login()) .frame(maxWidth: .infinity) .primaryButton() .accessibility(.login(.loginButton)) diff --git a/iosApp/Survey/Sources/Presentation/Modules/ResetPassword/ResetPasswordView.swift b/iosApp/Survey/Sources/Presentation/Modules/ResetPassword/ResetPasswordView.swift index ffb7cc07..371d765a 100644 --- a/iosApp/Survey/Sources/Presentation/Modules/ResetPassword/ResetPasswordView.swift +++ b/iosApp/Survey/Sources/Presentation/Modules/ResetPassword/ResetPasswordView.swift @@ -31,7 +31,7 @@ struct ResetPasswordView: View { Assets.logoWhite.image - Text(Localize.resetPasswordTextInstruction()) + Text(String.localizeId.reset_password_text_instruction()) .multilineTextAlignment(.center) Spacer().frame(maxHeight: 70.0) @@ -51,7 +51,7 @@ struct ResetPasswordView: View { } var emailField: some View { - TextField(Localize.resetPasswordFieldEmail(), text: $email) + TextField(String.localizeId.reset_password_field_email(), text: $email) .keyboardType(.emailAddress) .primaryTextField() .accessibility(.resetPassword(.emailField)) @@ -59,7 +59,7 @@ struct ResetPasswordView: View { var resetButton: some View { Button {} label: { - Text(Localize.resetPasswordButtonReset()) + Text(String.localizeId.reset_password_button_reset()) .frame(maxWidth: .infinity) .primaryButton() .accessibility(.resetPassword(.resetButton)) diff --git a/iosApp/Survey/Sources/Supports/Extensions/MokoResources/StringResource+Localized.swift b/iosApp/Survey/Sources/Supports/Extensions/MokoResources/StringResource+Localized.swift new file mode 100644 index 00000000..2d720113 --- /dev/null +++ b/iosApp/Survey/Sources/Supports/Extensions/MokoResources/StringResource+Localized.swift @@ -0,0 +1,21 @@ +// +// StringResource+Localized.swift +// Survey +// +// Created by Bliss on 18/10/22. +// Copyright © 2022 Nimble. All rights reserved. +// + +import Shared + +extension String { + + static let localizeId = MR.strings() +} + +extension StringResource { + + func callAsFunction() -> String { + desc().localized() + } +} diff --git a/iosApp/Survey/Sources/Supports/Helpers/Typealiases/Typealiases.swift b/iosApp/Survey/Sources/Supports/Helpers/Typealiases/Typealiases.swift index bb74e195..6102fb54 100644 --- a/iosApp/Survey/Sources/Supports/Helpers/Typealiases/Typealiases.swift +++ b/iosApp/Survey/Sources/Supports/Helpers/Typealiases/Typealiases.swift @@ -8,4 +8,3 @@ typealias Assets = R.image typealias Fonts = R.font -typealias Localize = R.string.localizable diff --git a/nimble-jsonapi-kotlin b/nimble-jsonapi-kotlin index 04cf06cd..534b7d07 160000 --- a/nimble-jsonapi-kotlin +++ b/nimble-jsonapi-kotlin @@ -1 +1 @@ -Subproject commit 04cf06cd7e72623e11f26dcf0c56356f576b1596 +Subproject commit 534b7d07bd771827f36507a10a10cf1a13cd4cfe diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index cb195b45..12436cba 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -12,6 +12,7 @@ plugins { id(Plugin.MOCKMP).version(Version.MOCKMP) id(Plugin.KSP).version(Version.KSP) id(Plugin.KOVER) + id(Plugin.MOKO_RESOURCES) } version = "1.0" @@ -30,6 +31,7 @@ kotlin { podfile = project.file("../iosApp/Podfile") framework { baseName = "Shared" + export(Dependency.MOKO_RESOURCES) } xcodeConfigurationToNativeBuildType["Debug Staging"] = NativeBuildType.DEBUG xcodeConfigurationToNativeBuildType["Debug Production"] = NativeBuildType.DEBUG @@ -93,6 +95,10 @@ kotlin { } } +dependencies { + commonMainApi(Dependency.MOKO_RESOURCES) +} + android { compileSdk = Android.COMPILE_SDK sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") @@ -105,7 +111,7 @@ android { unitTests.all { if (it.name == "testReleaseUnitTest") { it.extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) { - isDisabled.set(true) + isDisabled.set(true) } } it.extensions.configure(kotlinx.kover.api.KoverTaskExtension::class) { @@ -114,7 +120,7 @@ android { } } - detekt { +detekt { source = files( "./" ) @@ -208,3 +214,7 @@ kover { } } } + +multiplatformResources { + multiplatformResourcesPackage = "co.nimblehq.blisskmmic" +} diff --git a/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/presentation/helpers/MokoLocalize.kt b/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/presentation/helpers/MokoLocalize.kt new file mode 100644 index 00000000..1a414940 --- /dev/null +++ b/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/presentation/helpers/MokoLocalize.kt @@ -0,0 +1,17 @@ +package co.nimblehq.blisskmmic.presentation.helpers + +import co.nimblehq.blisskmmic.MR +import dev.icerock.moko.resources.AssetResource +import dev.icerock.moko.resources.ResourceContainer +import dev.icerock.moko.resources.StringResource +import dev.icerock.moko.resources.desc.Resource +import dev.icerock.moko.resources.desc.StringDesc + +class MokoLocalize { + fun localize(id: String?): StringDesc? { + if (id == MR.strings.common_error.toString()) { + return StringDesc.Resource(MR.strings.common_error) + } + return null + } +} diff --git a/shared/src/commonMain/resources/MR/base/strings.xml b/shared/src/commonMain/resources/MR/base/strings.xml new file mode 100644 index 00000000..4a08e601 --- /dev/null +++ b/shared/src/commonMain/resources/MR/base/strings.xml @@ -0,0 +1,11 @@ + + + An error occurred. Please try again. + Email + Password + Log in + Forgot? + Email + Enter your email to receive instructions for resetting your password. + Reset + diff --git a/shared/src/commonTest/kotlin/co/nimblehq/blisskmmic/presentation/helpers/MokoLocalizeTest.kt b/shared/src/commonTest/kotlin/co/nimblehq/blisskmmic/presentation/helpers/MokoLocalizeTest.kt new file mode 100644 index 00000000..9f587691 --- /dev/null +++ b/shared/src/commonTest/kotlin/co/nimblehq/blisskmmic/presentation/helpers/MokoLocalizeTest.kt @@ -0,0 +1,21 @@ +package co.nimblehq.blisskmmic.presentation.helpers + +import co.nimblehq.blisskmmic.MR +import dev.icerock.moko.resources.desc.Resource +import dev.icerock.moko.resources.desc.StringDesc +import io.kotest.matchers.shouldBe +import kotlin.test.Test + +class MokoLocalizeTest { + + @Test + fun `When calling localize with common_error desc, it returns correct StringDesc`() { + MokoLocalize().localize(MR.strings.common_error.toString()) shouldBe + StringDesc.Resource(MR.strings.common_error) + } + + @Test + fun `When calling localize with an unknown desc, it returns null`() { + MokoLocalize().localize("unknown") shouldBe null + } +} diff --git a/shared/src/commonTest/kotlin/co/nimblehq/blisskmmic/presentation/modules/login/LoginViewModelTest.kt b/shared/src/commonTest/kotlin/co/nimblehq/blisskmmic/presentation/modules/login/LoginViewModelTest.kt index a2628648..60a4427a 100644 --- a/shared/src/commonTest/kotlin/co/nimblehq/blisskmmic/presentation/modules/login/LoginViewModelTest.kt +++ b/shared/src/commonTest/kotlin/co/nimblehq/blisskmmic/presentation/modules/login/LoginViewModelTest.kt @@ -5,14 +5,18 @@ import co.nimblehq.blisskmmic.domain.usecase.LogInUseCase import io.kotest.matchers.shouldBe import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.newSingleThreadContext import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain -import org.kodein.mock.* +import org.kodein.mock.Fake +import org.kodein.mock.Mock import org.kodein.mock.tests.TestsWithMocks -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test @ExperimentalCoroutinesApi class LoginViewModelTest : TestsWithMocks() {