@zigrivers/scaffold 3.7.0 → 3.9.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.
- package/README.md +113 -8
- package/content/knowledge/browser-extension/browser-extension-architecture.md +195 -0
- package/content/knowledge/browser-extension/browser-extension-content-scripts.md +264 -0
- package/content/knowledge/browser-extension/browser-extension-conventions.md +156 -0
- package/content/knowledge/browser-extension/browser-extension-cross-browser.md +229 -0
- package/content/knowledge/browser-extension/browser-extension-dev-environment.md +247 -0
- package/content/knowledge/browser-extension/browser-extension-manifest.md +220 -0
- package/content/knowledge/browser-extension/browser-extension-project-structure.md +183 -0
- package/content/knowledge/browser-extension/browser-extension-requirements.md +107 -0
- package/content/knowledge/browser-extension/browser-extension-security.md +202 -0
- package/content/knowledge/browser-extension/browser-extension-service-workers.md +265 -0
- package/content/knowledge/browser-extension/browser-extension-store-submission.md +155 -0
- package/content/knowledge/browser-extension/browser-extension-testing.md +270 -0
- package/content/knowledge/data-pipeline/data-pipeline-architecture.md +175 -0
- package/content/knowledge/data-pipeline/data-pipeline-batch-patterns.md +263 -0
- package/content/knowledge/data-pipeline/data-pipeline-conventions.md +176 -0
- package/content/knowledge/data-pipeline/data-pipeline-dev-environment.md +350 -0
- package/content/knowledge/data-pipeline/data-pipeline-orchestration.md +291 -0
- package/content/knowledge/data-pipeline/data-pipeline-project-structure.md +257 -0
- package/content/knowledge/data-pipeline/data-pipeline-quality.md +324 -0
- package/content/knowledge/data-pipeline/data-pipeline-requirements.md +145 -0
- package/content/knowledge/data-pipeline/data-pipeline-schema-management.md +295 -0
- package/content/knowledge/data-pipeline/data-pipeline-security.md +326 -0
- package/content/knowledge/data-pipeline/data-pipeline-streaming-patterns.md +280 -0
- package/content/knowledge/data-pipeline/data-pipeline-testing.md +406 -0
- package/content/knowledge/library/library-api-design.md +306 -0
- package/content/knowledge/library/library-architecture.md +247 -0
- package/content/knowledge/library/library-bundling.md +244 -0
- package/content/knowledge/library/library-conventions.md +229 -0
- package/content/knowledge/library/library-dev-environment.md +220 -0
- package/content/knowledge/library/library-documentation.md +300 -0
- package/content/knowledge/library/library-project-structure.md +237 -0
- package/content/knowledge/library/library-requirements.md +173 -0
- package/content/knowledge/library/library-security.md +257 -0
- package/content/knowledge/library/library-testing.md +319 -0
- package/content/knowledge/library/library-type-definitions.md +284 -0
- package/content/knowledge/library/library-versioning.md +300 -0
- package/content/knowledge/ml/ml-architecture.md +172 -0
- package/content/knowledge/ml/ml-conventions.md +209 -0
- package/content/knowledge/ml/ml-dev-environment.md +299 -0
- package/content/knowledge/ml/ml-experiment-tracking.md +285 -0
- package/content/knowledge/ml/ml-model-evaluation.md +256 -0
- package/content/knowledge/ml/ml-observability.md +253 -0
- package/content/knowledge/ml/ml-project-structure.md +216 -0
- package/content/knowledge/ml/ml-requirements.md +138 -0
- package/content/knowledge/ml/ml-security.md +188 -0
- package/content/knowledge/ml/ml-serving-patterns.md +243 -0
- package/content/knowledge/ml/ml-testing.md +301 -0
- package/content/knowledge/ml/ml-training-patterns.md +269 -0
- package/content/knowledge/mobile-app/mobile-app-architecture.md +283 -0
- package/content/knowledge/mobile-app/mobile-app-conventions.md +180 -0
- package/content/knowledge/mobile-app/mobile-app-deployment.md +298 -0
- package/content/knowledge/mobile-app/mobile-app-dev-environment.md +257 -0
- package/content/knowledge/mobile-app/mobile-app-distribution.md +264 -0
- package/content/knowledge/mobile-app/mobile-app-observability.md +317 -0
- package/content/knowledge/mobile-app/mobile-app-offline-patterns.md +311 -0
- package/content/knowledge/mobile-app/mobile-app-project-structure.md +245 -0
- package/content/knowledge/mobile-app/mobile-app-push-notifications.md +321 -0
- package/content/knowledge/mobile-app/mobile-app-requirements.md +147 -0
- package/content/knowledge/mobile-app/mobile-app-security.md +338 -0
- package/content/knowledge/mobile-app/mobile-app-testing.md +400 -0
- package/content/methodology/browser-extension-overlay.yml +82 -0
- package/content/methodology/data-pipeline-overlay.yml +70 -0
- package/content/methodology/library-overlay.yml +67 -0
- package/content/methodology/ml-overlay.yml +70 -0
- package/content/methodology/mobile-app-overlay.yml +71 -0
- package/dist/cli/commands/init.d.ts +22 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +202 -3
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +190 -0
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/config/schema.d.ts +1456 -80
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +87 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/config/schema.test.js +312 -3
- package/dist/config/schema.test.js.map +1 -1
- package/dist/core/assembly/overlay-loader.test.js +55 -0
- package/dist/core/assembly/overlay-loader.test.js.map +1 -1
- package/dist/e2e/project-type-overlays.test.d.ts +2 -1
- package/dist/e2e/project-type-overlays.test.d.ts.map +1 -1
- package/dist/e2e/project-type-overlays.test.js +780 -14
- package/dist/e2e/project-type-overlays.test.js.map +1 -1
- package/dist/types/config.d.ts +16 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/wizard/questions.d.ts +28 -1
- package/dist/wizard/questions.d.ts.map +1 -1
- package/dist/wizard/questions.js +127 -1
- package/dist/wizard/questions.js.map +1 -1
- package/dist/wizard/questions.test.js +224 -4
- package/dist/wizard/questions.test.js.map +1 -1
- package/dist/wizard/wizard.d.ts +22 -0
- package/dist/wizard/wizard.d.ts.map +1 -1
- package/dist/wizard/wizard.js +28 -1
- package/dist/wizard/wizard.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobile-app-dev-environment
|
|
3
|
+
description: Simulator and emulator setup, physical device testing, hot reload, debugging tools, and developer toolchain for iOS and Android
|
|
4
|
+
topics: [mobile-app, dev-environment, simulator, emulator, debugging, xcode, android-studio, hot-reload]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Mobile development environments are significantly more complex than web development: two IDEs, two simulators, hardware-specific debugging, code signing requirements, and platform SDK updates that break builds. A well-configured dev environment reduces friction by 50% — standardize tool versions, automate simulator setup, and document the steps from clean machine to first successful build.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
iOS development requires Xcode (macOS only) with simulators managed via `xcodebuild` or Xcode's device window. Android development uses Android Studio with AVD Manager for emulators and ADB for device management. Both platforms support hot reload (SwiftUI previews, Compose previews, Metro bundler for React Native). Debugging tools include LLDB (iOS), Android Studio debugger, and Instruments/Android Profiler for performance analysis. Standardize SDK versions in `.tool-versions` or `mise.toml` to prevent team drift.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### iOS Toolchain Setup
|
|
16
|
+
|
|
17
|
+
**Required tools**
|
|
18
|
+
- Xcode: install from the Mac App Store or `xcodes` CLI — not from third-party sources
|
|
19
|
+
- Command Line Tools: `xcode-select --install`
|
|
20
|
+
- Simulators: downloaded on demand within Xcode or via `xcodebuild -downloadPlatform iOS`
|
|
21
|
+
- CocoaPods (if used): `gem install cocoapods` (prefer rbenv/mise to manage Ruby version)
|
|
22
|
+
- Bundler for Ruby tools: `gem install bundler && bundle install` (locks CocoaPods/Fastlane versions)
|
|
23
|
+
- Mint for Swift CLI tools: `brew install mint` — pin versions in `Mintfile`
|
|
24
|
+
|
|
25
|
+
**Version pinning**
|
|
26
|
+
```
|
|
27
|
+
# .tool-versions (asdf/mise)
|
|
28
|
+
xcode 15.4
|
|
29
|
+
ruby 3.3.0
|
|
30
|
+
node 20.18.0 # if using React Native
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Simulator management**
|
|
34
|
+
```bash
|
|
35
|
+
# List available simulators
|
|
36
|
+
xcrun simctl list devices
|
|
37
|
+
|
|
38
|
+
# Boot a simulator
|
|
39
|
+
xcrun simctl boot "iPhone 16 Pro"
|
|
40
|
+
|
|
41
|
+
# Open Simulator app (required to see the UI)
|
|
42
|
+
open -a Simulator
|
|
43
|
+
|
|
44
|
+
# Install app on booted simulator
|
|
45
|
+
xcrun simctl install booted path/to/MyApp.app
|
|
46
|
+
|
|
47
|
+
# Launch app
|
|
48
|
+
xcrun simctl launch booted com.example.myapp
|
|
49
|
+
|
|
50
|
+
# Take screenshot
|
|
51
|
+
xcrun simctl io booted screenshot screenshot.png
|
|
52
|
+
|
|
53
|
+
# Trigger push notification (iOS 16+)
|
|
54
|
+
xcrun simctl push booted com.example.myapp payload.json
|
|
55
|
+
|
|
56
|
+
# Reset simulator (clears all data)
|
|
57
|
+
xcrun simctl erase "iPhone 16 Pro"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Useful Xcode settings for development velocity**
|
|
61
|
+
- Enable "Show build durations": Preferences > Behaviors > Build (custom)
|
|
62
|
+
- Increase build parallelism: `defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks $(sysctl -n hw.ncpu)`
|
|
63
|
+
- Enable address sanitizer and thread sanitizer for debug builds to catch memory issues early
|
|
64
|
+
- Explicit modules: set `SWIFT_ENABLE_EXPLICIT_MODULES = YES` for faster incremental builds (Xcode 16+)
|
|
65
|
+
|
|
66
|
+
**SwiftUI Previews for hot-reload-like iteration**
|
|
67
|
+
```swift
|
|
68
|
+
#Preview {
|
|
69
|
+
UserProfileView(viewModel: UserProfileViewModel.preview)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
extension UserProfileViewModel {
|
|
73
|
+
static var preview: UserProfileViewModel {
|
|
74
|
+
let vm = UserProfileViewModel(repository: MockUserRepository())
|
|
75
|
+
vm.user = User(id: "1", name: "Jane Smith", email: "jane@example.com")
|
|
76
|
+
return vm
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Previews require a `PreviewProvider`-compatible data setup. Use protocol-based fakes rather than mocks — they compile faster and work in both tests and previews.
|
|
82
|
+
|
|
83
|
+
**Physical device setup (iOS)**
|
|
84
|
+
- Developer account: free account allows device testing; paid account required for distribution
|
|
85
|
+
- Device registration: Settings > Privacy > Developer Mode (iOS 16+) must be enabled
|
|
86
|
+
- Add device UDID to provisioning profile in Apple Developer portal, or use Automatic Signing in Xcode
|
|
87
|
+
- Trust the developer certificate on device: Settings > General > VPN & Device Management
|
|
88
|
+
|
|
89
|
+
### Android Toolchain Setup
|
|
90
|
+
|
|
91
|
+
**Required tools**
|
|
92
|
+
- Android Studio: install from developer.android.com — includes the Android SDK, build tools, and AVD Manager
|
|
93
|
+
- JDK: Android Studio bundles its own JDK; for CLI builds, use JDK 17 (Gradle 8.x requirement)
|
|
94
|
+
- ADB (Android Debug Bridge): included in Android SDK Platform Tools — add to PATH
|
|
95
|
+
- Bundletool: for testing AAB files locally
|
|
96
|
+
|
|
97
|
+
**SDK and build tools versions**
|
|
98
|
+
```kotlin
|
|
99
|
+
// app/build.gradle.kts
|
|
100
|
+
android {
|
|
101
|
+
compileSdk = 35
|
|
102
|
+
defaultConfig {
|
|
103
|
+
minSdk = 26
|
|
104
|
+
targetSdk = 35
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Pin build tools in `gradle/wrapper/gradle-wrapper.properties`:
|
|
110
|
+
```
|
|
111
|
+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**AVD (Android Virtual Device) management**
|
|
115
|
+
```bash
|
|
116
|
+
# List available AVDs
|
|
117
|
+
emulator -list-avds
|
|
118
|
+
|
|
119
|
+
# Start emulator
|
|
120
|
+
emulator -avd Pixel_9_API_35
|
|
121
|
+
|
|
122
|
+
# Or via avdmanager
|
|
123
|
+
avdmanager list avd
|
|
124
|
+
|
|
125
|
+
# Create an AVD
|
|
126
|
+
avdmanager create avd -n "Pixel_9_API_35" -k "system-images;android-35;google_apis_playstore;x86_64"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**ADB commands for development**
|
|
130
|
+
```bash
|
|
131
|
+
# List connected devices/emulators
|
|
132
|
+
adb devices
|
|
133
|
+
|
|
134
|
+
# Install APK
|
|
135
|
+
adb install -r app-debug.apk
|
|
136
|
+
|
|
137
|
+
# View logs filtered by tag
|
|
138
|
+
adb logcat -s MyApp:V
|
|
139
|
+
|
|
140
|
+
# Open app
|
|
141
|
+
adb shell am start -n com.example.myapp/.MainActivity
|
|
142
|
+
|
|
143
|
+
# Input text (useful for automation)
|
|
144
|
+
adb shell input text "testpassword"
|
|
145
|
+
|
|
146
|
+
# Tap coordinates
|
|
147
|
+
adb shell input tap 540 960
|
|
148
|
+
|
|
149
|
+
# Take screenshot
|
|
150
|
+
adb exec-out screencap -p > screenshot.png
|
|
151
|
+
|
|
152
|
+
# Enable WiFi debugging (Android 11+)
|
|
153
|
+
adb pair <ip>:<port> # pair first
|
|
154
|
+
adb connect <ip>:<port>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Physical device setup (Android)**
|
|
158
|
+
- Enable Developer Options: Settings > About Phone > tap Build Number 7 times
|
|
159
|
+
- Enable USB Debugging: Developer Options > USB Debugging
|
|
160
|
+
- For Android 11+: enable Wireless Debugging for cable-free development
|
|
161
|
+
- Trust the computer when prompted on first connection
|
|
162
|
+
|
|
163
|
+
**Compose hot reload**
|
|
164
|
+
Android Studio's "Apply Code Changes" and "Apply Changes and Restart Activity" provide incremental deployment:
|
|
165
|
+
- Apply Code Changes (lightning bolt icon): deploys changed Kotlin bytecode without restarting the app — works for logic changes
|
|
166
|
+
- Apply Changes and Restart Activity: restarts the current activity with new code — works for UI composition changes
|
|
167
|
+
- Full rebuild is still required for resource changes (strings, drawables, manifest)
|
|
168
|
+
|
|
169
|
+
Compose interactive preview:
|
|
170
|
+
```kotlin
|
|
171
|
+
@Preview(showBackground = true, name = "Light mode")
|
|
172
|
+
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark mode")
|
|
173
|
+
@Composable
|
|
174
|
+
fun UserCardPreview() {
|
|
175
|
+
MyAppTheme {
|
|
176
|
+
UserCard(user = previewUser)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Debugging Tools
|
|
182
|
+
|
|
183
|
+
**iOS debugging**
|
|
184
|
+
|
|
185
|
+
*LLDB in Xcode*
|
|
186
|
+
- Set breakpoints by clicking line numbers or: `breakpoint set --name viewDidLoad`
|
|
187
|
+
- Print expressions: `po viewModel.user` (prints object description), `p viewModel.isLoading` (prints value)
|
|
188
|
+
- Memory graph: Debug > View Memory Graph Hierarchy to inspect object retain counts
|
|
189
|
+
- View hierarchy debugger: Debug > View Debugging > Capture View Hierarchy — visualize the full UIView/SwiftUI layer tree in 3D
|
|
190
|
+
|
|
191
|
+
*Instruments*
|
|
192
|
+
- Time Profiler: find CPU hotspots, identify methods consuming > 5% of CPU time
|
|
193
|
+
- Allocations: track heap memory growth, identify retain cycles via backtraces
|
|
194
|
+
- Leaks: automated memory leak detection
|
|
195
|
+
- Network: inspect HTTP requests, timing, and response bodies
|
|
196
|
+
- Energy Log: identify battery-draining background work
|
|
197
|
+
- Core Animation: frame rendering performance, identifies GPU-bound vs CPU-bound jank
|
|
198
|
+
|
|
199
|
+
*Console.app / os_log*
|
|
200
|
+
```swift
|
|
201
|
+
import OSLog
|
|
202
|
+
private let logger = Logger(subsystem: "com.example.myapp", category: "Auth")
|
|
203
|
+
logger.debug("Login attempt for user: \(userId)")
|
|
204
|
+
logger.error("Network error: \(error.localizedDescription)")
|
|
205
|
+
```
|
|
206
|
+
Filter in Console.app by subsystem/category. OSLog is zero-cost when logging is disabled.
|
|
207
|
+
|
|
208
|
+
**Android debugging**
|
|
209
|
+
|
|
210
|
+
*Android Studio debugger*
|
|
211
|
+
- Breakpoints with conditions: right-click breakpoint > Condition
|
|
212
|
+
- Evaluate expression: Debug > Evaluate Expression (or Alt+F8) while paused
|
|
213
|
+
- Frame variable inspection: Variables pane shows all locals and fields
|
|
214
|
+
- LogCat: filter by package name, tag, or regex. Color-coded by severity level.
|
|
215
|
+
|
|
216
|
+
*Android Studio Profiler*
|
|
217
|
+
- CPU Profiler: record method traces or sample CPU usage; identify ANR-prone operations
|
|
218
|
+
- Memory Profiler: heap dump, allocation tracking, GC event visualization
|
|
219
|
+
- Network Profiler: request/response inspection, payload viewer
|
|
220
|
+
- Energy Profiler: battery usage breakdown by CPU, network, and GPS
|
|
221
|
+
|
|
222
|
+
*Debugging Compose*
|
|
223
|
+
- Layout Inspector (Android Studio Electric Eel+): live inspection of Compose tree with recomposition counts
|
|
224
|
+
- Recomposition highlighter: `Modifier.debugInspectorInfo` or Android Studio's built-in recomposition overlay
|
|
225
|
+
- `@Preview` parameter combinations to test edge cases without running the app
|
|
226
|
+
|
|
227
|
+
### Team Environment Standardization
|
|
228
|
+
|
|
229
|
+
**Mise/asdf for tool version consistency**
|
|
230
|
+
```toml
|
|
231
|
+
# .mise.toml (or .tool-versions for asdf)
|
|
232
|
+
[tools]
|
|
233
|
+
java = "17.0.12"
|
|
234
|
+
node = "20.18.0"
|
|
235
|
+
ruby = "3.3.0"
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Commit `.mise.toml` to git — every team member and CI runner uses the same tool versions.
|
|
239
|
+
|
|
240
|
+
**Fastlane for build automation**
|
|
241
|
+
```ruby
|
|
242
|
+
# Fastfile
|
|
243
|
+
lane :build_debug do
|
|
244
|
+
gradle(task: "assembleDebug")
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
lane :build_ios do
|
|
248
|
+
gym(scheme: "MyApp", configuration: "Debug")
|
|
249
|
+
end
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Document the minimum `make setup` or `./scripts/bootstrap.sh` that gets a new developer from clean machine to running app in under 10 minutes. Include:
|
|
253
|
+
1. Install Xcode/Android Studio (with version pinned)
|
|
254
|
+
2. Install system dependencies (`brew install ...`)
|
|
255
|
+
3. Install project dependencies (pods, SPM packages, Gradle sync)
|
|
256
|
+
4. Create local config files from templates
|
|
257
|
+
5. Run on simulator to verify
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobile-app-distribution
|
|
3
|
+
description: TestFlight and Google Play internal track, enterprise MDM distribution, staged rollouts, beta testing programs, and OTA updates for mobile apps
|
|
4
|
+
topics: [mobile-app, distribution, testflight, google-play, enterprise-mdm, staged-rollout, beta-testing, ota]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Mobile app distribution has more complexity than web deployment: app store review introduces unpredictable latency, staged rollouts require monitoring and rollback planning, enterprise distribution requires MDM infrastructure, and React Native/Expo apps have limited OTA update options for business logic. Design the distribution pipeline before writing code — it affects app architecture (feature flags, forced update logic) and CI/CD setup.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
iOS pre-release distribution uses TestFlight (internal team up to 100 testers, external up to 10,000). Android uses Google Play's internal testing, closed testing (alpha), and open testing (beta) tracks. Enterprise apps distribute via MDM (Jamf, Microsoft Intune, VMware Workspace ONE) or in-house provisioning. Staged rollouts (production track, percentage-based) allow monitoring before full release. Forced update patterns prevent users from running critically broken versions. React Native apps can deliver business logic updates OTA via Expo Updates or CodePush within platform policy limits.
|
|
12
|
+
|
|
13
|
+
## Deep Guidance
|
|
14
|
+
|
|
15
|
+
### TestFlight (iOS)
|
|
16
|
+
|
|
17
|
+
**Internal testing**
|
|
18
|
+
- Up to 100 internal testers (must be App Store Connect users in your team)
|
|
19
|
+
- Available within minutes of upload — no Apple review required
|
|
20
|
+
- Access via TestFlight app on device; invitations via email
|
|
21
|
+
- 90-day expiration per build; can be extended
|
|
22
|
+
|
|
23
|
+
**External testing**
|
|
24
|
+
- Up to 10,000 testers (external — no App Store Connect account required)
|
|
25
|
+
- Requires Apple review for first submission to a group (24–48 hours); subsequent builds with same metadata are faster
|
|
26
|
+
- Tester groups allow segmented beta access (e.g., "power users", "partners")
|
|
27
|
+
- Feedback is collected via TestFlight screenshot feedback feature
|
|
28
|
+
|
|
29
|
+
**Fastlane Pilot for automated TestFlight upload**
|
|
30
|
+
```ruby
|
|
31
|
+
lane :beta do
|
|
32
|
+
# Build
|
|
33
|
+
gym(
|
|
34
|
+
scheme: "MyApp",
|
|
35
|
+
configuration: "Release",
|
|
36
|
+
export_method: "app-store"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Upload to TestFlight
|
|
40
|
+
pilot(
|
|
41
|
+
app_identifier: "com.example.myapp",
|
|
42
|
+
changelog: ENV["CHANGELOG"] || git_commit_message,
|
|
43
|
+
distribute_external: false,
|
|
44
|
+
notify_external_testers: false,
|
|
45
|
+
skip_waiting_for_build_processing: false # wait for processing before distributing
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**App Store Connect API key for CI**
|
|
51
|
+
```ruby
|
|
52
|
+
# Authenticate with API key (no 2FA required — safe for CI)
|
|
53
|
+
app_store_connect_api_key(
|
|
54
|
+
key_id: ENV["ASC_KEY_ID"],
|
|
55
|
+
issuer_id: ENV["ASC_ISSUER_ID"],
|
|
56
|
+
key_content: ENV["ASC_PRIVATE_KEY"],
|
|
57
|
+
is_key_content_base64: true,
|
|
58
|
+
in_house: false
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Generate the API key in App Store Connect > Users and Access > Keys. Store as CI secrets — this key has broad permissions.
|
|
63
|
+
|
|
64
|
+
**TestFlight build metadata**
|
|
65
|
+
- `What to Test` field: mandatory for external testing review; tells reviewers what to focus on
|
|
66
|
+
- Build version must be higher than any previously uploaded build — automate with Fastlane's `increment_build_number`
|
|
67
|
+
- Keep a changelog per build: git commit summaries work for internal; user-friendly summaries for external
|
|
68
|
+
|
|
69
|
+
**Crash-free rate monitoring**
|
|
70
|
+
Monitor TestFlight crash-free rate in Xcode Organizer or App Store Connect. Set a threshold (e.g., < 99% crash-free is a blocker) before promoting a TestFlight build to App Store production.
|
|
71
|
+
|
|
72
|
+
### Google Play Distribution Tracks
|
|
73
|
+
|
|
74
|
+
**Track hierarchy**
|
|
75
|
+
```
|
|
76
|
+
Internal testing → Closed testing (alpha) → Open testing (beta) → Production
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Each track has its own review requirements and rollout speed:
|
|
80
|
+
- Internal: up to 100 testers, available in minutes, no review
|
|
81
|
+
- Closed (alpha): invite-only Google Groups, available in hours, Google review required
|
|
82
|
+
- Open (beta): public opt-in, available in days, Google review required
|
|
83
|
+
- Production: public, subject to full review
|
|
84
|
+
|
|
85
|
+
**Track promotion strategy**
|
|
86
|
+
```
|
|
87
|
+
CI/CD → Internal testing (every PR merge)
|
|
88
|
+
→ Closed alpha (every weekly release candidate)
|
|
89
|
+
→ Open beta (after 3 days crash-free in alpha)
|
|
90
|
+
→ Production 10% (staged rollout start)
|
|
91
|
+
→ Production 100% (after 48 hours monitoring)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Fastlane Supply for Play Store**
|
|
95
|
+
```ruby
|
|
96
|
+
lane :deploy_internal do
|
|
97
|
+
gradle(task: "bundle", build_type: "Release")
|
|
98
|
+
supply(
|
|
99
|
+
track: "internal",
|
|
100
|
+
aab: "app/build/outputs/bundle/release/app-release.aab",
|
|
101
|
+
package_name: "com.example.myapp",
|
|
102
|
+
json_key_data: ENV["PLAY_STORE_SERVICE_ACCOUNT_JSON"]
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
lane :promote_to_beta do
|
|
107
|
+
supply(
|
|
108
|
+
track: "alpha",
|
|
109
|
+
track_promote_to: "beta",
|
|
110
|
+
package_name: "com.example.myapp"
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Service account authentication**
|
|
116
|
+
- Create a service account in Google Play Console: Setup > API access
|
|
117
|
+
- Grant the service account "Release Manager" role
|
|
118
|
+
- Download the JSON key file — store as a CI secret (`PLAY_STORE_SERVICE_ACCOUNT_JSON`)
|
|
119
|
+
- Rotate the service account key annually
|
|
120
|
+
|
|
121
|
+
### Staged Rollouts
|
|
122
|
+
|
|
123
|
+
**iOS: Phased Release**
|
|
124
|
+
App Store Connect supports phased release for iOS apps:
|
|
125
|
+
- Day 1: 1% of eligible users
|
|
126
|
+
- Day 2: 2%
|
|
127
|
+
- Day 3–6: doubling per day to 5%, 10%, 20%, 50%
|
|
128
|
+
- Day 7: 100%
|
|
129
|
+
|
|
130
|
+
Pause the rollout if crash rate spikes. Resume manually when stable. This is a 7-day automatic progression — you cannot customize the percentages on iOS.
|
|
131
|
+
|
|
132
|
+
**Android: Staged Production Rollout**
|
|
133
|
+
Google Play provides fine-grained percentage control:
|
|
134
|
+
```kotlin
|
|
135
|
+
// Via Fastlane
|
|
136
|
+
supply(
|
|
137
|
+
track: "production",
|
|
138
|
+
rollout: "0.1", // 10% rollout
|
|
139
|
+
aab: "app-release.aab"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
// Increase rollout after monitoring
|
|
143
|
+
supply(
|
|
144
|
+
track: "production",
|
|
145
|
+
rollout: "0.5" // promote to 50%
|
|
146
|
+
)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Rollout monitoring checklist**
|
|
150
|
+
Before increasing rollout percentage:
|
|
151
|
+
- Crash-free users rate > 99.5% (Firebase Crashlytics / Play Console)
|
|
152
|
+
- ANR (Application Not Responding) rate < 0.25% (Android Play Console)
|
|
153
|
+
- Network error rate stable (Firebase Performance / custom dashboard)
|
|
154
|
+
- Revenue/conversion metrics not regressing (analytics)
|
|
155
|
+
- No P0 support tickets or social media reports
|
|
156
|
+
|
|
157
|
+
**Rollback strategy**
|
|
158
|
+
iOS: halt phased release in App Store Connect; users who already updated cannot be rolled back (no downgrade mechanism). Prepare a hotfix release and fast-track it through the queue.
|
|
159
|
+
|
|
160
|
+
Android: halt staged rollout and set rollout to 0%. Users who already updated are on the new version. Prepare a hotfix and expedite through the Play review queue.
|
|
161
|
+
|
|
162
|
+
### Enterprise MDM Distribution
|
|
163
|
+
|
|
164
|
+
**iOS: In-House (Apple Developer Enterprise Program)**
|
|
165
|
+
- Requires Apple Developer Enterprise Program ($299/year)
|
|
166
|
+
- Apps distributed via a hosted IPA with a distribution manifest plist
|
|
167
|
+
- Install via Safari on device: `itms-services://?action=download-manifest&url=https://example.com/manifest.plist`
|
|
168
|
+
- No App Store review — responsibility for content is entirely the enterprise's
|
|
169
|
+
- Provisioning profiles expire annually — plan renewal in advance
|
|
170
|
+
|
|
171
|
+
**iOS: MDM-managed distribution**
|
|
172
|
+
- Push apps to enrolled devices via MDM (Jamf, Microsoft Intune, Mosyle)
|
|
173
|
+
- Apps can be silently installed on managed devices without user interaction
|
|
174
|
+
- VPP (Volume Purchase Program) for App Store apps; custom B2B apps for private distribution
|
|
175
|
+
- Device enrollment: Device Enrollment Program (DEP/ABM) for zero-touch setup
|
|
176
|
+
|
|
177
|
+
**Android: Enterprise distribution**
|
|
178
|
+
- Google Play managed: publish to a private Google Play track visible only to enrolled organization devices
|
|
179
|
+
- APK sideloading via MDM: push APK directly to managed devices
|
|
180
|
+
- Android Enterprise fully managed device profile for dedicated devices (kiosks, logistics scanners)
|
|
181
|
+
- `android:sharedUserId` removed in API 29+ — do not rely on shared user IDs for enterprise app families
|
|
182
|
+
|
|
183
|
+
**MDM lifecycle events**
|
|
184
|
+
Apps distributed via MDM receive signals for:
|
|
185
|
+
- Device enrollment/unenrollment: handle data wipe
|
|
186
|
+
- Policy changes: respond to new restrictions (camera, clipboard, screen capture)
|
|
187
|
+
- Managed app configuration: receive key-value configuration from MDM without hardcoding server URLs
|
|
188
|
+
|
|
189
|
+
### Forced Update Patterns
|
|
190
|
+
|
|
191
|
+
**Remote config-driven forced update**
|
|
192
|
+
```swift
|
|
193
|
+
// iOS: Firebase Remote Config
|
|
194
|
+
let remoteConfig = RemoteConfig.remoteConfig()
|
|
195
|
+
remoteConfig.fetchAndActivate { status, error in
|
|
196
|
+
let minVersion = remoteConfig.configValue(forKey: "min_required_version").stringValue
|
|
197
|
+
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0"
|
|
198
|
+
|
|
199
|
+
if currentVersion.isOlderThan(minVersion) {
|
|
200
|
+
// Show forced update modal — no dismissal
|
|
201
|
+
ForceUpdateViewController.show(on: rootViewController)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```kotlin
|
|
207
|
+
// Android: Firebase Remote Config
|
|
208
|
+
remoteConfig.fetchAndActivate().addOnCompleteListener { task ->
|
|
209
|
+
val minVersion = remoteConfig.getString("min_required_version")
|
|
210
|
+
val currentVersion = BuildConfig.VERSION_NAME
|
|
211
|
+
|
|
212
|
+
if (currentVersion.isOlderThan(minVersion)) {
|
|
213
|
+
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName")))
|
|
214
|
+
finish()
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Forced update design principles**
|
|
220
|
+
- Show the forced update screen only for critical security fixes or data-breaking changes — not convenience
|
|
221
|
+
- Always provide context: "This update is required to keep your account secure"
|
|
222
|
+
- Deep link to the App Store / Play Store update page
|
|
223
|
+
- Never block the update prompt — no dismiss button for forced updates
|
|
224
|
+
- For soft updates (recommended but not required), show a dismissable banner with a "Update now" action
|
|
225
|
+
|
|
226
|
+
### OTA (Over-the-Air) Updates for React Native / Expo
|
|
227
|
+
|
|
228
|
+
**Expo Updates**
|
|
229
|
+
```json
|
|
230
|
+
// app.json
|
|
231
|
+
{
|
|
232
|
+
"expo": {
|
|
233
|
+
"updates": {
|
|
234
|
+
"enabled": true,
|
|
235
|
+
"checkAutomatically": "ON_LOAD",
|
|
236
|
+
"fallbackToCacheTimeout": 0
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import * as Updates from 'expo-updates';
|
|
244
|
+
|
|
245
|
+
async function checkForUpdate() {
|
|
246
|
+
try {
|
|
247
|
+
const update = await Updates.checkForUpdateAsync();
|
|
248
|
+
if (update.isAvailable) {
|
|
249
|
+
await Updates.fetchUpdateAsync();
|
|
250
|
+
// Prompt user or reload silently
|
|
251
|
+
await Updates.reloadAsync();
|
|
252
|
+
}
|
|
253
|
+
} catch (error) {
|
|
254
|
+
// Update check failure should not affect UX
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**OTA update constraints (platform policy)**
|
|
260
|
+
- iOS: OTA updates may only deliver JavaScript and asset changes — cannot modify native modules, add permissions, or change app metadata
|
|
261
|
+
- Android: same restrictions — OTA cannot add new native capabilities
|
|
262
|
+
- Both platforms: OTA updates that change app behavior to circumvent App Store / Play Store policies result in account termination
|
|
263
|
+
- Safe to OTA: bug fixes in JS logic, UI changes, copy changes, non-native feature additions
|
|
264
|
+
- Requires native build: new permissions, new native modules, SDK upgrades, binary dependencies
|