@rune-kit/rune 2.1.1 → 2.2.1
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 +40 -34
- package/compiler/__tests__/pack-split.test.js +145 -0
- package/compiler/adapters/antigravity.js +1 -1
- package/compiler/adapters/codex.js +77 -0
- package/compiler/adapters/cursor.js +1 -1
- package/compiler/adapters/generic.js +1 -1
- package/compiler/adapters/index.js +4 -0
- package/compiler/adapters/opencode.js +86 -0
- package/compiler/adapters/windsurf.js +1 -1
- package/compiler/bin/rune.js +10 -7
- package/compiler/doctor.js +42 -0
- package/compiler/emitter.js +64 -10
- package/compiler/parser.js +42 -3
- package/compiler/transformer.js +10 -6
- package/compiler/transforms/branding.js +1 -1
- package/compiler/transforms/compliance.js +40 -0
- package/extensions/ai-ml/PACK.md +38 -474
- package/extensions/ai-ml/skills/ai-agents.md +172 -0
- package/extensions/ai-ml/skills/code-sandbox.md +187 -0
- package/extensions/ai-ml/skills/deep-research.md +146 -0
- package/extensions/ai-ml/skills/embedding-search.md +66 -0
- package/extensions/ai-ml/skills/fine-tuning-guide.md +74 -0
- package/extensions/ai-ml/skills/llm-architect.md +125 -0
- package/extensions/ai-ml/skills/llm-integration.md +64 -0
- package/extensions/ai-ml/skills/prompt-patterns.md +72 -0
- package/extensions/ai-ml/skills/rag-patterns.md +66 -0
- package/extensions/ai-ml/skills/web-extraction.md +114 -0
- package/extensions/analytics/PACK.md +19 -484
- package/extensions/analytics/skills/ab-testing.md +72 -0
- package/extensions/analytics/skills/dashboard-patterns.md +83 -0
- package/extensions/analytics/skills/data-validation.md +68 -0
- package/extensions/analytics/skills/funnel-analysis.md +81 -0
- package/extensions/analytics/skills/sql-patterns.md +57 -0
- package/extensions/analytics/skills/statistical-analysis.md +79 -0
- package/extensions/analytics/skills/tracking-setup.md +71 -0
- package/extensions/backend/PACK.md +44 -618
- package/extensions/backend/skills/api-patterns.md +84 -0
- package/extensions/backend/skills/async-pipeline.md +193 -0
- package/extensions/backend/skills/auth-patterns.md +97 -0
- package/extensions/backend/skills/background-jobs.md +133 -0
- package/extensions/backend/skills/caching-patterns.md +108 -0
- package/extensions/backend/skills/cli-generation.md +133 -0
- package/extensions/backend/skills/database-patterns.md +87 -0
- package/extensions/backend/skills/middleware-patterns.md +104 -0
- package/extensions/chrome-ext/PACK.md +19 -921
- package/extensions/chrome-ext/skills/cws-preflight.md +143 -0
- package/extensions/chrome-ext/skills/cws-publish.md +104 -0
- package/extensions/chrome-ext/skills/ext-ai-integration.md +251 -0
- package/extensions/chrome-ext/skills/ext-messaging.md +139 -0
- package/extensions/chrome-ext/skills/ext-storage.md +133 -0
- package/extensions/chrome-ext/skills/mv3-scaffold.md +164 -0
- package/extensions/content/PACK.md +43 -335
- package/extensions/content/skills/blog-patterns.md +88 -0
- package/extensions/content/skills/cms-integration.md +131 -0
- package/extensions/content/skills/content-scoring.md +107 -0
- package/extensions/content/skills/i18n.md +83 -0
- package/extensions/content/skills/mdx-authoring.md +137 -0
- package/extensions/content/skills/reference.md +1014 -0
- package/extensions/content/skills/seo-patterns.md +67 -0
- package/extensions/content/skills/video-repurpose.md +153 -0
- package/extensions/devops/PACK.md +38 -457
- package/extensions/devops/skills/chaos-testing.md +67 -0
- package/extensions/devops/skills/ci-cd.md +75 -0
- package/extensions/devops/skills/docker.md +58 -0
- package/extensions/devops/skills/edge-serverless.md +163 -0
- package/extensions/devops/skills/infra-as-code.md +158 -0
- package/extensions/devops/skills/kubernetes.md +110 -0
- package/extensions/devops/skills/monitoring.md +57 -0
- package/extensions/devops/skills/server-setup.md +64 -0
- package/extensions/devops/skills/ssl-domain.md +42 -0
- package/extensions/ecommerce/PACK.md +62 -226
- package/extensions/ecommerce/skills/cart-system.md +79 -0
- package/extensions/ecommerce/skills/inventory-mgmt.md +102 -0
- package/extensions/ecommerce/skills/order-management.md +126 -0
- package/extensions/ecommerce/skills/payment-integration.md +472 -0
- package/extensions/ecommerce/skills/shopify-dev.md +69 -0
- package/extensions/ecommerce/skills/subscription-billing.md +93 -0
- package/extensions/ecommerce/skills/tax-compliance.md +117 -0
- package/extensions/gamedev/PACK.md +66 -317
- package/extensions/gamedev/skills/asset-pipeline.md +74 -0
- package/extensions/gamedev/skills/audio-system.md +129 -0
- package/extensions/gamedev/skills/camera-system.md +87 -0
- package/extensions/gamedev/skills/ecs.md +98 -0
- package/extensions/gamedev/skills/game-loops.md +72 -0
- package/extensions/gamedev/skills/input-system.md +199 -0
- package/extensions/gamedev/skills/multiplayer.md +180 -0
- package/extensions/gamedev/skills/particles.md +105 -0
- package/extensions/gamedev/skills/physics-engine.md +89 -0
- package/extensions/gamedev/skills/scene-management.md +146 -0
- package/extensions/gamedev/skills/threejs-patterns.md +90 -0
- package/extensions/gamedev/skills/webgl.md +71 -0
- package/extensions/mobile/PACK.md +56 -223
- package/extensions/mobile/skills/app-store-connect.md +152 -0
- package/extensions/mobile/skills/app-store-prep.md +66 -0
- package/extensions/mobile/skills/deep-linking.md +109 -0
- package/extensions/mobile/skills/flutter.md +60 -0
- package/extensions/mobile/skills/ios-build-pipeline.md +142 -0
- package/extensions/mobile/skills/native-bridge.md +66 -0
- package/extensions/mobile/skills/ota-updates.md +97 -0
- package/extensions/mobile/skills/push-notifications.md +111 -0
- package/extensions/mobile/skills/react-native.md +82 -0
- package/extensions/saas/PACK.md +26 -720
- package/extensions/saas/skills/billing-integration.md +121 -0
- package/extensions/saas/skills/feature-flags.md +130 -0
- package/extensions/saas/skills/multi-tenant.md +103 -0
- package/extensions/saas/skills/onboarding-flow.md +139 -0
- package/extensions/saas/skills/subscription-flow.md +95 -0
- package/extensions/saas/skills/team-management.md +144 -0
- package/extensions/security/PACK.md +10 -448
- package/extensions/security/skills/api-security.md +140 -0
- package/extensions/security/skills/compliance.md +68 -0
- package/extensions/security/skills/owasp-audit.md +64 -0
- package/extensions/security/skills/pentest-patterns.md +77 -0
- package/extensions/security/skills/secret-mgmt.md +65 -0
- package/extensions/security/skills/supply-chain.md +65 -0
- package/extensions/trading/PACK.md +18 -535
- package/extensions/trading/skills/chart-components.md +55 -0
- package/extensions/trading/skills/experiment-loop.md +125 -0
- package/extensions/trading/skills/fintech-patterns.md +47 -0
- package/extensions/trading/skills/indicator-library.md +58 -0
- package/extensions/trading/skills/quant-analysis.md +111 -0
- package/extensions/trading/skills/realtime-data.md +58 -0
- package/extensions/trading/skills/trade-logic.md +104 -0
- package/extensions/ui/PACK.md +34 -853
- package/extensions/ui/skills/a11y-audit.md +91 -0
- package/extensions/ui/skills/animation-patterns.md +106 -0
- package/extensions/ui/skills/component-patterns.md +75 -0
- package/extensions/ui/skills/design-decision.md +98 -0
- package/extensions/ui/skills/design-system.md +68 -0
- package/extensions/ui/skills/landing-patterns.md +155 -0
- package/extensions/ui/skills/palette-picker.md +162 -0
- package/extensions/ui/skills/react-health.md +90 -0
- package/extensions/ui/skills/type-system.md +125 -0
- package/extensions/ui/skills/web-vitals.md +153 -0
- package/extensions/zalo/PACK.md +117 -0
- package/extensions/zalo/skills/zalo-oa-mcp.md +317 -0
- package/extensions/zalo/skills/zalo-oa-messaging.md +429 -0
- package/extensions/zalo/skills/zalo-oa-setup.md +236 -0
- package/extensions/zalo/skills/zalo-oa-webhook.md +189 -0
- package/extensions/zalo/skills/zalo-personal-messaging.md +194 -0
- package/extensions/zalo/skills/zalo-personal-setup.md +153 -0
- package/extensions/zalo/skills/zalo-rate-guard.md +219 -0
- package/package.json +5 -2
- package/skills/brainstorm/SKILL.md +63 -1
- package/skills/cook/SKILL.md +89 -6
- package/skills/debug/SKILL.md +5 -0
- package/skills/fix/SKILL.md +5 -0
- package/skills/mcp-builder/SKILL.md +48 -1
- package/skills/neural-memory/SKILL.md +362 -0
- package/skills/plan/SKILL.md +3 -0
- package/skills/rescue/SKILL.md +5 -0
- package/skills/review/SKILL.md +44 -5
- package/skills/review-intake/SKILL.md +17 -1
- package/skills/skill-router/SKILL.md +106 -8
- package/skills/team/SKILL.md +24 -1
- package/skills/test/SKILL.md +18 -0
- package/skills/verification/SKILL.md +40 -1
|
@@ -1,273 +1,106 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "@rune/mobile"
|
|
3
|
-
description: Mobile development patterns — React Native, Flutter, app store preparation,
|
|
3
|
+
description: Mobile development patterns — React Native, Flutter, deep linking, push notifications, OTA updates, app store preparation, native bridge integration, iOS build pipeline, and App Store Connect automation.
|
|
4
4
|
metadata:
|
|
5
5
|
author: runedev
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.3.0"
|
|
7
7
|
layer: L4
|
|
8
8
|
price: "$15"
|
|
9
9
|
target: Mobile developers
|
|
10
|
+
format: split
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
# @rune/mobile
|
|
13
14
|
|
|
14
15
|
## Purpose
|
|
15
16
|
|
|
16
|
-
Mobile development has platform-specific pitfalls that web developers hit repeatedly: navigation stacks that leak memory, FlatList rendering that drops frames,
|
|
17
|
+
Mobile development has platform-specific pitfalls that web developers hit repeatedly: navigation stacks that leak memory, FlatList rendering that drops frames, New Architecture migration that silently breaks third-party libraries, deep links that work in dev but fail in production, push notifications that never arrive on iOS, OTA updates that crash on bytecode mismatch, and app store rejections for missing privacy manifests. This pack provides patterns for React Native and Flutter — detect the framework, audit for mobile-specific anti-patterns, and emit fixes that pass platform review.
|
|
17
18
|
|
|
18
19
|
## Triggers
|
|
19
20
|
|
|
20
21
|
- Auto-trigger: when `react-native`, `expo`, `flutter`, `android/`, `ios/`, `app.json` (Expo) detected
|
|
21
22
|
- `/rune react-native` — audit React Native architecture and performance
|
|
22
23
|
- `/rune flutter` — audit Flutter architecture and state management
|
|
24
|
+
- `/rune deep-linking` — set up or audit deep linking (Universal Links, App Links)
|
|
25
|
+
- `/rune push-notifications` — set up or audit push notification pipeline
|
|
26
|
+
- `/rune ota-updates` — set up or audit OTA update strategy
|
|
23
27
|
- `/rune app-store-prep` — prepare app store submission
|
|
24
28
|
- `/rune native-bridge` — audit or create native module bridges
|
|
29
|
+
- `/rune ios-build` — end-to-end iOS build, sign, archive, upload pipeline
|
|
30
|
+
- `/rune app-store-connect` — App Store Connect API operations (versions, screenshots, localization, IAPs)
|
|
25
31
|
- Called by `cook` (L1) when mobile task detected
|
|
26
32
|
- Called by `team` (L1) when porting web to mobile
|
|
27
33
|
|
|
28
34
|
## Skills Included
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
| Skill | Model | Description |
|
|
37
|
+
|-------|-------|-------------|
|
|
38
|
+
| react-native | sonnet | New Architecture migration, navigation, state management, performance optimization |
|
|
39
|
+
| flutter | sonnet | Widget composition, Riverpod/BLoC state, platform channels, adaptive layouts |
|
|
40
|
+
| deep-linking | sonnet | Universal Links (iOS), App Links (Android), auth + deep link race condition |
|
|
41
|
+
| push-notifications | sonnet | FCM v1, APNs, Expo Notifications, permission handling, delivery debugging |
|
|
42
|
+
| ota-updates | sonnet | EAS Update, runtime version management, rollback, bytecode compatibility |
|
|
43
|
+
| app-store-prep | sonnet | Screenshots, metadata, privacy manifests, submission checklist |
|
|
44
|
+
| native-bridge | sonnet | Expo Modules API, TurboModules, Swift/Kotlin interop, background tasks |
|
|
45
|
+
| ios-build-pipeline | sonnet | Certificate generation, provisioning, Xcode archive, IPA export, TestFlight upload |
|
|
46
|
+
| app-store-connect | sonnet | Version management, localization, screenshot upload, IAP, review submission |
|
|
31
47
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
#### Workflow
|
|
35
|
-
|
|
36
|
-
**Step 1 — Detect React Native setup**
|
|
37
|
-
Use Grep to find framework markers: `react-native` in package.json, `expo` config, navigation library (`@react-navigation`, `expo-router`), state management (`zustand`, `redux`, `jotai`), and native module usage. Read `app.json`/`app.config.js` for Expo configuration.
|
|
38
|
-
|
|
39
|
-
**Step 2 — Audit performance patterns**
|
|
40
|
-
Check for: FlatList without `keyExtractor` or with inline `renderItem` (re-renders), images without caching (`FastImage` or `expo-image`), heavy re-renders from context (missing `useMemo`/`useCallback`), navigation listeners not cleaned up, large JS bundle without lazy loading (`React.lazy` + `Suspense`).
|
|
41
|
-
|
|
42
|
-
**Step 3 — Emit optimized patterns**
|
|
43
|
-
For each issue, emit the fix: memoized FlatList item components, proper image caching setup, navigation with typed routes, optimized state selectors, and Hermes engine configuration.
|
|
44
|
-
|
|
45
|
-
#### Example
|
|
46
|
-
|
|
47
|
-
```tsx
|
|
48
|
-
// BEFORE: FlatList anti-patterns — re-renders every item on any state change
|
|
49
|
-
<FlatList
|
|
50
|
-
data={items}
|
|
51
|
-
renderItem={({ item }) => <ItemCard item={item} onPress={() => nav.navigate('Detail', { id: item.id })} />}
|
|
52
|
-
/>
|
|
53
|
-
|
|
54
|
-
// AFTER: memoized components, stable callbacks, proper key extraction
|
|
55
|
-
const MemoizedCard = React.memo(({ item, onPress }) => (
|
|
56
|
-
<ItemCard item={item} onPress={onPress} />
|
|
57
|
-
));
|
|
58
|
-
|
|
59
|
-
const renderItem = useCallback(({ item }) => (
|
|
60
|
-
<MemoizedCard item={item} onPress={() => nav.navigate('Detail', { id: item.id })} />
|
|
61
|
-
), [nav]);
|
|
62
|
-
|
|
63
|
-
<FlatList
|
|
64
|
-
data={items}
|
|
65
|
-
renderItem={renderItem}
|
|
66
|
-
keyExtractor={item => item.id}
|
|
67
|
-
getItemLayout={(_, index) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index })}
|
|
68
|
-
maxToRenderPerBatch={10}
|
|
69
|
-
windowSize={5}
|
|
70
|
-
/>
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
### flutter
|
|
76
|
-
|
|
77
|
-
Flutter patterns — widget composition, state management (Riverpod, BLoC), platform channels, adaptive layouts.
|
|
78
|
-
|
|
79
|
-
#### Workflow
|
|
80
|
-
|
|
81
|
-
**Step 1 — Detect Flutter architecture**
|
|
82
|
-
Use Grep to find state management (`riverpod`, `flutter_bloc`, `provider`, `get_it`), routing (`go_router`, `auto_route`), and platform channel usage. Read `pubspec.yaml` for dependencies and `lib/` structure for architecture pattern (feature-first, layer-first).
|
|
83
|
-
|
|
84
|
-
**Step 2 — Audit widget tree and state**
|
|
85
|
-
Check for: `setState` in complex widgets (should use state management), deeply nested widget trees (extract widgets), `BuildContext` passed through many layers (use InheritedWidget or Riverpod), missing `const` constructors (unnecessary rebuilds), platform-specific code without adaptive checks.
|
|
86
|
-
|
|
87
|
-
**Step 3 — Emit refactored patterns**
|
|
88
|
-
For each issue, emit: extracted widget with const constructor, Riverpod provider for state, proper error handling with `AsyncValue`, and adaptive layout using `LayoutBuilder` + breakpoints.
|
|
89
|
-
|
|
90
|
-
#### Example
|
|
91
|
-
|
|
92
|
-
```dart
|
|
93
|
-
// BEFORE: setState in complex widget, no separation
|
|
94
|
-
class HomeScreen extends StatefulWidget { ... }
|
|
95
|
-
class _HomeScreenState extends State<HomeScreen> {
|
|
96
|
-
List<Item> items = [];
|
|
97
|
-
bool loading = true;
|
|
98
|
-
|
|
99
|
-
@override
|
|
100
|
-
void initState() {
|
|
101
|
-
super.initState();
|
|
102
|
-
fetchItems().then((data) => setState(() { items = data; loading = false; }));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// AFTER: Riverpod with AsyncValue, separated concerns
|
|
107
|
-
@riverpod
|
|
108
|
-
Future<List<Item>> items(Ref ref) async {
|
|
109
|
-
final repo = ref.watch(itemRepositoryProvider);
|
|
110
|
-
return repo.fetchAll();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
class HomeScreen extends ConsumerWidget {
|
|
114
|
-
const HomeScreen({super.key});
|
|
115
|
-
|
|
116
|
-
@override
|
|
117
|
-
Widget build(BuildContext context, WidgetRef ref) {
|
|
118
|
-
final itemsAsync = ref.watch(itemsProvider);
|
|
119
|
-
return itemsAsync.when(
|
|
120
|
-
data: (items) => ItemList(items: items),
|
|
121
|
-
loading: () => const ShimmerList(),
|
|
122
|
-
error: (err, stack) => ErrorView(message: err.toString(), onRetry: () => ref.invalidate(itemsProvider)),
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
### app-store-prep
|
|
131
|
-
|
|
132
|
-
App store submission preparation — screenshots, metadata, privacy policy, review guidelines compliance, TestFlight/internal testing.
|
|
133
|
-
|
|
134
|
-
#### Workflow
|
|
135
|
-
|
|
136
|
-
**Step 1 — Audit submission readiness**
|
|
137
|
-
Check for: app icon (1024x1024 for iOS, adaptive for Android), splash screen, privacy policy URL in app config, required permissions with usage descriptions (`NSCameraUsageDescription`, etc.), minimum SDK versions, and build configuration (release signing).
|
|
138
|
-
|
|
139
|
-
**Step 2 — Generate metadata**
|
|
140
|
-
From README and app config, generate: app title (30 chars max), subtitle (30 chars), description (4000 chars), keywords (100 chars), category selection, age rating questionnaire answers, and screenshot specifications per device size.
|
|
141
|
-
|
|
142
|
-
**Step 3 — Emit submission checklist**
|
|
143
|
-
Output a structured checklist covering both platforms: Apple App Store (TestFlight build, privacy declarations, review notes) and Google Play (internal testing track, data safety form, content rating questionnaire).
|
|
144
|
-
|
|
145
|
-
#### Example
|
|
146
|
-
|
|
147
|
-
```markdown
|
|
148
|
-
## App Store Submission Checklist
|
|
149
|
-
|
|
150
|
-
### iOS (Apple App Store Connect)
|
|
151
|
-
- [ ] App icon: 1024x1024 PNG, no alpha, no rounded corners
|
|
152
|
-
- [ ] Screenshots: 6.7" (1290x2796), 6.5" (1242x2688), 5.5" (1242x2208)
|
|
153
|
-
- [ ] Privacy policy URL: https://example.com/privacy
|
|
154
|
-
- [ ] NSCameraUsageDescription: "Used to scan QR codes for quick login"
|
|
155
|
-
- [ ] NSLocationWhenInUseUsageDescription: "Used to show nearby stores"
|
|
156
|
-
- [ ] TestFlight build uploaded and tested
|
|
157
|
-
- [ ] Export compliance: Uses HTTPS only (no custom encryption) → select "No"
|
|
158
|
-
|
|
159
|
-
### Android (Google Play Console)
|
|
160
|
-
- [ ] Adaptive icon: foreground (108dp) + background layer
|
|
161
|
-
- [ ] Feature graphic: 1024x500 PNG
|
|
162
|
-
- [ ] Data safety form: camera (optional), location (optional)
|
|
163
|
-
- [ ] Content rating: IARC questionnaire completed
|
|
164
|
-
- [ ] Internal testing track: at least 1 build tested
|
|
165
|
-
- [ ] Signing: upload key + app signing by Google Play enabled
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
### native-bridge
|
|
171
|
-
|
|
172
|
-
Native bridge patterns — platform-specific code, native module creation, Swift/Kotlin interop, background tasks.
|
|
173
|
-
|
|
174
|
-
#### Workflow
|
|
175
|
-
|
|
176
|
-
**Step 1 — Detect bridge requirements**
|
|
177
|
-
Use Grep to find platform-specific code: `Platform.OS`, `Platform.select`, `NativeModules`, `MethodChannel`, Turbo Modules (`TurboModule`), or Expo modules (`expo-modules-core`). Read existing native code in `ios/` and `android/` directories.
|
|
178
|
-
|
|
179
|
-
**Step 2 — Audit bridge safety**
|
|
180
|
-
Check for: type mismatches between JS/Dart and native (string expected, int sent), missing error handling on native side, synchronous bridge calls blocking UI thread, missing null checks on platform-specific returns, and hardcoded platform assumptions.
|
|
181
|
-
|
|
182
|
-
**Step 3 — Emit type-safe bridge**
|
|
183
|
-
For React Native: emit Turbo Module with codegen types or Expo Module with TypeScript interface. For Flutter: emit MethodChannel with proper error handling, type-safe serialization, and platform-specific implementations for both iOS (Swift) and Android (Kotlin).
|
|
184
|
-
|
|
185
|
-
#### Example
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
// React Native — Expo Module (type-safe, modern approach)
|
|
189
|
-
// modules/haptics/index.ts
|
|
190
|
-
import { NativeModule, requireNativeModule } from 'expo-modules-core';
|
|
191
|
-
|
|
192
|
-
interface HapticsModule extends NativeModule {
|
|
193
|
-
impact(style: 'light' | 'medium' | 'heavy'): void;
|
|
194
|
-
notification(type: 'success' | 'warning' | 'error'): void;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const HapticsNative = requireNativeModule<HapticsModule>('Haptics');
|
|
198
|
-
|
|
199
|
-
export function impact(style: 'light' | 'medium' | 'heavy' = 'medium') {
|
|
200
|
-
HapticsNative.impact(style);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// modules/haptics/ios/HapticsModule.swift
|
|
204
|
-
import ExpoModulesCore
|
|
205
|
-
import UIKit
|
|
206
|
-
|
|
207
|
-
public class HapticsModule: Module {
|
|
208
|
-
public func definition() -> ModuleDefinition {
|
|
209
|
-
Name("Haptics")
|
|
210
|
-
Function("impact") { (style: String) in
|
|
211
|
-
let generator: UIImpactFeedbackGenerator
|
|
212
|
-
switch style {
|
|
213
|
-
case "light": generator = UIImpactFeedbackGenerator(style: .light)
|
|
214
|
-
case "heavy": generator = UIImpactFeedbackGenerator(style: .heavy)
|
|
215
|
-
default: generator = UIImpactFeedbackGenerator(style: .medium)
|
|
216
|
-
}
|
|
217
|
-
generator.impactOccurred()
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
---
|
|
48
|
+
Skill files: `skills/<skill-name>.md`
|
|
224
49
|
|
|
225
50
|
## Connections
|
|
226
51
|
|
|
227
52
|
```
|
|
228
53
|
Calls → browser-pilot (L3): device testing and screenshot automation
|
|
229
54
|
Calls → asset-creator (L3): generate app icons and splash screens
|
|
55
|
+
Calls → sentinel (L2): audit push notification security, deep link validation
|
|
56
|
+
Calls → verification (L3): run mobile-specific checks (build, lint, type-check)
|
|
57
|
+
Calls → @rune/ui (L4): design system tokens, palette, typography for mobile UI consistency
|
|
58
|
+
Calls → @rune/backend (L4): API patterns for mobile backend integration (auth, push server)
|
|
59
|
+
Calls → @rune/security (L4): code signing audit, API key management, certificate validation
|
|
230
60
|
Called By ← cook (L1): when mobile task detected
|
|
231
61
|
Called By ← team (L1): when porting web to mobile
|
|
232
62
|
Called By ← launch (L1): app store submission flow
|
|
63
|
+
Called By ← deploy (L2): mobile-specific deployment (EAS Build, Fastlane)
|
|
64
|
+
Inter-skill: ios-build-pipeline → app-store-prep (pipeline feeds into submission checklist)
|
|
65
|
+
Inter-skill: app-store-connect → app-store-prep (API automation completes manual checklist items)
|
|
66
|
+
Inter-skill: ios-build-pipeline → app-store-connect (upload build → attach to version → submit)
|
|
233
67
|
```
|
|
234
68
|
|
|
235
69
|
## Tech Stack Support
|
|
236
70
|
|
|
237
|
-
| Framework | State Management | Navigation | Build |
|
|
238
|
-
|
|
239
|
-
| React Native (bare) | Zustand / Redux | React Navigation v7 | Metro + Gradle/Xcode |
|
|
240
|
-
| Expo (managed) | Zustand | Expo Router v4 | EAS Build |
|
|
241
|
-
| Flutter | Riverpod / BLoC | GoRouter | Flutter CLI |
|
|
71
|
+
| Framework | State Management | Navigation | Build | OTA |
|
|
72
|
+
|-----------|-----------------|------------|-------|-----|
|
|
73
|
+
| React Native (bare) | Zustand / Redux | React Navigation v7 | Metro + Gradle/Xcode | CodePush |
|
|
74
|
+
| Expo (managed) | Zustand | Expo Router v4 | EAS Build | EAS Update |
|
|
75
|
+
| Flutter | Riverpod / BLoC | GoRouter | Flutter CLI | Shorebird |
|
|
76
|
+
| Native iOS (Swift) | SwiftUI @Observable | NavigationStack | xcodebuild | — |
|
|
242
77
|
|
|
243
|
-
##
|
|
78
|
+
## Sharp Edges
|
|
244
79
|
|
|
245
|
-
|
|
246
|
-
2. MUST NOT ship with debug configurations (Flipper, dev menu, debug signing) in production builds.
|
|
247
|
-
3. MUST include usage descriptions for every permission requested — empty descriptions cause instant rejection.
|
|
248
|
-
4. MUST use platform-adaptive components (Material on Android, Cupertino on iOS) or declare a unified design system.
|
|
249
|
-
5. MUST handle offline gracefully — mobile apps lose connectivity; show cached data or clear offline state.
|
|
80
|
+
Critical failures to know before using this pack:
|
|
250
81
|
|
|
251
|
-
|
|
82
|
+
- **New Architecture** silently breaks legacy `NativeModules.X` and `setNativeProps` — audit all native deps against `reactnative.directory` before upgrading
|
|
83
|
+
- **OTA bytecode mismatch** crashes on launch — never deploy OTA update across React Native version boundaries; use `fingerprintExperimental` runtime version
|
|
84
|
+
- **Universal Links** silently break when AASA endpoint redirects (HTTP→HTTPS) — serve at exact path, verify with `curl -I`
|
|
85
|
+
- **Firebase Dynamic Links** shut down August 2025 — all `page.link` URLs dead; migrate to Branch.io or standard App Links
|
|
86
|
+
- **PrivacyInfo.xcprivacy** absence triggers auto-rejection on App Store (mandatory since April 2025)
|
|
87
|
+
- **FCM Legacy API** fully shut down June 2024 — must use FCM v1 with service account JSON
|
|
88
|
+
- **OpenSSL 3.x** `.p12` export silently fails without `-legacy` flag on macOS 14+
|
|
89
|
+
- **ASC API rate limit**: 200 req/min; JWT expires in 20 min — implement auto-refresh and exponential backoff
|
|
252
90
|
|
|
253
|
-
|
|
254
|
-
|---|---|---|
|
|
255
|
-
| FlatList optimization causes blank cells on fast scroll (windowSize too small) | HIGH | Default windowSize=5, test with 1000+ items before declaring optimized |
|
|
256
|
-
| Native bridge type mismatch crashes app instead of returning error | CRITICAL | Always wrap native calls in try-catch; validate types before bridge call |
|
|
257
|
-
| Expo managed workflow limits native module access | MEDIUM | Detect Expo vs bare in Step 1; suggest `expo prebuild` for native module needs |
|
|
258
|
-
| App store rejection for missing privacy declaration | HIGH | Cross-reference every permission in Info.plist/AndroidManifest with usage description |
|
|
259
|
-
| Flutter hot reload masks state bugs that appear in release mode | MEDIUM | Test with `flutter run --release` before declaring state management correct |
|
|
260
|
-
| Screenshots generated at wrong device size fail store review | LOW | Use exact pixel dimensions from current App Store/Play Store requirements |
|
|
91
|
+
Full sharp edges table: see individual skill files.
|
|
261
92
|
|
|
262
93
|
## Done When
|
|
263
94
|
|
|
264
|
-
- React Native/Flutter codebase audited for
|
|
265
|
-
-
|
|
266
|
-
-
|
|
267
|
-
-
|
|
268
|
-
-
|
|
269
|
-
-
|
|
95
|
+
- React Native/Flutter codebase audited for New Architecture compatibility with migration plan
|
|
96
|
+
- Deep links working on both platforms with authentication integration and real device verification
|
|
97
|
+
- Push notifications delivering reliably via FCM v1 with proper permission handling
|
|
98
|
+
- OTA update strategy configured with runtime version management and rollback procedure
|
|
99
|
+
- App store metadata generated with correct dimensions, privacy manifest, and platform-specific requirements
|
|
100
|
+
- Native bridges typed and error-handled for both platforms using modern APIs
|
|
101
|
+
- iOS build pipeline producing signed IPA with idempotent signing state
|
|
102
|
+
- App Store Connect operations automated — version, localization, screenshots, IAP, submission
|
|
270
103
|
|
|
271
104
|
## Cost Profile
|
|
272
105
|
|
|
273
|
-
~
|
|
106
|
+
~16,000–32,000 tokens per full pack run (all 9 skills). Individual skill: ~2,000–5,000 tokens. Sonnet default. Use haiku for config detection; escalate to sonnet for code generation, build pipeline, and ASC API patterns.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "app-store-connect"
|
|
3
|
+
pack: "@rune/mobile"
|
|
4
|
+
description: "App Store Connect API automation — version management, localized store listings, screenshot upload, IAP/subscription creation, review submission, customer review monitoring."
|
|
5
|
+
model: sonnet
|
|
6
|
+
tools: [Read, Edit, Write, Grep, Glob, Bash]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# app-store-connect
|
|
10
|
+
|
|
11
|
+
App Store Connect API automation — version management, localized store listings, screenshot upload, IAP/subscription creation, review submission, customer review monitoring.
|
|
12
|
+
|
|
13
|
+
#### Workflow
|
|
14
|
+
|
|
15
|
+
**Step 1 — Authenticate with ASC API**
|
|
16
|
+
App Store Connect uses JWT (ES256) with 20-minute expiry:
|
|
17
|
+
```typescript
|
|
18
|
+
import jwt from 'jsonwebtoken';
|
|
19
|
+
import fs from 'fs';
|
|
20
|
+
|
|
21
|
+
function generateASCToken(keyId: string, issuerId: string, privateKeyPath: string): string {
|
|
22
|
+
const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
|
|
23
|
+
return jwt.sign({}, privateKey, {
|
|
24
|
+
algorithm: 'ES256',
|
|
25
|
+
expiresIn: '20m',
|
|
26
|
+
issuer: issuerId,
|
|
27
|
+
header: {
|
|
28
|
+
alg: 'ES256',
|
|
29
|
+
kid: keyId,
|
|
30
|
+
typ: 'JWT',
|
|
31
|
+
},
|
|
32
|
+
audience: 'appstoreconnect-v1',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
Sharp edge: Token expires in 20 min — must auto-refresh when within 60s of expiry. Rate limit: 200 requests/minute, 429 response requires exponential backoff.
|
|
37
|
+
|
|
38
|
+
**Step 2 — Version management**
|
|
39
|
+
Create new App Store version:
|
|
40
|
+
```
|
|
41
|
+
POST /v1/appStoreVersions
|
|
42
|
+
{
|
|
43
|
+
"data": {
|
|
44
|
+
"type": "appStoreVersions",
|
|
45
|
+
"attributes": {
|
|
46
|
+
"platform": "IOS",
|
|
47
|
+
"versionString": "1.2.0"
|
|
48
|
+
},
|
|
49
|
+
"relationships": {
|
|
50
|
+
"app": { "data": { "type": "apps", "id": "<app-id>" } }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
- Only ONE editable version allowed per platform at a time
|
|
56
|
+
- Cannot create version if existing version is "Pending Developer Release"
|
|
57
|
+
- Version string must be higher than current live version (semver)
|
|
58
|
+
|
|
59
|
+
**Step 3 — Localized store listing**
|
|
60
|
+
For each locale (`en-US`, `ja`, `de-DE`, etc.):
|
|
61
|
+
- `description` (4000 chars max)
|
|
62
|
+
- `keywords` (100 chars max, comma-separated)
|
|
63
|
+
- `whatsNew` (4000 chars, release notes)
|
|
64
|
+
- `promotionalText` (170 chars, can be updated without new version)
|
|
65
|
+
|
|
66
|
+
**Step 4 — Screenshot upload (chunked reservation)**
|
|
67
|
+
ASC uses a 3-step upload process:
|
|
68
|
+
1. Reserve upload: `POST /v1/appScreenshots` with `fileName`, `fileSize` → get `uploadOperations` array
|
|
69
|
+
2. Upload chunks: PUT each chunk to the returned URLs with correct `Content-Length` and offset headers
|
|
70
|
+
3. Commit: `PATCH /v1/appScreenshots/{id}` with `uploaded: true` and SHA-256 `sourceFileChecksum`
|
|
71
|
+
|
|
72
|
+
Sharp edges:
|
|
73
|
+
- Chunk size dictated by API response, NOT configurable client-side
|
|
74
|
+
- Must send ALL chunks before commit or upload silently fails
|
|
75
|
+
- Screenshot dimensions must EXACTLY match device class (e.g., 1320×2868 for 6.9")
|
|
76
|
+
- Maximum 10 screenshots per locale per device class
|
|
77
|
+
|
|
78
|
+
**Step 5 — In-App Purchase & subscription management**
|
|
79
|
+
Create IAP:
|
|
80
|
+
```
|
|
81
|
+
POST /v1/inAppPurchases
|
|
82
|
+
{ "type": "inAppPurchases", "attributes": { "name": "Pro Upgrade", "productId": "com.example.pro", "inAppPurchaseType": "NON_CONSUMABLE" } }
|
|
83
|
+
```
|
|
84
|
+
For subscriptions: create subscription group first, then subscription within group, then set pricing per territory. Territory pricing requires concurrent requests with retry — ASC rate limits per-territory pricing endpoints aggressively.
|
|
85
|
+
|
|
86
|
+
**Step 6 — Submission readiness check**
|
|
87
|
+
Before submitting for review, verify completeness:
|
|
88
|
+
- [ ] App Store version exists with build attached
|
|
89
|
+
- [ ] All required locales have description, keywords, screenshots
|
|
90
|
+
- [ ] Screenshots uploaded for ALL required device classes (6.9", 6.7", 6.5")
|
|
91
|
+
- [ ] Age rating questionnaire completed
|
|
92
|
+
- [ ] App review contact info set (first name, last name, phone, email)
|
|
93
|
+
- [ ] Privacy policy URL set
|
|
94
|
+
- [ ] Export compliance answered
|
|
95
|
+
- [ ] Content rights declaration completed (if app has third-party content)
|
|
96
|
+
|
|
97
|
+
**Step 7 — Submit and monitor**
|
|
98
|
+
```
|
|
99
|
+
POST /v1/appStoreVersionSubmissions
|
|
100
|
+
{ "data": { "relationships": { "appStoreVersion": { "data": { "type": "appStoreVersions", "id": "<version-id>" } } } } }
|
|
101
|
+
```
|
|
102
|
+
Poll `GET /v1/appStoreVersions/{id}` for `appStoreState` transitions: `WAITING_FOR_REVIEW` → `IN_REVIEW` → `READY_FOR_SALE` (or `REJECTED`). On rejection: fetch `appStoreVersionSubmissions` for reviewer notes.
|
|
103
|
+
|
|
104
|
+
#### Example
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Complete ASC API client pattern
|
|
108
|
+
interface ASCClient {
|
|
109
|
+
// Auth
|
|
110
|
+
refreshToken(): string;
|
|
111
|
+
|
|
112
|
+
// Apps
|
|
113
|
+
listApps(): Promise<ASCApp[]>;
|
|
114
|
+
getApp(id: string): Promise<ASCApp>;
|
|
115
|
+
|
|
116
|
+
// Versions
|
|
117
|
+
createVersion(appId: string, version: string): Promise<ASCVersion>;
|
|
118
|
+
attachBuild(versionId: string, buildId: string): Promise<void>;
|
|
119
|
+
|
|
120
|
+
// Localization
|
|
121
|
+
updateLocalization(versionId: string, locale: string, data: LocalizationData): Promise<void>;
|
|
122
|
+
|
|
123
|
+
// Screenshots (3-step)
|
|
124
|
+
reserveScreenshot(setId: string, fileName: string, fileSize: number): Promise<UploadOps>;
|
|
125
|
+
uploadChunks(ops: UploadOps, fileBuffer: Buffer): Promise<void>;
|
|
126
|
+
commitScreenshot(screenshotId: string, checksum: string): Promise<void>;
|
|
127
|
+
|
|
128
|
+
// IAP
|
|
129
|
+
createIAP(appId: string, name: string, productId: string, type: IAPType): Promise<ASCIAP>;
|
|
130
|
+
|
|
131
|
+
// Submission
|
|
132
|
+
checkReadiness(versionId: string): Promise<ReadinessReport>;
|
|
133
|
+
submitForReview(versionId: string): Promise<void>;
|
|
134
|
+
pollReviewStatus(versionId: string, intervalMs?: number): AsyncGenerator<ReviewStatus>;
|
|
135
|
+
|
|
136
|
+
// Reviews
|
|
137
|
+
listCustomerReviews(appId: string): Promise<CustomerReview[]>;
|
|
138
|
+
respondToReview(reviewId: string, body: string): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Pagination helper — ASC uses cursor-based pagination via `next` links
|
|
142
|
+
async function* paginate<T>(client: ASCClient, url: string): AsyncGenerator<T> {
|
|
143
|
+
let nextUrl: string | null = url;
|
|
144
|
+
while (nextUrl) {
|
|
145
|
+
const response = await client.request(nextUrl);
|
|
146
|
+
for (const item of response.data) {
|
|
147
|
+
yield item as T;
|
|
148
|
+
}
|
|
149
|
+
nextUrl = response.links?.next ?? null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "app-store-prep"
|
|
3
|
+
pack: "@rune/mobile"
|
|
4
|
+
description: "App store submission preparation — screenshots, metadata, privacy manifests, review guidelines compliance, TestFlight/internal testing."
|
|
5
|
+
model: sonnet
|
|
6
|
+
tools: [Read, Edit, Write, Grep, Glob, Bash]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# app-store-prep
|
|
10
|
+
|
|
11
|
+
App store submission preparation — screenshots, metadata, privacy manifests, review guidelines compliance, TestFlight/internal testing.
|
|
12
|
+
|
|
13
|
+
#### Workflow
|
|
14
|
+
|
|
15
|
+
**Step 1 — Audit submission readiness**
|
|
16
|
+
Check for:
|
|
17
|
+
- App icon: 1024x1024 for iOS (no alpha, no rounded corners), adaptive icon for Android
|
|
18
|
+
- Splash screen configured
|
|
19
|
+
- Privacy policy URL in app config
|
|
20
|
+
- Required permissions with specific (not generic) usage descriptions — "App requires access to your camera" gets rejected. Must be specific: "Used to scan QR codes for quick login"
|
|
21
|
+
- `PrivacyInfo.xcprivacy` present (mandatory since April 2025): Apple requires privacy manifest for apps using file timestamp, boot time, disk space, or UserDefaults APIs. React Native core and many libraries access these APIs. Missing manifest = auto-rejection
|
|
22
|
+
- Minimum SDK versions: iOS 18 SDK mandatory (Xcode 16+, April 2025), Android API 34 minimum
|
|
23
|
+
- Release signing configured (not debug)
|
|
24
|
+
- `.aab` format for Google Play (APK no longer accepted for new apps)
|
|
25
|
+
|
|
26
|
+
**Step 2 — Generate metadata**
|
|
27
|
+
From README and app config, generate: app title (30 chars max), subtitle (30 chars), description (4000 chars), keywords (100 chars), category selection, age rating questionnaire answers, and screenshot specifications per device size.
|
|
28
|
+
|
|
29
|
+
Current required screenshot sizes:
|
|
30
|
+
- iPhone 6.9" (1320×2868) — iPhone 16 Pro Max (NEW, required for new apps)
|
|
31
|
+
- iPhone 6.7" (1290×2796)
|
|
32
|
+
- iPhone 6.5" (1242×2688)
|
|
33
|
+
- iPad 12.9" (2048×2732) — if app supports iPad
|
|
34
|
+
- Android: feature graphic 1024×500
|
|
35
|
+
|
|
36
|
+
**Step 3 — Emit submission checklist**
|
|
37
|
+
Output structured checklist covering both platforms with platform-specific gotchas.
|
|
38
|
+
|
|
39
|
+
#### Example
|
|
40
|
+
|
|
41
|
+
```markdown
|
|
42
|
+
## App Store Submission Checklist
|
|
43
|
+
|
|
44
|
+
### iOS (Apple App Store Connect)
|
|
45
|
+
- [ ] App icon: 1024x1024 PNG, no alpha, no rounded corners
|
|
46
|
+
- [ ] Screenshots: 6.9" (1320x2868), 6.7" (1290x2796), 6.5" (1242x2688)
|
|
47
|
+
- [ ] Privacy policy URL: https://example.com/privacy
|
|
48
|
+
- [ ] `PrivacyInfo.xcprivacy` included (MANDATORY since April 2025)
|
|
49
|
+
- [ ] NSCameraUsageDescription: "Used to scan QR codes for quick login" (SPECIFIC, not generic)
|
|
50
|
+
- [ ] NSLocationWhenInUseUsageDescription: "Used to show nearby stores on the map"
|
|
51
|
+
- [ ] TestFlight build uploaded and tested on physical device
|
|
52
|
+
- [ ] Export compliance: Uses HTTPS only (no custom encryption) → select "No"
|
|
53
|
+
- [ ] Built with Xcode 16+ / iOS 18 SDK (mandatory since April 2025)
|
|
54
|
+
|
|
55
|
+
### Android (Google Play Console)
|
|
56
|
+
- [ ] Adaptive icon: foreground (108dp) + background layer
|
|
57
|
+
- [ ] Feature graphic: 1024x500 PNG
|
|
58
|
+
- [ ] App Bundle format (.aab, NOT .apk)
|
|
59
|
+
- [ ] Target API 34+ (Android 14)
|
|
60
|
+
- [ ] 64-bit native libraries included (32-bit only = rejection)
|
|
61
|
+
- [ ] Data safety form: accurately declare ALL collected data (analytics SDKs collect device IDs)
|
|
62
|
+
- [ ] `SCHEDULE_EXACT_ALARM` justified if using scheduled notifications
|
|
63
|
+
- [ ] Content rating: IARC questionnaire completed
|
|
64
|
+
- [ ] Internal testing track: at least 1 build tested
|
|
65
|
+
- [ ] Signing: upload key + app signing by Google Play enabled
|
|
66
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "deep-linking"
|
|
3
|
+
pack: "@rune/mobile"
|
|
4
|
+
description: "Deep linking setup and debugging — Universal Links (iOS), App Links (Android), deferred deep links, authentication + deep link integration."
|
|
5
|
+
model: sonnet
|
|
6
|
+
tools: [Read, Edit, Write, Grep, Glob, Bash]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# deep-linking
|
|
10
|
+
|
|
11
|
+
Deep linking setup and debugging — Universal Links (iOS), App Links (Android), deferred deep links, authentication + deep link integration.
|
|
12
|
+
|
|
13
|
+
#### Workflow
|
|
14
|
+
|
|
15
|
+
**Step 1 — Detect current deep link setup**
|
|
16
|
+
Use Grep to find: `expo-linking`, `Linking.addEventListener`, `useURL`, `expo-router` deep link config, `apple-app-site-association`, `assetlinks.json`, `IntentFilter` in `AndroidManifest.xml`. Check for React Navigation `linking` config or Expo Router file-based deep link handling.
|
|
17
|
+
|
|
18
|
+
**Step 2 — Audit deep link reliability**
|
|
19
|
+
Check for these common failure modes:
|
|
20
|
+
- **AASA file redirect**: `.well-known/apple-app-site-association` endpoint must not redirect (HTTP→HTTPS or www→non-www). Any redirect silently breaks Universal Links
|
|
21
|
+
- **AASA caching**: Apple CDN caches AASA aggressively (up to 24h). Changes appear correct on server but old version is served to devices
|
|
22
|
+
- **SHA-256 mismatch**: Dev/Preview builds use different signing key than Production. `assetlinks.json` must include ALL certificates (upload key + app signing key)
|
|
23
|
+
- **Multiple environments**: Staging and production need separate AASA entries with different bundle IDs and team IDs
|
|
24
|
+
- **Firebase Dynamic Links dead**: Shut down August 25, 2025. All `page.link` subdomains stopped working. Must migrate to Branch.io, custom server, or standard App Links/Universal Links
|
|
25
|
+
- **Simulator limitation**: Universal Links and App Links do not work on simulators. Must test on real physical devices
|
|
26
|
+
|
|
27
|
+
**Step 3 — Audit authentication + deep link integration**
|
|
28
|
+
Check for race condition: deep link arrives before auth state resolves. Pattern: capture initial URL, wait for auth, then navigate. In React Navigation v7, `NAVIGATE` action pushes new screen even for existing routes — deep link handler must check current route before navigating.
|
|
29
|
+
|
|
30
|
+
**Step 4 — Emit deep link configuration**
|
|
31
|
+
For Expo Router: verify file-based route structure matches expected deep link paths. For React Navigation v7: emit typed `linking` config with authentication gate. For server: emit AASA and `assetlinks.json` with correct team ID, bundle ID, and all signing certificates.
|
|
32
|
+
|
|
33
|
+
#### Example
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// Expo Router — app/_layout.tsx with auth-gated deep link handling
|
|
37
|
+
import { useURL } from 'expo-linking';
|
|
38
|
+
import { useRouter, useSegments } from 'expo-router';
|
|
39
|
+
|
|
40
|
+
export default function RootLayout() {
|
|
41
|
+
const url = useURL();
|
|
42
|
+
const router = useRouter();
|
|
43
|
+
const segments = useSegments();
|
|
44
|
+
const { user, isLoading } = useAuth();
|
|
45
|
+
const pendingDeepLink = useRef<string | null>(null);
|
|
46
|
+
|
|
47
|
+
// Capture deep link before auth resolves
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (url && isLoading) {
|
|
50
|
+
pendingDeepLink.current = url;
|
|
51
|
+
}
|
|
52
|
+
}, [url, isLoading]);
|
|
53
|
+
|
|
54
|
+
// Navigate after auth resolves
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (isLoading) return;
|
|
57
|
+
|
|
58
|
+
const inAuthGroup = segments[0] === '(auth)';
|
|
59
|
+
if (!user && !inAuthGroup) {
|
|
60
|
+
router.replace('/login');
|
|
61
|
+
} else if (user && inAuthGroup) {
|
|
62
|
+
// Process pending deep link or go to home
|
|
63
|
+
if (pendingDeepLink.current) {
|
|
64
|
+
const path = new URL(pendingDeepLink.current).pathname;
|
|
65
|
+
pendingDeepLink.current = null;
|
|
66
|
+
router.replace(path);
|
|
67
|
+
} else {
|
|
68
|
+
router.replace('/');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}, [user, isLoading, segments]);
|
|
72
|
+
|
|
73
|
+
return <Slot />;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
// .well-known/apple-app-site-association — NO redirects on this endpoint
|
|
79
|
+
{
|
|
80
|
+
"applinks": {
|
|
81
|
+
"details": [
|
|
82
|
+
{
|
|
83
|
+
"appIDs": ["TEAMID.com.example.app", "TEAMID.com.example.app.staging"],
|
|
84
|
+
"components": [
|
|
85
|
+
{ "/": "/product/*", "comment": "Product deep links" },
|
|
86
|
+
{ "/": "/invite/*", "comment": "Invite deep links" }
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
// .well-known/assetlinks.json — include BOTH upload key AND app signing key
|
|
96
|
+
[
|
|
97
|
+
{
|
|
98
|
+
"relation": ["delegate_permission/common.handle_all_urls"],
|
|
99
|
+
"target": {
|
|
100
|
+
"namespace": "android_app",
|
|
101
|
+
"package_name": "com.example.app",
|
|
102
|
+
"sha256_cert_fingerprints": [
|
|
103
|
+
"AA:BB:CC:...:upload_key_fingerprint",
|
|
104
|
+
"DD:EE:FF:...:app_signing_key_fingerprint"
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
```
|