mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix: migrate watch app to single-target app (Xcode 27+ compat) (#92477)
* fix: migrate watch app to single-target app * fix: build watch screenshots generically * docs(ios): clarify watch embed invariant * docs(ios): clarify watch embed invariant --------- Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -12,7 +12,7 @@ report_include:
|
||||
- Sources/**
|
||||
- ShareExtension/**
|
||||
- ActivityWidget/**
|
||||
- WatchExtension/Sources/**
|
||||
- WatchApp/Sources/**
|
||||
build_arguments:
|
||||
- -destination
|
||||
- generic/platform=iOS Simulator
|
||||
|
||||
@@ -39,15 +39,6 @@
|
||||
"profileKey": "OPENCLAW_WATCH_APP_PROFILE",
|
||||
"profileName": "OpenClaw App Store ai.openclawfoundation.app.watchkitapp",
|
||||
"capabilities": []
|
||||
},
|
||||
{
|
||||
"target": "OpenClawWatchExtension",
|
||||
"displayName": "OpenClaw Watch Extension",
|
||||
"bundleId": "ai.openclawfoundation.app.watchkitapp.extension",
|
||||
"platform": "IOS",
|
||||
"profileKey": "OPENCLAW_WATCH_EXTENSION_PROFILE",
|
||||
"profileName": "OpenClaw App Store ai.openclawfoundation.app.watchkitapp.extension",
|
||||
"capabilities": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,11 +8,9 @@ OPENCLAW_CODE_SIGN_STYLE = Automatic
|
||||
OPENCLAW_CODE_SIGN_IDENTITY = Apple Development
|
||||
OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
|
||||
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
|
||||
OPENCLAW_WATCH_EXTENSION_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp.extension
|
||||
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
|
||||
OPENCLAW_ACTIVITY_WIDGET_PROFILE =
|
||||
OPENCLAW_WATCH_APP_PROFILE =
|
||||
OPENCLAW_WATCH_EXTENSION_PROFILE =
|
||||
|
||||
// Local contributors can override this by running scripts/ios-configure-signing.sh.
|
||||
// Keep include after defaults: xcconfig is evaluated top-to-bottom.
|
||||
|
||||
@@ -9,11 +9,9 @@ OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
|
||||
OPENCLAW_SHARE_BUNDLE_ID = ai.openclawfoundation.app.share
|
||||
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
|
||||
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
|
||||
OPENCLAW_WATCH_EXTENSION_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp.extension
|
||||
|
||||
// Leave empty with automatic signing.
|
||||
OPENCLAW_APP_PROFILE =
|
||||
OPENCLAW_SHARE_PROFILE =
|
||||
OPENCLAW_ACTIVITY_WIDGET_PROFILE =
|
||||
OPENCLAW_WATCH_APP_PROFILE =
|
||||
OPENCLAW_WATCH_EXTENSION_PROFILE =
|
||||
|
||||
@@ -155,7 +155,6 @@ This should create `apps/ios/fastlane/.env` with non-secret App Store Connect va
|
||||
- `ai.openclawfoundation.app.share`
|
||||
- `ai.openclawfoundation.app.activitywidget`
|
||||
- `ai.openclawfoundation.app.watchkitapp`
|
||||
- `ai.openclawfoundation.app.watchkitapp.extension`
|
||||
|
||||
Use `pnpm ios:release:signing:setup` for the initial portal setup, then `MATCH_PASSWORD=... pnpm ios:release:signing:sync:push` to publish encrypted Fastlane match assets to the shared private repo.
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ OPENCLAW_DEVELOPMENT_TEAM = FWJYW4S8P8
|
||||
OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
|
||||
OPENCLAW_SHARE_BUNDLE_ID = ai.openclawfoundation.app.share
|
||||
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
|
||||
OPENCLAW_WATCH_EXTENSION_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp.extension
|
||||
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
|
||||
OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT = development
|
||||
|
||||
@@ -19,7 +18,6 @@ OPENCLAW_APP_PROFILE = ai.openclawfoundation.app Development
|
||||
OPENCLAW_SHARE_PROFILE = ai.openclawfoundation.app.share Development
|
||||
OPENCLAW_ACTIVITY_WIDGET_PROFILE =
|
||||
OPENCLAW_WATCH_APP_PROFILE =
|
||||
OPENCLAW_WATCH_EXTENSION_PROFILE =
|
||||
|
||||
// Keep local includes after defaults: xcconfig is evaluated top-to-bottom,
|
||||
// so later assignments in local files override the defaults above.
|
||||
|
||||
@@ -109,10 +109,10 @@ Sources/Voice/VoiceWakePreferences.swift
|
||||
ShareExtension/ShareViewController.swift
|
||||
ActivityWidget/OpenClawActivityWidgetBundle.swift
|
||||
ActivityWidget/OpenClawLiveActivity.swift
|
||||
WatchExtension/Sources/OpenClawWatchApp.swift
|
||||
WatchExtension/Sources/WatchConnectivityReceiver.swift
|
||||
WatchExtension/Sources/WatchInboxStore.swift
|
||||
WatchExtension/Sources/WatchInboxView.swift
|
||||
WatchApp/Sources/OpenClawWatchApp.swift
|
||||
WatchApp/Sources/WatchConnectivityReceiver.swift
|
||||
WatchApp/Sources/WatchInboxStore.swift
|
||||
WatchApp/Sources/WatchInboxView.swift
|
||||
../shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift
|
||||
../shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift
|
||||
../shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownPreprocessor.swift
|
||||
|
||||
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
@@ -20,9 +20,9 @@
|
||||
<string>$(OPENCLAW_MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(OPENCLAW_BUILD_VERSION)</string>
|
||||
<key>WKApplication</key>
|
||||
<true/>
|
||||
<key>WKCompanionAppBundleIdentifier</key>
|
||||
<string>$(OPENCLAW_APP_BUNDLE_ID)</string>
|
||||
<key>WKWatchKitApp</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>OpenClaw</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(OPENCLAW_MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(OPENCLAW_BUILD_VERSION)</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>WKAppBundleIdentifier</key>
|
||||
<string>$(OPENCLAW_WATCH_APP_BUNDLE_ID)</string>
|
||||
</dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.watchkit</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -293,6 +293,8 @@ def capture_watch_screenshot
|
||||
Dir[File.join(output_dir, "Apple Watch*-*.png")].each { |path| FileUtils.rm_f(path) }
|
||||
FileUtils.rm_rf(derived_data_path)
|
||||
|
||||
# Single-target watch apps only expose generic simulator build destinations in Xcode.
|
||||
# Keep the selected UDID for install/launch/screenshot below.
|
||||
sh(
|
||||
xcodebuild_shell_join([
|
||||
"xcodebuild",
|
||||
@@ -303,7 +305,7 @@ def capture_watch_screenshot
|
||||
"-configuration",
|
||||
"Debug",
|
||||
"-destination",
|
||||
"platform=watchOS Simulator,id=#{udid}",
|
||||
"generic/platform=watchOS Simulator",
|
||||
"-derivedDataPath",
|
||||
derived_data_path,
|
||||
"build",
|
||||
@@ -311,10 +313,8 @@ def capture_watch_screenshot
|
||||
)
|
||||
|
||||
UI.user_error!("Watch screenshot build did not produce #{app_path}.") unless File.exist?(app_path)
|
||||
extension_path = File.join(app_path, "PlugIns", "OpenClawWatchExtension.appex")
|
||||
watch_app_identifier = bundle_identifier_for_product(app_path)
|
||||
watch_extension_identifier = bundle_identifier_for_product(extension_path)
|
||||
screenshot_mode_bundle_identifiers = [watch_app_identifier, watch_extension_identifier]
|
||||
screenshot_mode_bundle_identifiers = [watch_app_identifier]
|
||||
|
||||
sh("#{shell_join(["xcrun", "simctl", "boot", udid])} >/dev/null 2>&1 || true")
|
||||
sh(shell_join(["xcrun", "simctl", "bootstatus", udid, "-b"]))
|
||||
|
||||
+14
-39
@@ -88,7 +88,7 @@ targets:
|
||||
exit 1
|
||||
fi
|
||||
swiftformat --lint --config "$SRCROOT/../../config/swiftformat" \
|
||||
--unexclude "$SRCROOT/Sources,$SRCROOT/ShareExtension,$SRCROOT/ActivityWidget,$SRCROOT/WatchExtension,$SRCROOT/../shared/OpenClawKit,$SRCROOT/../swabble" \
|
||||
--unexclude "$SRCROOT/Sources,$SRCROOT/ShareExtension,$SRCROOT/ActivityWidget,$SRCROOT/WatchApp,$SRCROOT/../shared/OpenClawKit,$SRCROOT/../swabble" \
|
||||
--filelist "$SRCROOT/SwiftSources.input.xcfilelist"
|
||||
- name: SwiftLint
|
||||
basedOnDependencyAnalysis: false
|
||||
@@ -251,13 +251,17 @@ targets:
|
||||
NSExtensionPointIdentifier: com.apple.widgetkit-extension
|
||||
|
||||
OpenClawWatchApp:
|
||||
type: application.watchapp2
|
||||
type: application
|
||||
platform: watchOS
|
||||
deploymentTarget: "11.0"
|
||||
sources:
|
||||
- path: WatchApp
|
||||
excludes:
|
||||
- Info.plist
|
||||
dependencies:
|
||||
- target: OpenClawWatchExtension
|
||||
- sdk: AppIntents.framework
|
||||
- sdk: WatchConnectivity.framework
|
||||
- sdk: UserNotifications.framework
|
||||
configFiles:
|
||||
Debug: Config/Signing.xcconfig
|
||||
Release: Config/Signing.xcconfig
|
||||
@@ -274,6 +278,12 @@ targets:
|
||||
ENABLE_APP_INTENTS_METADATA_GENERATION: NO
|
||||
PRODUCT_BUNDLE_IDENTIFIER: "$(OPENCLAW_WATCH_APP_BUNDLE_ID)"
|
||||
PROVISIONING_PROFILE_SPECIFIER: "$(OPENCLAW_WATCH_APP_PROFILE)"
|
||||
SWIFT_STRICT_CONCURRENCY: complete
|
||||
SWIFT_VERSION: "6.0"
|
||||
configs:
|
||||
Debug:
|
||||
ARCHS: "$(NATIVE_ARCH_ACTUAL)"
|
||||
ONLY_ACTIVE_ARCH: YES
|
||||
info:
|
||||
path: WatchApp/Info.plist
|
||||
properties:
|
||||
@@ -281,42 +291,7 @@ targets:
|
||||
CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)"
|
||||
CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)"
|
||||
WKCompanionAppBundleIdentifier: "$(OPENCLAW_APP_BUNDLE_ID)"
|
||||
WKWatchKitApp: true
|
||||
|
||||
OpenClawWatchExtension:
|
||||
type: watchkit2-extension
|
||||
platform: watchOS
|
||||
deploymentTarget: "11.0"
|
||||
sources:
|
||||
- path: WatchExtension/Sources
|
||||
- path: WatchExtension/Assets.xcassets
|
||||
dependencies:
|
||||
- sdk: AppIntents.framework
|
||||
- sdk: WatchConnectivity.framework
|
||||
- sdk: UserNotifications.framework
|
||||
configFiles:
|
||||
Debug: Config/Signing.xcconfig
|
||||
Release: Config/Signing.xcconfig
|
||||
attributes:
|
||||
DevelopmentTeam: "$(OPENCLAW_DEVELOPMENT_TEAM)"
|
||||
ProvisioningStyle: "$(OPENCLAW_CODE_SIGN_STYLE)"
|
||||
settings:
|
||||
base:
|
||||
CODE_SIGN_IDENTITY: "$(OPENCLAW_CODE_SIGN_IDENTITY)"
|
||||
CODE_SIGN_STYLE: "$(OPENCLAW_CODE_SIGN_STYLE)"
|
||||
DEVELOPMENT_TEAM: "$(OPENCLAW_DEVELOPMENT_TEAM)"
|
||||
PRODUCT_BUNDLE_IDENTIFIER: "$(OPENCLAW_WATCH_EXTENSION_BUNDLE_ID)"
|
||||
PROVISIONING_PROFILE_SPECIFIER: "$(OPENCLAW_WATCH_EXTENSION_PROFILE)"
|
||||
info:
|
||||
path: WatchExtension/Info.plist
|
||||
properties:
|
||||
CFBundleDisplayName: OpenClaw
|
||||
CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)"
|
||||
CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)"
|
||||
NSExtension:
|
||||
NSExtensionAttributes:
|
||||
WKAppBundleIdentifier: "$(OPENCLAW_WATCH_APP_BUNDLE_ID)"
|
||||
NSExtensionPointIdentifier: com.apple.watchkit
|
||||
WKApplication: true
|
||||
|
||||
OpenClawTests:
|
||||
type: bundle.unit-test
|
||||
|
||||
@@ -70,7 +70,6 @@ bundle_base="$(normalize_bundle_id "${bundle_base}")"
|
||||
share_bundle_id="${OPENCLAW_IOS_SHARE_BUNDLE_ID:-${bundle_base}.share}"
|
||||
activity_widget_bundle_id="${OPENCLAW_IOS_ACTIVITY_WIDGET_BUNDLE_ID:-${bundle_base}.activitywidget}"
|
||||
watch_app_bundle_id="${OPENCLAW_IOS_WATCH_APP_BUNDLE_ID:-${bundle_base}.watchkitapp}"
|
||||
watch_extension_bundle_id="${OPENCLAW_IOS_WATCH_EXTENSION_BUNDLE_ID:-${watch_app_bundle_id}.extension}"
|
||||
|
||||
code_sign_style="${OPENCLAW_IOS_CODE_SIGN_STYLE:-Automatic}"
|
||||
code_sign_identity="${OPENCLAW_IOS_CODE_SIGN_IDENTITY:-Apple Development}"
|
||||
@@ -79,7 +78,6 @@ app_profile="${OPENCLAW_IOS_APP_PROFILE:-}"
|
||||
share_profile="${OPENCLAW_IOS_SHARE_PROFILE:-}"
|
||||
activity_widget_profile="${OPENCLAW_IOS_ACTIVITY_WIDGET_PROFILE:-}"
|
||||
watch_app_profile="${OPENCLAW_IOS_WATCH_APP_PROFILE:-}"
|
||||
watch_extension_profile="${OPENCLAW_IOS_WATCH_EXTENSION_PROFILE:-}"
|
||||
|
||||
tmp_file="$(mktemp "${TMPDIR:-/tmp}/openclaw-ios-configure-signing.XXXXXX")"
|
||||
cat >"${tmp_file}" <<EOF
|
||||
@@ -89,11 +87,11 @@ cat >"${tmp_file}" <<EOF
|
||||
// IOS_DEVELOPMENT_TEAM / IOS_PREFERRED_TEAM_ID
|
||||
// OPENCLAW_IOS_APP_BUNDLE_ID / OPENCLAW_IOS_BUNDLE_ID_BASE
|
||||
// OPENCLAW_IOS_SHARE_BUNDLE_ID / OPENCLAW_IOS_ACTIVITY_WIDGET_BUNDLE_ID
|
||||
// OPENCLAW_IOS_WATCH_APP_BUNDLE_ID / OPENCLAW_IOS_WATCH_EXTENSION_BUNDLE_ID
|
||||
// OPENCLAW_IOS_WATCH_APP_BUNDLE_ID
|
||||
// OPENCLAW_IOS_CODE_SIGN_STYLE / OPENCLAW_IOS_CODE_SIGN_IDENTITY
|
||||
// OPENCLAW_IOS_APNS_ENTITLEMENT_ENVIRONMENT / OPENCLAW_IOS_APP_PROFILE
|
||||
// OPENCLAW_IOS_SHARE_PROFILE / OPENCLAW_IOS_ACTIVITY_WIDGET_PROFILE
|
||||
// OPENCLAW_IOS_WATCH_APP_PROFILE / OPENCLAW_IOS_WATCH_EXTENSION_PROFILE
|
||||
// OPENCLAW_IOS_WATCH_APP_PROFILE
|
||||
OPENCLAW_CODE_SIGN_STYLE = ${code_sign_style}
|
||||
OPENCLAW_CODE_SIGN_IDENTITY = ${code_sign_identity}
|
||||
OPENCLAW_DEVELOPMENT_TEAM = ${team_id}
|
||||
@@ -103,13 +101,11 @@ OPENCLAW_APP_BUNDLE_ID = ${bundle_base}
|
||||
OPENCLAW_SHARE_BUNDLE_ID = ${share_bundle_id}
|
||||
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ${activity_widget_bundle_id}
|
||||
OPENCLAW_WATCH_APP_BUNDLE_ID = ${watch_app_bundle_id}
|
||||
OPENCLAW_WATCH_EXTENSION_BUNDLE_ID = ${watch_extension_bundle_id}
|
||||
OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT = ${apns_entitlement_environment}
|
||||
OPENCLAW_APP_PROFILE = ${app_profile}
|
||||
OPENCLAW_SHARE_PROFILE = ${share_profile}
|
||||
OPENCLAW_ACTIVITY_WIDGET_PROFILE = ${activity_widget_profile}
|
||||
OPENCLAW_WATCH_APP_PROFILE = ${watch_app_profile}
|
||||
OPENCLAW_WATCH_EXTENSION_PROFILE = ${watch_extension_profile}
|
||||
EOF
|
||||
|
||||
if [[ -f "${LOCAL_SIGNING_FILE}" ]] && cmp -s "${tmp_file}" "${LOCAL_SIGNING_FILE}"; then
|
||||
|
||||
@@ -172,7 +172,6 @@ OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
|
||||
OPENCLAW_SHARE_BUNDLE_ID = ai.openclawfoundation.app.share
|
||||
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
|
||||
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
|
||||
OPENCLAW_WATCH_EXTENSION_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp.extension
|
||||
OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT = production
|
||||
OPENCLAW_PUSH_TRANSPORT = relay
|
||||
OPENCLAW_PUSH_DISTRIBUTION = official
|
||||
|
||||
@@ -28,7 +28,6 @@ function runConfigureSigning(teamId: string, user = "localuser"): string {
|
||||
OPENCLAW_IOS_SHARE_BUNDLE_ID: "",
|
||||
OPENCLAW_IOS_ACTIVITY_WIDGET_BUNDLE_ID: "",
|
||||
OPENCLAW_IOS_WATCH_APP_BUNDLE_ID: "",
|
||||
OPENCLAW_IOS_WATCH_EXTENSION_BUNDLE_ID: "",
|
||||
USER: user,
|
||||
},
|
||||
encoding: "utf8",
|
||||
|
||||
@@ -28,9 +28,7 @@ describe("scripts/ios-release-signing.mjs", () => {
|
||||
expect(output).toContain(
|
||||
"OPENCLAW_WATCH_APP_PROFILE = OpenClaw App Store ai.openclawfoundation.app.watchkitapp",
|
||||
);
|
||||
expect(output).toContain(
|
||||
"OPENCLAW_WATCH_EXTENSION_PROFILE = OpenClaw App Store ai.openclawfoundation.app.watchkitapp.extension",
|
||||
);
|
||||
expect(output).not.toContain("OPENCLAW_WATCH_EXTENSION_PROFILE");
|
||||
});
|
||||
|
||||
it("documents the canonical release signing plan", () => {
|
||||
@@ -40,9 +38,7 @@ describe("scripts/ios-release-signing.mjs", () => {
|
||||
expect(output).toContain("Signing repo: git@github.com:openclaw/apps-signing.git");
|
||||
expect(output).toContain("Signing branch: main");
|
||||
expect(output).toContain("Signing setup and sync: Fastlane match");
|
||||
expect(output).toContain(
|
||||
"OpenClawWatchExtension: ai.openclawfoundation.app.watchkitapp.extension",
|
||||
);
|
||||
expect(output).not.toContain("OpenClawWatchExtension");
|
||||
expect(output).toContain("capabilities: PUSH_NOTIFICATIONS");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user