@zigrivers/scaffold 3.6.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +127 -12
  2. package/content/knowledge/backend/backend-api-design.md +103 -0
  3. package/content/knowledge/backend/backend-architecture.md +100 -0
  4. package/content/knowledge/backend/backend-async-patterns.md +101 -0
  5. package/content/knowledge/backend/backend-auth-patterns.md +100 -0
  6. package/content/knowledge/backend/backend-conventions.md +105 -0
  7. package/content/knowledge/backend/backend-data-modeling.md +102 -0
  8. package/content/knowledge/backend/backend-deployment.md +100 -0
  9. package/content/knowledge/backend/backend-dev-environment.md +102 -0
  10. package/content/knowledge/backend/backend-observability.md +102 -0
  11. package/content/knowledge/backend/backend-project-structure.md +100 -0
  12. package/content/knowledge/backend/backend-requirements.md +103 -0
  13. package/content/knowledge/backend/backend-security.md +104 -0
  14. package/content/knowledge/backend/backend-testing.md +101 -0
  15. package/content/knowledge/backend/backend-worker-patterns.md +100 -0
  16. package/content/knowledge/cli/cli-architecture.md +101 -0
  17. package/content/knowledge/cli/cli-conventions.md +117 -0
  18. package/content/knowledge/cli/cli-dev-environment.md +121 -0
  19. package/content/knowledge/cli/cli-distribution-patterns.md +106 -0
  20. package/content/knowledge/cli/cli-interactivity-patterns.md +116 -0
  21. package/content/knowledge/cli/cli-output-patterns.md +107 -0
  22. package/content/knowledge/cli/cli-project-structure.md +124 -0
  23. package/content/knowledge/cli/cli-requirements.md +101 -0
  24. package/content/knowledge/cli/cli-shell-integration.md +130 -0
  25. package/content/knowledge/cli/cli-testing.md +134 -0
  26. package/content/knowledge/library/library-api-design.md +306 -0
  27. package/content/knowledge/library/library-architecture.md +247 -0
  28. package/content/knowledge/library/library-bundling.md +244 -0
  29. package/content/knowledge/library/library-conventions.md +229 -0
  30. package/content/knowledge/library/library-dev-environment.md +220 -0
  31. package/content/knowledge/library/library-documentation.md +300 -0
  32. package/content/knowledge/library/library-project-structure.md +237 -0
  33. package/content/knowledge/library/library-requirements.md +173 -0
  34. package/content/knowledge/library/library-security.md +257 -0
  35. package/content/knowledge/library/library-testing.md +319 -0
  36. package/content/knowledge/library/library-type-definitions.md +284 -0
  37. package/content/knowledge/library/library-versioning.md +300 -0
  38. package/content/knowledge/mobile-app/mobile-app-architecture.md +283 -0
  39. package/content/knowledge/mobile-app/mobile-app-conventions.md +180 -0
  40. package/content/knowledge/mobile-app/mobile-app-deployment.md +298 -0
  41. package/content/knowledge/mobile-app/mobile-app-dev-environment.md +257 -0
  42. package/content/knowledge/mobile-app/mobile-app-distribution.md +264 -0
  43. package/content/knowledge/mobile-app/mobile-app-observability.md +317 -0
  44. package/content/knowledge/mobile-app/mobile-app-offline-patterns.md +311 -0
  45. package/content/knowledge/mobile-app/mobile-app-project-structure.md +245 -0
  46. package/content/knowledge/mobile-app/mobile-app-push-notifications.md +321 -0
  47. package/content/knowledge/mobile-app/mobile-app-requirements.md +147 -0
  48. package/content/knowledge/mobile-app/mobile-app-security.md +338 -0
  49. package/content/knowledge/mobile-app/mobile-app-testing.md +400 -0
  50. package/content/knowledge/web-app/web-app-api-patterns.md +224 -0
  51. package/content/knowledge/web-app/web-app-architecture.md +116 -0
  52. package/content/knowledge/web-app/web-app-auth-patterns.md +256 -0
  53. package/content/knowledge/web-app/web-app-conventions.md +121 -0
  54. package/content/knowledge/web-app/web-app-data-patterns.md +218 -0
  55. package/content/knowledge/web-app/web-app-deployment-workflow.md +143 -0
  56. package/content/knowledge/web-app/web-app-deployment.md +134 -0
  57. package/content/knowledge/web-app/web-app-design-system.md +158 -0
  58. package/content/knowledge/web-app/web-app-dev-environment.md +173 -0
  59. package/content/knowledge/web-app/web-app-observability.md +221 -0
  60. package/content/knowledge/web-app/web-app-project-structure.md +160 -0
  61. package/content/knowledge/web-app/web-app-rendering-strategies.md +133 -0
  62. package/content/knowledge/web-app/web-app-requirements.md +112 -0
  63. package/content/knowledge/web-app/web-app-security.md +193 -0
  64. package/content/knowledge/web-app/web-app-session-patterns.md +214 -0
  65. package/content/knowledge/web-app/web-app-testing.md +249 -0
  66. package/content/knowledge/web-app/web-app-ux-patterns.md +162 -0
  67. package/content/methodology/backend-overlay.yml +73 -0
  68. package/content/methodology/cli-overlay.yml +69 -0
  69. package/content/methodology/library-overlay.yml +67 -0
  70. package/content/methodology/mobile-app-overlay.yml +71 -0
  71. package/content/methodology/web-app-overlay.yml +79 -0
  72. package/dist/cli/commands/init.d.ts +21 -0
  73. package/dist/cli/commands/init.d.ts.map +1 -1
  74. package/dist/cli/commands/init.js +261 -13
  75. package/dist/cli/commands/init.js.map +1 -1
  76. package/dist/cli/commands/init.test.js +206 -0
  77. package/dist/cli/commands/init.test.js.map +1 -1
  78. package/dist/config/schema.d.ts +1392 -64
  79. package/dist/config/schema.d.ts.map +1 -1
  80. package/dist/config/schema.js +82 -5
  81. package/dist/config/schema.js.map +1 -1
  82. package/dist/config/schema.test.js +302 -1
  83. package/dist/config/schema.test.js.map +1 -1
  84. package/dist/core/assembly/overlay-loader.d.ts.map +1 -1
  85. package/dist/core/assembly/overlay-loader.js +2 -1
  86. package/dist/core/assembly/overlay-loader.js.map +1 -1
  87. package/dist/core/assembly/overlay-loader.test.js +56 -0
  88. package/dist/core/assembly/overlay-loader.test.js.map +1 -1
  89. package/dist/e2e/game-pipeline.test.js +1 -0
  90. package/dist/e2e/game-pipeline.test.js.map +1 -1
  91. package/dist/e2e/project-type-overlays.test.d.ts +16 -0
  92. package/dist/e2e/project-type-overlays.test.d.ts.map +1 -0
  93. package/dist/e2e/project-type-overlays.test.js +834 -0
  94. package/dist/e2e/project-type-overlays.test.js.map +1 -0
  95. package/dist/types/config.d.ts +19 -2
  96. package/dist/types/config.d.ts.map +1 -1
  97. package/dist/types/index.d.ts +0 -1
  98. package/dist/types/index.d.ts.map +1 -1
  99. package/dist/types/index.js +0 -1
  100. package/dist/types/index.js.map +1 -1
  101. package/dist/wizard/questions.d.ts +27 -1
  102. package/dist/wizard/questions.d.ts.map +1 -1
  103. package/dist/wizard/questions.js +142 -3
  104. package/dist/wizard/questions.js.map +1 -1
  105. package/dist/wizard/questions.test.js +206 -8
  106. package/dist/wizard/questions.test.js.map +1 -1
  107. package/dist/wizard/wizard.d.ts +21 -0
  108. package/dist/wizard/wizard.d.ts.map +1 -1
  109. package/dist/wizard/wizard.js +27 -1
  110. package/dist/wizard/wizard.js.map +1 -1
  111. package/package.json +1 -1
  112. package/dist/types/wizard.d.ts +0 -14
  113. package/dist/types/wizard.d.ts.map +0 -1
  114. package/dist/types/wizard.js +0 -2
  115. package/dist/types/wizard.js.map +0 -1
