diff --git a/CHANGELOG.md b/CHANGELOG.md index e5911f3..8ea00d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Pagination support for item tags list -- CodedError system with `NATI-XXXX` prefixed error codes +- CodedError system with `NATIVEAPPTEMPLATE-XXXX` prefixed error codes - App version display in settings - Design system constants (spacing, animation, glass, layout, corner radius) - GlassButtonStyle and GlassCard components with glassmorphism styling diff --git a/CLAUDE.md b/CLAUDE.md index 6c45041..58353cf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -107,18 +107,17 @@ NativeAppTemplate/ ``` ### Error Handling (CodedError System) -All errors use the `CodedError` protocol in `NativeAppTemplate/Common/Errors/`. Error codes use the `NATI-XXXX` prefix (NativeAppTemplate iOS) to distinguish from Android (`NATA-XXXX`). +All errors use the `CodedError` protocol in `NativeAppTemplate/Common/Errors/`. Error codes share the `NATIVEAPPTEMPLATE-XXXX` prefix across iOS and Android. | Range | Type | File | |-------|------|------| -| NATI-1xxx | App/general errors | `AppError.swift` | -| NATI-2xxx | API/network errors | `NativeAppTemplateAPIError.swift` | -| NATI-3xxx | NFC/scan errors | `NFCError.swift` | +| NATIVEAPPTEMPLATE-1xxx | App/general errors | `AppError.swift` | +| NATIVEAPPTEMPLATE-2xxx | API/network errors | `NativeAppTemplateAPIError.swift` | - New error types must conform to `CodedError` and be placed in `Common/Errors/` - Use `error.codedDescription` (not `error.localizedDescription`) in all error messages - Use `Message(error: error)` convenience to post errors to `MessageBus` -- Error code numbers must match across iOS and Android (only the prefix differs) +- Error code numbers must match across iOS and Android ### Dependencies (Swift Package Manager) - KeychainAccess (4.2.2) - Secure credential storage diff --git a/NativeAppTemplate/Common/Errors/AppError.swift b/NativeAppTemplate/Common/Errors/AppError.swift index 7a1de92..b8b7c7c 100644 --- a/NativeAppTemplate/Common/Errors/AppError.swift +++ b/NativeAppTemplate/Common/Errors/AppError.swift @@ -16,7 +16,7 @@ enum AppError: CodedError { var errorCode: String { switch self { case .unexpected: - "NATI-1001" + "NATIVEAPPTEMPLATE-1001" } } diff --git a/NativeAppTemplate/Common/Errors/CodedError.swift b/NativeAppTemplate/Common/Errors/CodedError.swift index 11380b8..5fa12d9 100644 --- a/NativeAppTemplate/Common/Errors/CodedError.swift +++ b/NativeAppTemplate/Common/Errors/CodedError.swift @@ -3,8 +3,7 @@ // NativeAppTemplate // -// Error codes use the `NATI-XXXX` prefix (NativeAppTemplate iOS). -// Android uses `NATA-XXXX`. +// Error codes share the `NATIVEAPPTEMPLATE-XXXX` prefix across iOS and Android. // Ranges: 1xxx App errors, 2xxx API errors. import Foundation diff --git a/NativeAppTemplate/Common/Errors/NativeAppTemplateAPIError.swift b/NativeAppTemplate/Common/Errors/NativeAppTemplateAPIError.swift index 7c90fd8..9f64207 100644 --- a/NativeAppTemplate/Common/Errors/NativeAppTemplateAPIError.swift +++ b/NativeAppTemplate/Common/Errors/NativeAppTemplateAPIError.swift @@ -15,15 +15,15 @@ enum NativeAppTemplateAPIError: CodedError { nonisolated var errorCode: String { switch self { case .requestFailed: - "NATI-2001" + "NATIVEAPPTEMPLATE-2001" case .processingError: - "NATI-2002" + "NATIVEAPPTEMPLATE-2002" case .responseMissingRequiredMeta: - "NATI-2003" + "NATIVEAPPTEMPLATE-2003" case .responseHasIncorrectNumberOfElements: - "NATI-2004" + "NATIVEAPPTEMPLATE-2004" case .noData: - "NATI-2005" + "NATIVEAPPTEMPLATE-2005" } } diff --git a/NativeAppTemplate/Constants.swift b/NativeAppTemplate/Constants.swift index 08b82fe..3de608c 100644 --- a/NativeAppTemplate/Constants.swift +++ b/NativeAppTemplate/Constants.swift @@ -105,9 +105,9 @@ enum NativeAppTemplateConstants { enum Strings { #if DEBUG private static let env = ProcessInfo.processInfo.environment - static let scheme: String = env["NATEMPLATE_API_SCHEME"] ?? "https" - static let domain: String = env["NATEMPLATE_API_DOMAIN"] ?? "api.nativeapptemplate.com" - static let port: String = env["NATEMPLATE_API_PORT"] ?? "" + static let scheme: String = env["NATIVEAPPTEMPLATE_API_SCHEME"] ?? "https" + static let domain: String = env["NATIVEAPPTEMPLATE_API_DOMAIN"] ?? "api.nativeapptemplate.com" + static let port: String = env["NATIVEAPPTEMPLATE_API_PORT"] ?? "" #else static let scheme: String = "https" static let domain: String = "api.nativeapptemplate.com" diff --git a/NativeAppTemplateTests/UI/Shop Detail/ShopDetailViewModelTest.swift b/NativeAppTemplateTests/UI/Shop Detail/ShopDetailViewModelTest.swift index 3bbbe0b..71a8d19 100644 --- a/NativeAppTemplateTests/UI/Shop Detail/ShopDetailViewModelTest.swift +++ b/NativeAppTemplateTests/UI/Shop Detail/ShopDetailViewModelTest.swift @@ -111,7 +111,7 @@ struct ShopDetailViewModelTest { // swiftlint:disable:this type_body_length } await reloadTask.value - #expect(viewModel.messageBus.currentMessage?.message == "[NATI-2001] \(message) [Status: \(httpResponseCode)]") + #expect(viewModel.messageBus.currentMessage?.message == "[NATIVEAPPTEMPLATE-2001] \(message) [Status: \(httpResponseCode)]") #expect(viewModel.shouldDismiss) } diff --git a/NativeAppTemplateTests/UI/Shop List/ShopCreateViewModelTest.swift b/NativeAppTemplateTests/UI/Shop List/ShopCreateViewModelTest.swift index 3562a6d..cd7f383 100644 --- a/NativeAppTemplateTests/UI/Shop List/ShopCreateViewModelTest.swift +++ b/NativeAppTemplateTests/UI/Shop List/ShopCreateViewModelTest.swift @@ -180,7 +180,7 @@ struct ShopCreateViewModelTest { } await createShopTask.value - #expect(viewModel.messageBus.currentMessage?.message == "[NATI-2001] \(message) [Status: \(httpResponseCode)]") + #expect(viewModel.messageBus.currentMessage?.message == "[NATIVEAPPTEMPLATE-2001] \(message) [Status: \(httpResponseCode)]") #expect(viewModel.isCreating) #expect(shopRepository.shops.count == createdShopsCount) #expect(viewModel.shouldDismiss) @@ -215,7 +215,7 @@ struct ShopCreateViewModelTest { } await createShopTask.value - #expect(viewModel.messageBus.currentMessage?.message == "[NATI-2001] \(message) [Status: \(httpResponseCode)]") + #expect(viewModel.messageBus.currentMessage?.message == "[NATIVEAPPTEMPLATE-2001] \(message) [Status: \(httpResponseCode)]") #expect(viewModel.isCreating) #expect(shopRepository.shops.count == createdShopsCount) #expect(viewModel.shouldDismiss == false) diff --git a/NativeAppTemplateTests/UI/Shop Settings/ShopBasicSettingsViewModelTest.swift b/NativeAppTemplateTests/UI/Shop Settings/ShopBasicSettingsViewModelTest.swift index 40818d8..4d75ace 100644 --- a/NativeAppTemplateTests/UI/Shop Settings/ShopBasicSettingsViewModelTest.swift +++ b/NativeAppTemplateTests/UI/Shop Settings/ShopBasicSettingsViewModelTest.swift @@ -250,7 +250,7 @@ struct ShopBasicSettingsViewModelTest { } await reloadTask.value - #expect(viewModel.messageBus.currentMessage?.message == "[NATI-2001] \(message) [Status: \(httpResponseCode)]") + #expect(viewModel.messageBus.currentMessage?.message == "[NATIVEAPPTEMPLATE-2001] \(message) [Status: \(httpResponseCode)]") #expect(viewModel.shouldDismiss) } @@ -335,7 +335,7 @@ struct ShopBasicSettingsViewModelTest { } await updateShopTask.value - #expect(viewModel.messageBus.currentMessage?.message == "[NATI-2001] \(message) [Status: \(httpResponseCode)]") + #expect(viewModel.messageBus.currentMessage?.message == "[NATIVEAPPTEMPLATE-2001] \(message) [Status: \(httpResponseCode)]") #expect(viewModel.isUpdating == false) #expect(viewModel.isBusy == false) #expect(viewModel.shouldDismiss) diff --git a/NativeAppTemplateTests/UI/Shop Settings/ShopSettingsViewModelTest.swift b/NativeAppTemplateTests/UI/Shop Settings/ShopSettingsViewModelTest.swift index 3560648..0a3901a 100644 --- a/NativeAppTemplateTests/UI/Shop Settings/ShopSettingsViewModelTest.swift +++ b/NativeAppTemplateTests/UI/Shop Settings/ShopSettingsViewModelTest.swift @@ -96,7 +96,7 @@ struct ShopSettingsViewModelTest { } await reloadTask.value - #expect(viewModel.messageBus.currentMessage?.message == "[NATI-2001] \(message) [Status: \(httpResponseCode)]") + #expect(viewModel.messageBus.currentMessage?.message == "[NATIVEAPPTEMPLATE-2001] \(message) [Status: \(httpResponseCode)]") #expect(viewModel.shouldDismiss) #expect(viewModel.isFetching == false) #expect(viewModel.isBusy == false) @@ -168,7 +168,7 @@ struct ShopSettingsViewModelTest { await destroyShopTask.value #expect(viewModel.messageBus.currentMessage?.message == - "\(Strings.shopDeletedError) [NATI-2001] \(message) [Status: \(httpResponseCode)]") + "\(Strings.shopDeletedError) [NATIVEAPPTEMPLATE-2001] \(message) [Status: \(httpResponseCode)]") #expect(viewModel.isDeleting) #expect(viewModel.isBusy) #expect(sessionController.userState == .notLoggedIn) diff --git a/NativeAppTemplateTests/Utilities/AppErrorTest.swift b/NativeAppTemplateTests/Utilities/AppErrorTest.swift index 64403a6..d1198d8 100644 --- a/NativeAppTemplateTests/Utilities/AppErrorTest.swift +++ b/NativeAppTemplateTests/Utilities/AppErrorTest.swift @@ -11,7 +11,7 @@ struct AppErrorTest { @Test func unexpectedErrorCode() { let error = AppError.unexpected(description: "Something broke") - #expect(error.errorCode == "NATI-1001") + #expect(error.errorCode == "NATIVEAPPTEMPLATE-1001") } @Test @@ -23,7 +23,7 @@ struct AppErrorTest { @Test func unexpectedFormattedDescription() { let error = AppError.unexpected(description: "Something broke") - #expect(error.formattedDescription == "[NATI-1001] An unexpected error occurred. Something broke") + #expect(error.formattedDescription == "[NATIVEAPPTEMPLATE-1001] An unexpected error occurred. Something broke") } @Test @@ -34,7 +34,7 @@ struct AppErrorTest { line: 42, function: "testFunc()" ) - #expect(error.debugDescription.contains("NATI-1001")) + #expect(error.debugDescription.contains("NATIVEAPPTEMPLATE-1001")) #expect(error.debugDescription.contains("Something broke")) #expect(error.debugDescription.contains("TestFile.swift")) #expect(error.debugDescription.contains("42")) @@ -44,6 +44,6 @@ struct AppErrorTest { @Test func codedDescriptionViaErrorExtension() { let error: Error = AppError.unexpected(description: "Test") - #expect(error.codedDescription == "[NATI-1001] An unexpected error occurred. Test") + #expect(error.codedDescription == "[NATIVEAPPTEMPLATE-1001] An unexpected error occurred. Test") } } diff --git a/NativeAppTemplateTests/Utilities/CodedErrorTest.swift b/NativeAppTemplateTests/Utilities/CodedErrorTest.swift index bce9ad5..c68198c 100644 --- a/NativeAppTemplateTests/Utilities/CodedErrorTest.swift +++ b/NativeAppTemplateTests/Utilities/CodedErrorTest.swift @@ -10,20 +10,20 @@ import Testing @Suite struct CodedErrorTest { struct TestCodedError: CodedError { - var errorCode: String { "NATI-9999" } + var errorCode: String { "NATIVEAPPTEMPLATE-9999" } var errorDescription: String? { "Test error description" } } @Test func formattedDescription() { let error = TestCodedError() - #expect(error.formattedDescription == "[NATI-9999] Test error description") + #expect(error.formattedDescription == "[NATIVEAPPTEMPLATE-9999] Test error description") } @Test func codedDescriptionWithCodedError() { let error: Error = TestCodedError() - #expect(error.codedDescription == "[NATI-9999] Test error description") + #expect(error.codedDescription == "[NATIVEAPPTEMPLATE-9999] Test error description") } @Test @@ -37,13 +37,13 @@ struct CodedErrorTest { } struct NilDescriptionError: CodedError { - var errorCode: String { "NATI-0000" } + var errorCode: String { "NATIVEAPPTEMPLATE-0000" } var errorDescription: String? { nil } } @Test func formattedDescriptionWithNilErrorDescription() { let error = NilDescriptionError() - #expect(error.formattedDescription == "[NATI-0000] Unknown error") + #expect(error.formattedDescription == "[NATIVEAPPTEMPLATE-0000] Unknown error") } } diff --git a/NativeAppTemplateTests/Utilities/MessageBusTest.swift b/NativeAppTemplateTests/Utilities/MessageBusTest.swift index df158b8..0839d85 100644 --- a/NativeAppTemplateTests/Utilities/MessageBusTest.swift +++ b/NativeAppTemplateTests/Utilities/MessageBusTest.swift @@ -79,7 +79,7 @@ struct MessageBusTest { let message = Message(error: error) #expect(message.level == .error) - #expect(message.message == "[NATI-2005] NativeAppTemplateAPIError::NoData") + #expect(message.message == "[NATIVEAPPTEMPLATE-2005] NativeAppTemplateAPIError::NoData") #expect(message.autoDismiss == false) } diff --git a/NativeAppTemplateTests/Utilities/NativeAppTemplateAPIErrorTest.swift b/NativeAppTemplateTests/Utilities/NativeAppTemplateAPIErrorTest.swift index dce12bf..c156e3a 100644 --- a/NativeAppTemplateTests/Utilities/NativeAppTemplateAPIErrorTest.swift +++ b/NativeAppTemplateTests/Utilities/NativeAppTemplateAPIErrorTest.swift @@ -11,38 +11,38 @@ struct NativeAppTemplateAPIErrorTest { @Test func requestFailedErrorCode() { let error = NativeAppTemplateAPIError.requestFailed(nil, 500, "Server error") - #expect(error.errorCode == "NATI-2001") + #expect(error.errorCode == "NATIVEAPPTEMPLATE-2001") } @Test func processingErrorErrorCode() { let error = NativeAppTemplateAPIError.processingError(nil) - #expect(error.errorCode == "NATI-2002") + #expect(error.errorCode == "NATIVEAPPTEMPLATE-2002") } @Test func responseMissingRequiredMetaErrorCode() { let error = NativeAppTemplateAPIError.responseMissingRequiredMeta(field: "total") - #expect(error.errorCode == "NATI-2003") + #expect(error.errorCode == "NATIVEAPPTEMPLATE-2003") } @Test func responseHasIncorrectNumberOfElementsErrorCode() { let error = NativeAppTemplateAPIError.responseHasIncorrectNumberOfElements - #expect(error.errorCode == "NATI-2004") + #expect(error.errorCode == "NATIVEAPPTEMPLATE-2004") } @Test func noDataErrorCode() { let error = NativeAppTemplateAPIError.noData - #expect(error.errorCode == "NATI-2005") + #expect(error.errorCode == "NATIVEAPPTEMPLATE-2005") } @Test func requestFailedWithMessage() { let error = NativeAppTemplateAPIError.requestFailed(nil, 422, "Validation failed") #expect(error.errorDescription == "Validation failed [Status: 422]") - #expect(error.formattedDescription == "[NATI-2001] Validation failed [Status: 422]") + #expect(error.formattedDescription == "[NATIVEAPPTEMPLATE-2001] Validation failed [Status: 422]") } @Test @@ -84,12 +84,12 @@ struct NativeAppTemplateAPIErrorTest { func noDataDescription() { let error = NativeAppTemplateAPIError.noData #expect(error.errorDescription == "NativeAppTemplateAPIError::NoData") - #expect(error.formattedDescription == "[NATI-2005] NativeAppTemplateAPIError::NoData") + #expect(error.formattedDescription == "[NATIVEAPPTEMPLATE-2005] NativeAppTemplateAPIError::NoData") } @Test func codedDescriptionViaErrorExtension() { let error: Error = NativeAppTemplateAPIError.requestFailed(nil, 404, "Not found") - #expect(error.codedDescription == "[NATI-2001] Not found [Status: 404]") + #expect(error.codedDescription == "[NATIVEAPPTEMPLATE-2001] Not found [Status: 404]") } } diff --git a/README.md b/README.md index 62411af..188b2f3 100644 --- a/README.md +++ b/README.md @@ -142,12 +142,12 @@ To run this app successfully, ensure you have: To connect to a local API server, set these env vars on the Xcode scheme (Edit Scheme → Run → Arguments → Environment Variables): ``` -NATEMPLATE_API_SCHEME = http -NATEMPLATE_API_DOMAIN = -NATEMPLATE_API_PORT = 3000 +NATIVEAPPTEMPLATE_API_SCHEME = http +NATIVEAPPTEMPLATE_API_DOMAIN = +NATIVEAPPTEMPLATE_API_PORT = 3000 ``` -> **Note:** Never use `127.0.0.1`, `localhost`, or `0.0.0.0` for `NATEMPLATE_API_DOMAIN` — those resolve to the iOS Simulator/device itself, not your Mac. Use your Mac's LAN IP (e.g., `192.168.1.6`) so the simulator or a physical device can reach the API server. +> **Note:** Never use `127.0.0.1`, `localhost`, or `0.0.0.0` for `NATIVEAPPTEMPLATE_API_DOMAIN` — those resolve to the iOS Simulator/device itself, not your Mac. Use your Mac's LAN IP (e.g., `192.168.1.6`) so the simulator or a physical device can reach the API server. Keep the scheme in `xcuserdata` (per-developer, gitignored), not `xcshareddata`. In Xcode, open **Product → Scheme → Manage Schemes…**, find `NativeAppTemplate`, and **uncheck "Shared"**. This moves the scheme (with your local env vars) to `xcuserdata/.xcuserdatad/xcschemes/` so your API settings are not committed. If Xcode staged a deletion of the previously shared scheme, restore it with: