A Complete Developer Guide
App Bundle ID:
com.example.demoapp
App Group:group.com.example.demoapp
Targets:Runner(iPhone) ·DemoWatch Watch App·DemoMessages MessagesExtension
Table of Contents
- Architecture Overview
- Dependencies Added
- Xcode Setup (One-Time)
- Apple Watch Integration
- iMessage Integration
- Data Flow Diagrams
- Publishing to the App Store
- Build & Archive Checklist
- Troubleshooting & Challenges Experienced
1. Architecture Overview
Flutter cannot run its UI engine on Apple Watch or inside iMessage extensions. Instead, we use two communication bridges:
| Extension | Bridge Technology | Direction |
|---|---|---|
| Apple Watch |
WatchConnectivity framework via watch_connectivity Flutter package |
iPhone ↔ Watch (two-way, real-time) |
| iMessage |
App Groups shared UserDefaults container via shared_preference_app_group package |
Flutter → iMessage (one-way, cached) |
┌─────────────────────┐ WatchConnectivity ┌─────────────────────┐
│ Flutter App │ ◄──────────────────────────────► │ DemoWatch App │
│ (iPhone) │ │ (Apple Watch) │
└─────────────────────┘ └─────────────────────┘
│
│ App Groups (UserDefaults)
│ group.com.example.demoapp
▼
┌─────────────────────┐
│ iMessage Extension │
│ DemoMessages │
└─────────────────────┘
2. Dependencies Added
The following packages were added to pubspec.yaml:
dependencies:
watch_connectivity: ^0.2.8 # Apple Watch two-way communication
shared_preference_app_group: ^2.1.0 # Shared container for iMessage
Install with:
flutter pub get
3. Xcode Setup (One-Time)
3.1 App Groups Capability
App Groups allow all three targets to share a single data container (group.com.example.demoapp).
You must add this capability to each target in Xcode:
- Open
ios/Runner.xcworkspacein Xcode. - In the Project Navigator (left panel), select Runner target.
- Go to Signing & Capabilities tab.
- Click + Capability → App Groups.
- Add:
group.com.example.demoapp - Repeat steps 2–5 for DemoWatch Watch App target.
- Repeat steps 2–5 for DemoMessages MessagesExtension target.
✅ Confirmation: Check the entitlements file at
ios/DemoWatch Watch App/DemoWatch Watch App.entitlements— it should contain:<key>com.apple.security.application-groups</key> <array> <string>group.com.example.demoapp</string> </array>
3.2 Complication Data Source (Apple Watch only)
To enable watch face complications:
- Select the DemoWatch Watch App target in Xcode.
- Go to the Info tab.
- Add a new row with key:
CLKComplicationDataSource - Set value to:
ComplicationController
3.3 Watch Target Platform Settings
The Watch target must resolve as a watchOS target during archive. If Xcode resolves it as iphoneos, the Watch asset catalog will be compiled as an iOS asset catalog and the archive will fail with:
Assets.xcassets: The stickers icon set, app icon set, or icon stack named "AppIcon" did not have any applicable content.
For the DemoWatch Watch App target, confirm these build settings in Debug, Release, and Profile:
| Setting | Required value |
|---|---|
SDKROOT |
watchos |
SUPPORTED_PLATFORMS |
watchos watchsimulator |
TARGETED_DEVICE_FAMILY |
4 |
ASSETCATALOG_COMPILER_APPICON_NAME |
AppIcon |
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME |
AccentColor |
The current project pins these in ios/Runner.xcodeproj/project.pbxproj. This matters because the project-level Flutter/iOS configuration sets SDKROOT = iphoneos and SUPPORTED_PLATFORMS = iphoneos; the Watch target must explicitly override that inherited iOS platform.
You can verify the resolved target settings with:
xcodebuild -showBuildSettings \
-project ios/Runner.xcodeproj \
-scheme "DemoWatch Watch App" \
-configuration Release \
-destination "generic/platform=watchOS" \
-derivedDataPath /tmp/travler-derived-data \
| rg "PLATFORM_NAME|SDKROOT|SUPPORTED_PLATFORMS|TARGETED_DEVICE_FAMILY|BUILT_PRODUCTS_DIR"
Expected output should include:
BUILT_PRODUCTS_DIR = .../Release-watchos
PLATFORM_NAME = watchos
SDKROOT = .../WatchOS.platform/.../WatchOS*.sdk
SUPPORTED_PLATFORMS = watchos watchsimulator
TARGETED_DEVICE_FAMILY = 4
3.4 Watch AppIcon Asset Catalog
The Watch app icon set lives at:
ios/DemoWatch Watch App/Assets.xcassets/AppIcon.appiconset
Contents.json must declare watch-specific icon slots, not only the 1024 marketing icon. The catalog should include these families:
| Role | Sizes currently covered |
|---|---|
notificationCenter |
24x24, 27.5x27.5, 33x33 |
companionSettings |
29x29 at 2x and 3x |
appLauncher |
40x40, 44x44, 46x46, 50x50, 51x51, 54x54 |
quickLook |
86x86, 98x98, 108x108, 117x117, 129x129 |
watch-marketing |
1024x1024 |
If new icon artwork is generated, preserve the filenames declared in Contents.json or update the JSON and PNGs together. A missing filename, wrong idiom, or iOS-only icon set will break archive.
4. Apple Watch Integration
4.1 Flutter Side — WatchService
File: lib/services/watch_service.dart
This service is the bridge between your Flutter app and the Apple Watch.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:watch_connectivity/watch_connectivity.dart';
class WatchService {
final WatchConnectivity _watch = WatchConnectivity();
StreamSubscription<Map<String, dynamic>>? _messageSubscription;
// All raw messages from the Watch
final _messageController = StreamController<Map<String, dynamic>>.broadcast();
Stream<Map<String, dynamic>> get messageStream => _messageController.stream;
// Only action commands from the Watch (e.g., quick_book, cancel_booking)
final _actionController = StreamController<Map<String, dynamic>>.broadcast();
Stream<Map<String, dynamic>> get actionStream => _actionController.stream;
WatchService() {
_messageSubscription = _watch.messageStream.listen((message) {
_messageController.add(message);
if (message.containsKey('action')) {
_actionController.add(message);
}
});
}
Future<bool> isSupported() async => await _watch.isSupported;
Future<bool> isReachable() async => await _watch.isReachable;
void dispose() {
_messageSubscription?.cancel();
_messageController.close();
_actionController.close();
}
}
4.2 Syncing Data to the Watch
Call syncTravelData() whenever the user's data changes — after login, booking, or wallet top-up. The data is sent using Application Context, which means the Watch receives the latest state even if it was not connected at the time.
final watchService = WatchService();
await watchService.syncTravelData(
walletBalance: 125.50,
walletCurrency: 'Ksh',
nextTripDestination: 'Nairobi → Mombasa',
nextTripDate: 'Aug 12, 2026',
nextTripTime: '08:30 AM',
nextTripSeat: '14B',
qrCodeData: 'TICKET-XYZ-12345', // the raw ticket/QR string
activeBookingsCount: 2,
unreadNotifications: 3,
);
Best places to call this:
- After a successful booking API response
- After the user tops up their wallet
- In your home screen
initState()when the app opens
4.3 Listening for Watch Actions in Flutter
When the user presses Quick Book or Cancel on the Apple Watch, the Watch sends a message to Flutter via actionStream. Listen to this in your BLoC, provider, or main widget tree:
final watchService = WatchService();
watchService.actionStream.listen((actionData) {
final action = actionData['action'] as String?;
switch (action) {
case 'quick_book':
// Trigger your booking flow / open the booking screen
NavigationKeys.mainNav.currentState?.pushNamed('/booking');
break;
case 'cancel_booking':
final destination = actionData['destination'] as String?;
// Call your cancel API
context.read<BookingBloc>().add(CancelBookingEvent(destination: destination));
break;
}
});
4.4 Native Watch UI (SwiftUI)
File: ios/DemoWatch Watch App/ContentView.swift
The Watch app has a 4-tab swipeable interface. Each tab is a SwiftUI view powered by WatchViewModel:
| Tab | View | Content |
|---|---|---|
| 1 | TripDashboardView |
Next trip destination, date, time, seat + Quick Book & Cancel buttons |
| 2 | TicketQRView |
Auto-generated QR code scannable at the boarding point |
| 3 | WalletView |
Wallet balance + active bookings count |
| 4 | SupportNotificationsView |
Call Support button + unread notification count |
File: ios/DemoWatch Watch App/WatchViewModel.swift
The WatchViewModel is the native Swift equivalent of WatchService. It implements WCSessionDelegate to receive data from Flutter and manages all published state for the SwiftUI views.
// How the Watch receives data from Flutter:
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
DispatchQueue.main.async {
self.parseData(applicationContext)
}
}
// How the Watch sends actions back to Flutter:
func sendActionToFlutter(action: String, payload: [String: Any] = [:]) {
var message = payload
message["action"] = action
if session.isReachable {
session.sendMessage(message, replyHandler: nil, errorHandler: nil)
}
}
4.5 Watch Face Complications
File: ios/DemoWatch Watch App/ComplicationController.swift
Complications display DemoTravel info right on the Apple Watch clock face — without the user needing to open the app.
Supported complication families:
| Family | Watch Face | What's Shown |
|---|---|---|
graphicCorner |
Infograph |
🚌 Mombasa + departure time |
graphicCircular |
Infograph Modular | Bus icon + time |
graphicBezel |
Infograph | Circle with full destination curved around it |
modularLarge |
Modular | 3-row table: Trip / Time / Seat |
utilitarianLarge |
Utility | 🚌 Nairobi → Mombasa · 08:30 |
utilitarianSmall |
Utility | 🚌 08:30 |
modularSmall |
Modular |
Bus + time |
circularSmall |
Color / Chronograph |
Bus + time |
How complications stay up to date:
When WatchViewModel.parseData() receives new data from Flutter, it automatically:
- Writes the data to
UserDefaults(suiteName: "group.com.example.demoapp") - Calls
CLKComplicationServer.sharedInstance().reloadTimeline(for:)on all active complications
This means the watch face updates instantly every time you call syncTravelData() from Flutter.
The ComplicationController reads from UserDefaults:
let defaults = UserDefaults(suiteName: "group.com.example.demoapp")
let destination = defaults?.string(forKey: "nextTripDestination") ?? "No Trip"
let time = defaults?.string(forKey: "nextTripTime") ?? "--"
let seat = defaults?.string(forKey: "nextTripSeat") ?? "--"
5. iMessage Integration
5.1 Flutter Side — IMessageSyncService
File: lib/services/imessage_sync_service.dart
This service writes trip and wallet data into the shared App Group container so the iMessage extension can read them in Swift.
import 'dart:convert';
import 'package:shared_preference_app_group/shared_preference_app_group.dart';
const String _kAppGroup = 'group.com.example.demoapp';
class DemoTrip {
final String id;
final String destination;
final String origin;
final String date;
final String time;
final String seat;
final String qrCodeData;
DemoTrip({
required this.id,
required this.destination,
required this.origin,
required this.date,
required this.time,
required this.seat,
required this.qrCodeData,
});
Map<String, dynamic> toJson() => {
'id': id,
'destination': destination,
'origin': origin,
'date': date,
'time': time,
'seat': seat,
'qrCodeData': qrCodeData,
};
}
class IMessageSyncService {
static const String _tripsKey = 'demo_imessage_trips';
static const String _walletKey = 'demo_imessage_wallet';
// Call once at app startup (e.g. in main.dart)
Future<void> init() async {
await SharedPreferenceAppGroup.setAppGroup(_kAppGroup);
}
Future<void> syncAll({
required List<DemoTrip> trips,
required double walletBalance,
required String walletCurrency,
}) async {
await syncTrips(trips);
await syncWalletBalance(walletBalance, walletCurrency);
}
Future<void> syncTrips(List<DemoTrip> trips) async {
final encoded = jsonEncode(trips.map((t) => t.toJson()).toList());
await SharedPreferenceAppGroup.setString(_tripsKey, encoded);
}
Future<void> syncWalletBalance(double balance, String currency) async {
await SharedPreferenceAppGroup.setString(
_walletKey,
jsonEncode({'balance': balance, 'currency': currency}),
);
}
}
5.2 Syncing Trip Data for iMessage
Step 1 — Initialise once at app startup (e.g., in main.dart before runApp):
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// ... existing initialisation ...
// Initialise iMessage sync
final iMessageSync = IMessageSyncService();
await iMessageSync.init();
runApp(const MyApp());
}
Step 2 — Sync whenever data changes:
final iMessageSync = IMessageSyncService();
await iMessageSync.syncAll(
walletBalance: 125.50,
walletCurrency: 'Ksh',
trips: [
DemoTrip(
id: 'booking-001',
origin: 'Nairobi',
destination: 'Mombasa',
date: 'Aug 12, 2026',
time: '08:30 AM',
seat: '14B',
qrCodeData: 'TICKET-XYZ-12345',
),
DemoTrip(
id: 'booking-002',
origin: 'Mombasa',
destination: 'Nairobi',
date: 'Aug 15, 2026',
time: '02:00 PM',
seat: '7A',
qrCodeData: 'TICKET-ABC-67890',
),
],
);
5.3 Native iMessage UI (UIKit/Swift)
File: ios/DemoMessages MessagesExtension/MessagesViewController.swift
The iMessage extension is built with UIKit. It:
- Reads trip and wallet data from
UserDefaults(suiteName: "group.com.example.demoapp") - Displays a scrollable list of upcoming trips
- When a trip is tapped, inserts a rich
MSMessagebubble into the chat
Reading from the shared container in Swift:
let defaults = UserDefaults(suiteName: "group.com.example.demoapp")
// Read trips
if let tripsString = defaults?.string(forKey: "demo_imessage_trips"),
let data = tripsString.data(using: .utf8),
let rawList = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]] {
trips = rawList.compactMap { DemoTrip(dict: $0) }
}
// Read wallet
if let walletString = defaults?.string(forKey: "demo_imessage_wallet"),
let data = walletString.data(using: .utf8),
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
walletBalance = dict["balance"] as? Double ?? 0
walletCurrency = dict["currency"] as? String ?? "Ksh"
}
Sending a rich iMessage bubble:
func sendTrip(_ trip: DemoTrip) {
guard let conversation = currentConversation else { return }
let message = MSMessage(session: conversation.selectedMessage?.session ?? MSSession())
let layout = MSMessageTemplateLayout()
layout.caption = "🚌 \(trip.origin) → \(trip.destination)"
layout.subcaption = "\(trip.date) at \(trip.time)"
layout.trailingCaption = "Seat \(trip.seat)"
layout.trailingSubcaption = "Trip Booking"
layout.image = generateQRImage(from: trip.qrCodeData) // QR code
message.layout = layout
message.url = URL(string: "https://example.com")!
conversation.insert(message, completionHandler: nil)
}
Important: do not name your stored conversation property activeConversation. MSMessagesAppViewController already exposes an inherited activeConversation property. Use a separate name such as:
private var currentConversation: MSConversation?
override func willBecomeActive(with conversation: MSConversation) {
currentConversation = conversation
loadData()
}
What the recipient sees:
The recipient sees a custom rich bubble in their chat containing:
-
🚌 Nairobi → Mombasa(caption) -
Aug 12, 2026 at 08:30 AM(subcaption) -
Seat 14B(trailing) - QR code image embedded in the bubble
6. Data Flow Diagrams
Apple Watch — Data Flow
Flutter (Dart) Apple Watch (Swift)
───────────────── ────────────────────
WatchService.syncTravelData()
│
│ WatchConnectivity
│ updateApplicationContext()
│ ─────────────────────────► WatchViewModel.didReceiveApplicationContext()
│ │
│ ├──► Updates @Published UI state
│ ├──► Persists to UserDefaults (App Group)
│ └──► Reloads watch face complications
│
│ WCSession.sendMessage()
│ ◄───────────────────────── WatchViewModel.sendActionToFlutter()
│ (user taps Quick Book / Cancel on Watch)
│
WatchService.actionStream emits
│
└──► Your BLoC / Provider handles the action
iMessage — Data Flow
Flutter (Dart) iMessage Extension (Swift)
───────────────── ──────────────────────────
IMessageSyncService.syncAll()
│
│ UserDefaults
│ (suiteName: group.com.example.demoapp)
│ ─────────────────────────► MessagesViewController.loadData()
│ │
│ ├──► Renders trip list
│ └──► User taps trip → sends MSMessage bubble
│
│ (No return path — iMessage is one-way)
7. Publishing to the App Store
✅ Good news: The Apple Watch and iMessage extensions are bundled inside your main iOS app. You do not submit them separately. When users download DemoTravel from the App Store, the Watch app automatically installs on their paired Apple Watch.
Step-by-step publishing process:
Step 1 — Archive the app:
flutter build ipa
Or in Xcode: Product → Archive (with "Any iOS Device" selected as the destination).
Step 2 — Open Xcode Organizer:
After archiving, Xcode Organizer opens automatically. Select your archive and click Distribute App.
Step 3 — Upload to App Store Connect:
Choose App Store Connect → Upload. Follow the prompts. The Watch and iMessage extensions are included automatically.
Step 4 — Add Apple Watch Screenshots:
In App Store Connect, navigate to your app listing. You will now see a section for Apple Watch Screenshots. Take screenshots from the Watch Simulator in Xcode and upload them here. Apple requires these before the app can be reviewed.
Required Watch screenshot sizes:
- 40mm: 394 × 324 px
- 41mm: 352 × 430 px (Series 7/8/9)
- 44mm: 368 × 448 px
- 45mm: 396 × 484 px
Step 5 — Submit for Review:
Once screenshots are uploaded and everything looks good, click Submit for Review. Apple reviews the Watch and iMessage extensions as part of the same review process.
8. Build & Archive Checklist
Use this checklist before creating an App Store or TestFlight archive that includes Apple Watch and iMessage targets.
8.1 Before archiving
- Open
ios/Runner.xcworkspace, notios/Runner.xcodeproj, when using Xcode manually. - Confirm the
Runnerscheme includes dependencies on:DemoWatch Watch AppDemoMessages MessagesExtension
- Confirm
DemoWatch Watch Appresolves as watchOS:PLATFORM_NAME = watchos-
BUILT_PRODUCTS_DIRcontainsRelease-watchos SUPPORTED_PLATFORMS = watchos watchsimulator
- Confirm
DemoMessages MessagesExtensionresolves as iPhoneOS:PLATFORM_NAME = iphoneosPRODUCT_TYPE = com.apple.product-type.app-extension.messages
- Confirm all targets share the App Group:
RunnerDemoWatch Watch AppDemoMessages MessagesExtension
8.2 Useful build-only verification
This command checks the Watch and iMessage target build path without running tests or static analysis:
xcodebuild build \
-project ios/Runner.xcodeproj \
-scheme "DemoWatch Watch App" \
-configuration Release \
-destination "generic/platform=watchOS" \
-derivedDataPath /tmp/travler-derived-data \
CODE_SIGNING_ALLOWED=NO
Successful Watch asset catalog output looks like:
CompileAssetCatalogVariant ... DemoWatch Watch App/Assets.xcassets
... --app-icon AppIcon --target-device watch --platform watchos
/* com.apple.actool.compilation-results */
.../Assets.car
LinkAssetCatalog ... DemoWatch Watch App/Assets.xcassets
note: Emplaced .../DemoWatch Watch App.app/Assets.car
8.3 What not to run for this workflow
For fast archive debugging, skip:
flutter test
flutter analyze
dart analyze
The archive blockers here are Xcode target configuration, asset catalogs, and native Swift extension compilation. Unit/widget tests and Dart static analysis do not diagnose those issues.
9. Troubleshooting & Challenges Experienced
Watch data not updating?
- Make sure
syncTravelData()is being called after a booking or login. - Confirm the Watch app is installed on a paired Watch.
- On a physical device, both the iPhone and Watch must be connected.
- Check that the App Group
group.com.example.demoappis listed in both the Runner and Watch targets in Xcode.
iMessage extension shows "No upcoming trips"?
- Make sure
IMessageSyncService.init()is called at app startup before any sync calls. - Confirm
syncAll()has been called at least once. - Verify the App Group identifier
group.com.example.demoappis set on both Runner and DemoMessages targets. - Double check the key names in Swift (
demo_imessage_trips,demo_imessage_wallet) match the constants in Dart.
Watch face complications not appearing?
- Confirm
CLKComplicationDataSource = ComplicationControlleris set in the Watch target's Info.plist in Xcode. - Long-press your watch face → Edit → browse Complications for "DemoTravel".
- After calling
syncTravelData()in Flutter, the complication should update within seconds.
Build error: No such module 'CoreImage.CIFilterBuiltins'
- This module does not exist on watchOS. Use plain
import CoreImageinstead. - Use
CIFilter(name: "CIQRCodeGenerator")instead ofCIFilter.qrCodeGenerator(). - Use
Image(cgImage:, scale:, label:)in SwiftUI instead ofImage(uiImage:).
pod install fails with xcodeproj compatibility error?
- This is a known CocoaPods issue with Xcode 26+. Run
flutter runfrom the command line instead ofpod installdirectly — Flutter's tooling handles the pod installation correctly.
Archive error: AppIcon did not have any applicable content
Symptom:
/ios/DemoWatch Watch App/Assets.xcassets:
The stickers icon set, app icon set, or icon stack named "AppIcon" did not have any applicable content.
Root causes seen in this project:
- The Watch target was resolving as
iphoneos, so Xcode was trying to compile watch-only icon slots as iOS icons. - The Watch
AppIcon.appiconset/Contents.jsononly exposedwatch-marketingcontent or incomplete watch slots.
Fix:
- In
ios/Runner.xcodeproj/project.pbxproj, ensure theDemoWatch Watch Apptarget has:
SDKROOT = watchos;
SUPPORTED_PLATFORMS = "watchos watchsimulator";
TARGETED_DEVICE_FAMILY = 4;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- In
ios/DemoWatch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json, declare actualidiom: watchentries fornotificationCenter,companionSettings,appLauncher, andquickLook, pluswatch-marketing. - Confirm every filename referenced by
Contents.jsonexists and has the expected pixel dimensions.
Verification:
jq empty "ios/DemoWatch Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json"
sips -g pixelWidth -g pixelHeight "ios/DemoWatch Watch App/Assets.xcassets/AppIcon.appiconset/"*.png
Then confirm the target is not resolving as iPhoneOS:
xcodebuild -showBuildSettings \
-project ios/Runner.xcodeproj \
-scheme "DemoWatch Watch App" \
-configuration Release \
-destination "generic/platform=watchOS" \
-derivedDataPath /tmp/travler-derived-data \
| rg "PLATFORM_NAME|SDKROOT|SUPPORTED_PLATFORMS|BUILT_PRODUCTS_DIR"
Build error: No available simulator runtimes for platform watchsimulator
This can happen when actool runs in a restricted environment or Xcode cannot access simulator runtime services.
Symptoms:
No available simulator runtimes for platform watchsimulator.
SimServiceContext supportedRuntimes=[]
Fix:
- Run the build from normal Terminal or Xcode, not a restricted sandbox.
- Open Xcode once and let it finish installing components.
- Check Xcode → Settings → Platforms and install the required watchOS simulator runtime if missing.
- Use a device/generic watchOS destination for archive:
xcodebuild build \
-project ios/Runner.xcodeproj \
-scheme "DemoWatch Watch App" \
-configuration Release \
-destination "generic/platform=watchOS" \
-derivedDataPath /tmp/travler-derived-data \
CODE_SIGNING_ALLOWED=NO
Build error: activeConversation override in iMessage extension
Symptom:
overriding property must be as accessible as its enclosing type
cannot override with a stored property 'activeConversation'
ambiguous use of 'activeConversation'
Cause:
MSMessagesAppViewController already has an inherited activeConversation property. A stored property with the same name in MessagesViewController conflicts with it.
Fix:
Rename the custom property:
private var currentConversation: MSConversation?
override func willBecomeActive(with conversation: MSConversation) {
currentConversation = conversation
loadData()
}
private func sendTrip(_ trip: DemoTrip) {
guard let conversation = currentConversation else { return }
// insert MSMessage...
}
Build reaches Run Script and appears stuck
After Watch assets and iMessage compile issues are fixed, the build can spend a long time in Flutter/Xcode script phases, especially Run Script, Thin Binary, or Flutter embed steps.
What to check:
- Confirm previous failures are gone before interrupting. In the log, Watch asset success looks like
Emplaced .../DemoWatch Watch App.app/Assets.car. - Run from a normal Terminal when possible so script phases can access the full user environment.
- If the script is silent for several minutes, rerun with Xcode's report navigator open to see the exact active phase.
Documentation generated for DemoTravel v3.5.4 — Apple Watch & iMessage Integration
This sample project is provided for educational purposes.
Bundle ID: com.example.demoapp · App Group: group.com.example.demoapp
Top comments (0)