@@ -0,0 +1,180 @@
1
+ ---
2
+ name: mobile-app-conventions
3
+ description: Platform naming conventions, accessibility patterns, navigation patterns, and code style for iOS and Android mobile apps
4
+ topics: [mobile-app, conventions, swift, kotlin, naming, accessibility, navigation]
5
+ ---
6
+
7
+ Mobile platform conventions exist for consistency across the ecosystem, toolchain compatibility, and team readability. iOS and Android have distinct naming conventions, navigation paradigms, and accessibility implementation patterns. Mixing conventions or importing web/backend naming styles into mobile code creates cognitive friction and breaks tooling assumptions (SwiftUI previews, Android Studio refactoring, lint rules).
8
+
9
+ ## Summary
10
+
11
+ iOS conventions use PascalCase for types and camelCase for everything else; Swift files are one type per file named identically to the type. Android uses PascalCase for classes and camelCase for members, with XML resource names in snake_case. Navigation uses UINavigationController/Coordinator (iOS) or Navigation Component with NavGraph (Android). Accessibility labels follow platform semantics: VoiceOver on iOS, TalkBack on Android. Follow these patterns consistently — tooling and platform reviewers expect them.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### iOS Naming Conventions (Swift)
16
+
17
+ **Types (classes, structs, enums, protocols)**
18
+ - PascalCase: `UserProfileViewController`, `AuthenticationService`, `PaymentStatus`
19
+ - Protocols: prefer noun or adjective+able naming. `Codable`, `Equatable`, `DataSource`, `Delegate`
20
+ - Protocol conformances add a semantic suffix: `UITableViewDataSource`, `URLSessionDelegate`
21
+ - Generic type parameters: single uppercase letter for simple cases (`T`, `U`), descriptive names for constrained types (`Element`, `Key`, `Value`)
22
+
23
+ **Properties, methods, and variables**
24
+ - camelCase: `firstName`, `isAuthenticated`, `fetchUserProfile()`
25
+ - Boolean properties: use `is`, `has`, `should`, `can` prefix: `isLoading`, `hasUnreadMessages`, `canSubmit`
26
+ - Avoid abbreviations except widely established ones: `url`, `id`, `api` are acceptable; `usrNm` is not
27
+
28
+ **File naming**
29
+ - One primary type per file, filename matches type exactly: `UserProfileViewController.swift`, `AuthenticationService.swift`
30
+ - Extensions can live in separate files: `User+Codable.swift`, `View+Accessibility.swift`
31
+ - Protocol conformances in extension files when they're substantial: `UserProfileViewController+TableViewDelegate.swift`
32
+
33
+ **View naming (UIKit)**
34
+ - ViewControllers always end in `ViewController`: `LoginViewController`, `SettingsViewController`
35
+ - Views end in `View`: `ProfileHeaderView`, `EmptyStateView`
36
+ - Cells end in `Cell`: `ProductListCell`, `MessageBubbleCell`
37
+ - Reuse identifiers match class names: `tableView.register(ProductListCell.self, forCellReuseIdentifier: "ProductListCell")`
38
+
39
+ **SwiftUI naming**
40
+ - Views are structs with descriptive names: `UserProfileView`, `LoginForm`, `SettingsRow`
41
+ - ViewModels are `@Observable` classes or ObservableObjects ending in `ViewModel`: `UserProfileViewModel`
42
+ - Preview providers: `#Preview { UserProfileView() }`
43
+
44
+ **Constants**
45
+ - Use `enum` as a namespace (no-case enums cannot be instantiated): `enum APIConstants { static let baseURL = "..." }`
46
+ - `static let` on structs/classes for type-level constants
47
+ - Avoid global `let` at file scope except for truly global constants
48
+
49
+ ### Android Naming Conventions (Kotlin)
50
+
51
+ **Classes and interfaces**
52
+ - PascalCase: `UserProfileFragment`, `AuthRepository`, `PaymentUseCase`
53
+ - Activities: end in `Activity` — `MainActivity`, `LoginActivity`
54
+ - Fragments: end in `Fragment` — `UserProfileFragment`, `SettingsFragment`
55
+ - ViewModels: end in `ViewModel` — `UserProfileViewModel`, `HomeViewModel`
56
+ - Repositories: end in `Repository` — `UserRepository`, `ProductRepository`
57
+ - Use cases: verb phrase — `GetUserProfileUseCase`, `UpdateSettingsUseCase`
58
+
59
+ **Properties and functions**
60
+ - camelCase: `firstName`, `isAuthenticated`, `fetchUserProfile()`
61
+ - Boolean properties: `is`, `has`, `should` prefix — `isLoading`, `hasError`, `shouldShowEmpty`
62
+ - Extension functions follow the same rules: fun `String.isValidEmail()`, fun `View.setVisible(Boolean)`
63
+
64
+ **XML resource naming (snake_case always)**
65
+ - Layouts: `{type}_{name}.xml` — `activity_main.xml`, `fragment_profile.xml`, `item_product.xml`, `view_empty_state.xml`
66
+ - IDs: `{type}_{name}` — `@id/button_submit`, `@id/text_username`, `@id/recycler_products`
67
+ - Drawables: `ic_{name}.xml` for icons, `bg_{name}.xml` for backgrounds, `shape_{name}.xml` for shapes
68
+ - Colors: descriptive names in `colors.xml` (`color_primary`, `color_surface`), semantic names in theme (`colorPrimary`, `colorSurface`)
69
+ - Strings: `{screen}_{element}_{type}` — `login_email_hint`, `profile_name_label`, `error_network_message`
70
+ - Dimensions: `{component}_{property}` — `button_corner_radius`, `card_elevation`, `spacing_large`
71
+
72
+ **Compose naming**
73
+ - Composables: PascalCase like types: `UserProfileScreen`, `ProductCard`, `EmptyState`
74
+ - State holders ending in `State`: `LoginUiState`, `ProfileUiState`
75
+ - Preview functions: `@Preview @Composable fun UserProfileScreenPreview()`
76
+
77
+ ### Navigation Patterns — iOS
78
+
79
+ **UIKit navigation patterns**
80
+
81
+ *UINavigationController (hierarchical)*
82
+ - Push/pop model for drill-down navigation: settings list → individual setting
83
+ - `navigationController?.pushViewController(vc, animated: true)` / `.popViewController(animated: true)`
84
+ - Always use `navigationItem.title` and `navigationItem.backButtonTitle` for VoiceOver accessibility
85
+
86
+ *UITabBarController (top-level)*
87
+ - Manage 2–5 coordinate sibling views; do not nest tab bars
88
+ - Each tab owns its own `UINavigationController` — this is the standard pattern
89
+ - Tab bar items need `title` and `image` (SF Symbol): `UITabBarItem(title: "Home", image: UIImage(systemName: "house"), tag: 0)`
90
+
91
+ *Modal presentations*
92
+ - `.present(_:animated:completion:)` for modals
93
+ - Always provide a dismissal mechanism (button or swipe-down) — never trap the user in a modal
94
+ - `UISheetPresentationController` for bottom sheets (iOS 15+): `detents: [.medium(), .large()]`
95
+
96
+ *Coordinator pattern*
97
+ - Extract navigation logic from ViewControllers into Coordinator objects
98
+ - Each Coordinator owns a `UINavigationController` and creates/presents child ViewControllers
99
+ - Child Coordinators are stored in a `childCoordinators` array — release them in the finish callback to prevent leaks
100
+ - Delegate back to parent Coordinator for cross-boundary navigation
101
+
102
+ **SwiftUI navigation**
103
+ - `NavigationStack` (iOS 16+) for hierarchical navigation: `NavigationStack(path: $path) { ... }`
104
+ - `NavigationPath` for type-erased programmatic navigation
105
+ - `.navigationDestination(for:)` to register destination views for path types
106
+ - `TabView` with `tabItem` modifier for tab navigation
107
+ - `.sheet`, `.fullScreenCover`, `.popover` for modal presentation
108
+ - Pass navigation state via bindings or a `NavigationRouter` observable object — do not use environment objects for navigation state in deep hierarchies
109
+
110
+ ### Navigation Patterns — Android
111
+
112
+ **Navigation Component (Jetpack)**
113
+ - Define navigation graph in `nav_graph.xml` or `NavHost` composable
114
+ - Fragments connect via Actions defined in the nav graph
115
+ - Pass arguments with Safe Args plugin — type-safe navigation prevents bundle key typos
116
+ - Deep links registered in nav graph enable external app navigation
117
+
118
+ **Compose Navigation**
119
+ - `NavHost` with `composable` routes: `NavHost(navController, startDestination = "home") { composable("home") { HomeScreen(navController) } }`
120
+ - Route strings are stringly-typed — define as constants in a `Screen` sealed class or object
121
+ - Pass data via route arguments (`/{userId}`) or ViewModel shared across the back stack
122
+ - `rememberNavController()` at the top of the composition; pass `NavController` down as a lambda (`onNavigate: () -> Unit`) rather than passing the controller itself
123
+
124
+ **Back stack management**
125
+ - `popBackStack()` for simple back navigation
126
+ - `navigate("destination") { popUpTo("home") { inclusive = false } }` to clear the back stack when navigating to a top-level destination
127
+ - `launchSingleTop = true` to prevent duplicate destinations in the back stack
128
+
129
+ ### Accessibility Conventions
130
+
131
+ **iOS VoiceOver implementation**
132
+
133
+ Labels vs. hints:
134
+ - `accessibilityLabel`: what the element is — "Profile photo for Jane Smith"
135
+ - `accessibilityHint`: what happens when activated — "Double-tap to view full profile"
136
+ - Never include the element type in the label (VoiceOver announces it separately): say "Submit" not "Submit button"
137
+
138
+ Grouping:
139
+ - Use `accessibilityElements` to define a custom element order within a container
140
+ - `UIAccessibilityElement` with `isAccessibilityElement = true` for custom drawing views
141
+ - `accessibilityActivate()` override for complex controls with custom activation behavior
142
+
143
+ SwiftUI accessibility:
144
+ - `.accessibilityLabel("Submit order")` overrides the computed label
145
+ - `.accessibilityHint("Processes payment and places order")` adds the activation hint
146
+ - `.accessibilityAddTraits(.isButton)` when a custom view should be announced as a button
147
+ - `.accessibilityRemoveTraits(.isImage)` when an image is decorative
148
+ - `.accessibilityElement(children: .combine)` to merge a container's children into one accessible element
149
+
150
+ **Android TalkBack implementation**
151
+
152
+ View system:
153
+ - `android:contentDescription="@string/submit_button_label"` on all non-text interactive views
154
+ - `android:importantForAccessibility="no"` for decorative views (icons that duplicate adjacent text)
155
+ - `android:labelFor="@id/edit_text_email"` on Label TextViews — associates the label with the input for TalkBack
156
+ - `ViewCompat.setAccessibilityDelegate()` for advanced semantic customization
157
+
158
+ Compose semantics:
159
+ - `Modifier.semantics { contentDescription = "Submit order" }` for custom content descriptions
160
+ - `Modifier.semantics { role = Role.Button }` for role announcement
161
+ - `Modifier.clearAndSetSemantics { ... }` to replace inherited semantics entirely
162
+ - `Modifier.semantics(mergeDescendants = true) { ... }` to merge a group of elements
163
+
164
+ ### Code Style Conventions
165
+
166
+ **Swift**
167
+ - `guard` for early returns over nested `if` statements
168
+ - `let` by default; `var` only when mutation is necessary
169
+ - Trailing closures when the last parameter is a closure: `array.filter { $0.isActive }`
170
+ - `@discardableResult` on functions when the return value may intentionally be ignored
171
+ - Mark protocol methods with default implementations in extensions
172
+ - Avoid `!` force-unwrap; use `guard let` or `if let` with meaningful error handling
173
+
174
+ **Kotlin**
175
+ - `val` by default; `var` only when mutation is required
176
+ - Data classes for value types that need `equals`/`hashCode`/`copy`: `data class User(val id: String, val name: String)`
177
+ - Sealed classes for exhaustive state modeling: `sealed class UiState { data class Success(...); data object Loading; data class Error(...) }`
178
+ - Extension functions over utility classes: `fun String.isValidEmail()` not `EmailUtils.isValidEmail(string)`
179
+ - Coroutines over threads/callbacks; `suspend` functions for async operations
180
+ - `Flow` for reactive streams; `StateFlow` for UI state; `SharedFlow` for events
@@ -0,0 +1,298 @@
1
+ ---
2
+ name: mobile-app-deployment
3
+ description: App store submission, code signing, provisioning profiles, CI/CD with Fastlane, and release management for iOS and Android
4
+ topics: [mobile-app, deployment, app-store, google-play, code-signing, fastlane, ci-cd, release-management]
5
+ ---
6
+
7
+ Mobile app deployment is significantly more complex than web deployment: code signing creates a cryptographic chain of trust, app store review is a human process with 24–48 hour latency, and binary deployment means bugs cannot be hot-patched without a full submission cycle. Automate as much of this as possible with Fastlane — manual signing and upload processes are error-prone and do not scale to frequent releases.
8
+
9
+ ## Summary
10
+
11
+ iOS deployment requires Apple Developer account, code signing (certificates + provisioning profiles), App Store Connect submission, and 24–48 hour review. Android deployment requires a Google Play Developer account, APK/AAB signing with a release keystore, and Play Store submission. Both platforms support CI/CD automation via Fastlane lanes. Code signing is the most failure-prone step — use Fastlane Match (iOS) or a secrets-managed keystore (Android) to make it reproducible. Automate the full pipeline from test → build → sign → upload.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### iOS Code Signing
16
+
17
+ **Concepts**
18
+ - **Certificate**: A key pair issued by Apple. Two types relevant to development: Apple Distribution (for App Store) and Apple Development (for device testing).
19
+ - **App ID**: A unique identifier (`com.example.myapp`) registered in the Apple Developer portal.
20
+ - **Provisioning Profile**: A file that binds an App ID to a certificate and, for development, to specific device UDIDs. Must be re-downloaded when devices are added.
21
+ - **Entitlements**: Capabilities your app uses (push notifications, in-app purchases, Sign in with Apple) — must match between app target and provisioning profile.
22
+
23
+ **Automatic vs. manual signing**
24
+ - Automatic (Xcode manages signing): fine for individual developers, unreliable in CI — Xcode modifies the project file.
25
+ - Manual signing: specify `CODE_SIGN_IDENTITY`, `PROVISIONING_PROFILE_SPECIFIER` explicitly in xcconfig or build settings. Required for reliable CI.
26
+
27
+ **Fastlane Match for team signing**
28
+ Match stores certificates and profiles in a git repository (or S3/Google Cloud), encrypted with a passphrase. Every team member and CI runner fetches from the same source:
29
+
30
+ ```ruby
31
+ # Matchfile
32
+ git_url("https://github.com/example/certificates")
33
+ storage_mode("git")
34
+ type("appstore") # or "development", "adhoc"
35
+ app_identifier("com.example.myapp")
36
+
37
+ # Fastfile
38
+ lane :sync_signing do
39
+ match(type: "appstore", readonly: is_ci)
40
+ end
41
+
42
+ lane :build_release do
43
+ sync_signing
44
+ gym(
45
+ scheme: "MyApp",
46
+ configuration: "Release",
47
+ export_method: "app-store",
48
+ output_directory: "./build"
49
+ )
50
+ end
51
+ ```
52
+
53
+ **Match setup workflow**
54
+ ```bash
55
+ # First time: create certificates repo and generate certs
56
+ fastlane match init
57
+ fastlane match appstore # generates Distribution cert + App Store profile
58
+ fastlane match development # generates Development cert + profile
59
+
60
+ # Subsequent: sync to CI or new dev machine
61
+ MATCH_PASSWORD=<passphrase> fastlane match appstore --readonly
62
+ ```
63
+
64
+ **CI signing setup**
65
+ - Store `MATCH_PASSWORD` as a CI secret variable — never commit it
66
+ - Use `readonly: true` in CI (`is_ci` returns true in most CI environments) — CI should never regenerate certificates
67
+ - For GitHub Actions: store the certificates git repo URL and match password as repository secrets
68
+
69
+ ### iOS App Store Submission
70
+
71
+ **App Store Connect setup**
72
+ 1. Create the app record in App Store Connect (appstoreconnect.apple.com)
73
+ 2. Configure capabilities in the Apple Developer portal (push notifications, Sign in with Apple, etc.)
74
+ 3. Create App Store listing: screenshots (required for each device size), description, keywords, privacy policy URL
75
+ 4. Configure pricing and availability
76
+
77
+ **Required screenshots (2024)**
78
+ - iPhone: 6.9" display (iPhone 16 Pro Max), 6.5" display (iPhone 14 Plus)
79
+ - iPad (if universal): 12.9" iPad Pro, 11" iPad Pro
80
+ - Screenshots generated programmatically with `fastlane snapshot` + `fastlane frameit`
81
+
82
+ **Fastlane deliver for metadata + upload**
83
+ ```ruby
84
+ lane :release do
85
+ # Build
86
+ gym(scheme: "MyApp", configuration: "Release")
87
+
88
+ # Upload to App Store Connect
89
+ deliver(
90
+ submit_for_review: false, # set true to auto-submit
91
+ automatic_release: false, # set true to auto-release after approval
92
+ force: true, # skip HTML report generation
93
+ metadata_path: "./fastlane/metadata",
94
+ screenshots_path: "./fastlane/screenshots"
95
+ )
96
+ end
97
+ ```
98
+
99
+ **App Store review guidelines (commonly rejected items)**
100
+ - Crashy builds: any crash during review results in immediate rejection
101
+ - Incomplete functionality: demo/placeholder screens visible to reviewers
102
+ - Login-gated apps: must provide reviewer credentials in App Store Connect
103
+ - Guideline 4.2.2: apps must be more than a repackaged website
104
+ - Push notification permission: must explain usage before prompting
105
+ - Privacy labels must accurately describe all data collected
106
+
107
+ **TestFlight for pre-release distribution**
108
+ ```ruby
109
+ lane :beta do
110
+ gym(scheme: "MyApp", configuration: "Release")
111
+ pilot(
112
+ app_identifier: "com.example.myapp",
113
+ changelog: "Bug fixes and improvements",
114
+ distribute_external: false, # true for external testers
115
+ notify_external_testers: false
116
+ )
117
+ end
118
+ ```
119
+
120
+ ### Android Code Signing
121
+
122
+ **Release keystore**
123
+ ```bash
124
+ # Generate a release keystore (do this once; store securely)
125
+ keytool -genkey -v \
126
+ -keystore release.keystore \
127
+ -alias myapp \
128
+ -keyalg RSA \
129
+ -keysize 2048 \
130
+ -validity 10000
131
+
132
+ # Never commit release.keystore to git
133
+ ```
134
+
135
+ **Signing configuration in Gradle**
136
+ ```kotlin
137
+ // app/build.gradle.kts
138
+ android {
139
+ signingConfigs {
140
+ create("release") {
141
+ storeFile = file(System.getenv("KEYSTORE_PATH") ?: "release.keystore")
142
+ storePassword = System.getenv("KEYSTORE_PASSWORD")
143
+ keyAlias = System.getenv("KEY_ALIAS")
144
+ keyPassword = System.getenv("KEY_PASSWORD")
145
+ }
146
+ }
147
+ buildTypes {
148
+ release {
149
+ signingConfig = signingConfigs.getByName("release")
150
+ isMinifyEnabled = true
151
+ proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ **CI keystore management**
158
+ - Store keystore as a base64-encoded CI secret: `base64 release.keystore | pbcopy`
159
+ - In CI: decode and write to disk: `echo $KEYSTORE_BASE64 | base64 -d > release.keystore`
160
+ - Store `KEYSTORE_PASSWORD`, `KEY_ALIAS`, `KEY_PASSWORD` as separate CI secrets
161
+ - Never print these values in CI logs — mask secrets in CI configuration
162
+
163
+ **Google Play App Signing**
164
+ Enable Google Play App Signing: Google manages the release signing key, you upload with an upload key. Benefits: Google can re-sign if your upload key is lost; protects against keystore loss (catastrophic for Android apps — if you lose the keystore, you cannot update the app).
165
+
166
+ ### Android Play Store Submission
167
+
168
+ **App Bundle (AAB) vs APK**
169
+ - Always submit AAB (`.aab`) to Play Store — it enables Play Feature Delivery, dynamic delivery, and smaller installs
170
+ - APK is for direct distribution only (enterprise, sideloading)
171
+ - Build AAB: `./gradlew bundleRelease`
172
+
173
+ **Fastlane supply for Android**
174
+ ```ruby
175
+ lane :deploy_production do
176
+ gradle(task: "bundle", build_type: "Release")
177
+ supply(
178
+ track: "production",
179
+ aab: "app/build/outputs/bundle/release/app-release.aab",
180
+ package_name: "com.example.myapp"
181
+ )
182
+ end
183
+
184
+ lane :deploy_internal do
185
+ gradle(task: "bundle", build_type: "Release")
186
+ supply(
187
+ track: "internal",
188
+ aab: "app/build/outputs/bundle/release/app-release.aab"
189
+ )
190
+ end
191
+ ```
192
+
193
+ **Play Console tracks**
194
+ - Internal testing: up to 100 testers, immediate availability
195
+ - Closed testing (Alpha): specific Google Groups, same-day availability
196
+ - Open testing (Beta): public opt-in, same-day availability
197
+ - Production: staged rollout available (1% → 5% → 20% → 100%)
198
+
199
+ **ProGuard / R8 rules**
200
+ ```proguard
201
+ # Keep data classes used by Gson/Moshi/Retrofit
202
+ -keep class com.example.myapp.data.model.** { *; }
203
+
204
+ # Keep Retrofit service interfaces
205
+ -keep interface com.example.myapp.data.network.** { *; }
206
+
207
+ # Keep Parcelable implementations
208
+ -keep class * implements android.os.Parcelable {
209
+ public static final android.os.Parcelable$Creator *;
210
+ }
211
+ ```
212
+
213
+ Always build and test the release APK/AAB locally before submitting — R8 obfuscation can break reflection-dependent code (Gson, Retrofit, Hilt) that works fine in debug builds.
214
+
215
+ ### CI/CD Pipeline
216
+
217
+ **GitHub Actions — iOS**
218
+ ```yaml
219
+ name: iOS Release
220
+ on:
221
+ push:
222
+ tags: ['v*']
223
+ jobs:
224
+ release:
225
+ runs-on: macos-14
226
+ steps:
227
+ - uses: actions/checkout@v4
228
+ - uses: ruby/setup-ruby@v1
229
+ with:
230
+ bundler-cache: true
231
+ - name: Install dependencies
232
+ run: bundle exec pod install
233
+ - name: Sync signing
234
+ env:
235
+ MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
236
+ MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
237
+ run: bundle exec fastlane sync_signing
238
+ - name: Build and upload
239
+ env:
240
+ APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.ASC_KEY_ID }}
241
+ APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
242
+ APP_STORE_CONNECT_API_KEY: ${{ secrets.ASC_PRIVATE_KEY }}
243
+ run: bundle exec fastlane release
244
+ ```
245
+
246
+ **GitHub Actions — Android**
247
+ ```yaml
248
+ name: Android Release
249
+ on:
250
+ push:
251
+ tags: ['v*']
252
+ jobs:
253
+ release:
254
+ runs-on: ubuntu-latest
255
+ steps:
256
+ - uses: actions/checkout@v4
257
+ - uses: actions/setup-java@v4
258
+ with:
259
+ java-version: '17'
260
+ distribution: 'temurin'
261
+ - name: Decode keystore
262
+ env:
263
+ KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
264
+ run: echo $KEYSTORE_BASE64 | base64 -d > release.keystore
265
+ - name: Build release
266
+ env:
267
+ KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
268
+ KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
269
+ KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
270
+ run: ./gradlew bundleRelease
271
+ - name: Upload to Play Store
272
+ uses: r0adkll/upload-google-play@v1
273
+ with:
274
+ serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
275
+ packageName: com.example.myapp
276
+ releaseFiles: app/build/outputs/bundle/release/*.aab
277
+ track: internal
278
+ ```
279
+
280
+ ### Version Management
281
+
282
+ **iOS versioning**
283
+ - `CFBundleShortVersionString` (Marketing version): user-visible version (`1.2.3`)
284
+ - `CFBundleVersion` (Build number): must increase monotonically for each App Store submission
285
+ - Automate build number increment: `fastlane run increment_build_number`
286
+ - Automate version: `fastlane run increment_version_number bump_type:minor`
287
+
288
+ **Android versioning**
289
+ ```kotlin
290
+ android {
291
+ defaultConfig {
292
+ versionCode = 42 // Must increase on every Play Store submission
293
+ versionName = "1.2.3" // User-visible version string
294
+ }
295
+ }
296
+ ```
297
+
298
+ Automate versionCode in CI: read from git tag, CI build number, or a `version.properties` file.