diff --git a/apps/ios/TeleTuiIOS/README.md b/apps/ios/TeleTuiIOS/README.md index e07d390..f7a3ce3 100644 --- a/apps/ios/TeleTuiIOS/README.md +++ b/apps/ios/TeleTuiIOS/README.md @@ -15,15 +15,20 @@ cd apps/ios/TeleTuiIOS swift run TeleTuiIOSSmokeTests ``` -Simulator/device build is currently blocked until the Xcode license is accepted. -Xcode is installed at `/Applications/Xcode.app`, and `DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -version` reports Xcode 26.5, but `xcrun simctl` fails with: - -```text -You have not agreed to the Xcode license agreements. Please run 'sudo xcodebuild -license' from within a Terminal window to review and agree to the Xcode and Apple SDKs license. -``` - -After accepting the license, verify local iOS tooling with: +Verify local iOS tooling: ```bash DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer scripts/check-ios-prereqs.sh ``` + +Build the SwiftUI shell for iOS Simulator and package it as an installable `.app`: + +```bash +DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer scripts/build-ios-simulator-app.sh +``` + +Launch the fake-backed app in the first available iPhone simulator: + +```bash +DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer scripts/run-ios-simulator-app.sh +``` diff --git a/crates/tele-ios-ffi/README.md b/crates/tele-ios-ffi/README.md index 72e1a27..dfe8b93 100644 --- a/crates/tele-ios-ffi/README.md +++ b/crates/tele-ios-ffi/README.md @@ -18,9 +18,8 @@ The script builds `target/release/libtele_ios_ffi.a` and writes Swift sources, headers, a Swift typecheck-friendly `tele_ios_ffiFFI` module map, and an XCFramework-compatible module map under `build/ios-ffi/`. -Known blocker: +Current linking status: - Xcode is installed at `/Applications/Xcode.app`, and `DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -version` reports Xcode 26.5. -- `xcrun simctl` currently fails because the Xcode license has not been accepted: - `You have not agreed to the Xcode license agreements. Please run 'sudo xcodebuild -license' from within a Terminal window to review and agree to the Xcode and Apple SDKs license.` -- Real TDLib iOS simulator/device linking therefore is not validated yet. +- The iOS 26.5 simulator runtime is installed and `scripts/check-ios-prereqs.sh` passes with available iPhone/iPad simulators. +- The current app shell uses the fake Swift bridge. Real TDLib iOS simulator/device linking is still pending until TDLib is built for `iphonesimulator` and `iphoneos` and wired into the UniFFI target. diff --git a/docs/ios/hardening.md b/docs/ios/hardening.md index 5c8f1db..e4384cc 100644 --- a/docs/ios/hardening.md +++ b/docs/ios/hardening.md @@ -1,11 +1,12 @@ # iOS Hardening Notes -Phase 6 real-device validation is blocked on this machine until the Xcode license is accepted. +The local Xcode prerequisite gate now passes with Xcode 26.5 and the iOS 26.5 simulator runtime. -Current local blocker: +Validated commands: -```text -You have not agreed to the Xcode license agreements. Please run 'sudo xcodebuild -license' from within a Terminal window to review and agree to the Xcode and Apple SDKs license. +```bash +DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer scripts/check-ios-prereqs.sh +DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -scheme TeleTuiIOSApp -destination 'generic/platform=iOS Simulator' build ``` Implemented hardening hooks: @@ -14,7 +15,7 @@ Implemented hardening hooks: - Account switches increment a session generation counter. - Events are accepted only when both account id and generation match the active session, preventing stale events from a previous account from reaching view models. -Manual smoke still required after Xcode/TDLib device setup: +Manual smoke still required after real TDLib device setup: 1. Auth with real Telegram credentials. 2. Load chats, open multiple chats, send/edit/delete/reply/forward/react. diff --git a/docs/ios/release-checklist.md b/docs/ios/release-checklist.md index 670d8ef..ca0d5e3 100644 --- a/docs/ios/release-checklist.md +++ b/docs/ios/release-checklist.md @@ -11,13 +11,15 @@ ## Required Before TestFlight 1. Install full Xcode, accept the license, and verify `scripts/check-ios-prereqs.sh`. -2. Create the Xcode app project or wire the Swift package into an Xcode app target. -3. Add app icon, launch screen, bundle id, signing team, notification capability, and entitlements. -4. Build TDLib for simulator and device architectures. -5. Package Rust static libraries and generated UniFFI Swift/header/modulemap output into an XCFramework. -6. Run simulator smoke with fake bridge. -7. Run real-device smoke with real Telegram credentials. -8. Archive and upload to TestFlight. +2. Build the fake-backed simulator shell with `scripts/build-ios-simulator-app.sh`. +3. Launch the fake-backed simulator shell with `scripts/run-ios-simulator-app.sh`. +4. Create the Xcode app project or wire the Swift package into an Xcode app target for signing/archive. +5. Add app icon, launch screen, bundle id, signing team, notification capability, and entitlements. +6. Build TDLib for simulator and device architectures. +7. Package Rust static libraries and generated UniFFI Swift/header/modulemap output into an XCFramework. +8. Run simulator smoke with fake bridge. +9. Run real-device smoke with real Telegram credentials. +10. Archive and upload to TestFlight. ## CI Gates @@ -29,6 +31,7 @@ - `swiftc -typecheck` for generated Swift bindings - `swift build --product TeleTuiIOSApp` - `swift run TeleTuiIOSSmokeTests` +- `DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer scripts/build-ios-simulator-app.sh` ## Rollback diff --git a/scripts/build-ios-simulator-app.sh b/scripts/build-ios-simulator-app.sh new file mode 100755 index 0000000..950f57c --- /dev/null +++ b/scripts/build-ios-simulator-app.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +package_dir="${repo_root}/apps/ios/TeleTuiIOS" + +if [[ -z "${DEVELOPER_DIR:-}" && -d /Applications/Xcode.app/Contents/Developer ]]; then + export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer +fi + +configuration="${IOS_CONFIGURATION:-Debug}" +derived_data="${IOS_DERIVED_DATA:-/private/tmp/tele-tui-ios-derived-data}" +destination="${IOS_DESTINATION:-generic/platform=iOS Simulator}" +bundle_id="${IOS_BUNDLE_ID:-dev.teletui.TeleTuiIOSApp}" + +pushd "${package_dir}" >/dev/null +xcodebuild \ + -scheme TeleTuiIOSApp \ + -destination "${destination}" \ + -configuration "${configuration}" \ + -derivedDataPath "${derived_data}" \ + build +popd >/dev/null + +product_dir="${derived_data}/Build/Products/${configuration}-iphonesimulator" +binary_path="${product_dir}/TeleTuiIOSApp" +app_path="${product_dir}/TeleTuiIOSApp.app" + +if [[ ! -x "${binary_path}" ]]; then + printf 'Expected simulator executable was not produced: %s\n' "${binary_path}" >&2 + exit 1 +fi + +rm -rf "${app_path}" +mkdir -p "${app_path}" +cp "${binary_path}" "${app_path}/TeleTuiIOSApp" +cp "${package_dir}/Resources/PrivacyInfo.xcprivacy" "${app_path}/PrivacyInfo.xcprivacy" +printf 'APPL????' > "${app_path}/PkgInfo" + +cat > "${app_path}/Info.plist" < + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + TeleTui + CFBundleExecutable + TeleTuiIOSApp + CFBundleIdentifier + ${bundle_id} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + TeleTuiIOSApp + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSupportsIndirectInputEvents + + UILaunchScreen + + UIDeviceFamily + + 1 + 2 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + +PLIST + +codesign --force --sign - --timestamp=none "${app_path}/TeleTuiIOSApp" >/dev/null +codesign --force --sign - --timestamp=none "${app_path}" >/dev/null + +printf 'Packaged simulator app: %s\n' "${app_path}" diff --git a/scripts/check-ios-prereqs.sh b/scripts/check-ios-prereqs.sh index a2bee4b..c081cd9 100755 --- a/scripts/check-ios-prereqs.sh +++ b/scripts/check-ios-prereqs.sh @@ -10,4 +10,10 @@ xcodebuild -version xcrun simctl list devices available swift --version +if ! xcrun simctl list devices available | grep -Eq 'iPhone|iPad'; then + printf 'No available iOS simulator devices found. Install the iOS platform from Xcode Settings > Components or run:\n' >&2 + printf ' DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -downloadPlatform iOS\n' >&2 + exit 1 +fi + printf 'iOS prerequisites are available.\n' diff --git a/scripts/run-ios-simulator-app.sh b/scripts/run-ios-simulator-app.sh new file mode 100755 index 0000000..be37f6a --- /dev/null +++ b/scripts/run-ios-simulator-app.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +if [[ -z "${DEVELOPER_DIR:-}" && -d /Applications/Xcode.app/Contents/Developer ]]; then + export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer +fi + +configuration="${IOS_CONFIGURATION:-Debug}" +derived_data="${IOS_DERIVED_DATA:-/private/tmp/tele-tui-ios-derived-data}" +bundle_id="${IOS_BUNDLE_ID:-dev.teletui.TeleTuiIOSApp}" +app_path="${derived_data}/Build/Products/${configuration}-iphonesimulator/TeleTuiIOSApp.app" + +"${repo_root}/scripts/build-ios-simulator-app.sh" + +simulator_udid="${IOS_SIMULATOR_UDID:-}" +if [[ -z "${simulator_udid}" ]]; then + simulator_udid="$(xcrun simctl list devices available | awk -F '[()]' '/iPhone/ { print $2; exit }')" +fi + +if [[ -z "${simulator_udid}" ]]; then + printf 'No available iPhone simulator found. Run scripts/check-ios-prereqs.sh for details.\n' >&2 + exit 1 +fi + +if ! xcrun simctl list devices "${simulator_udid}" | grep -q 'Booted'; then + xcrun simctl boot "${simulator_udid}" +fi + +xcrun simctl bootstatus "${simulator_udid}" -b +xcrun simctl install "${simulator_udid}" "${app_path}" +xcrun simctl launch "${simulator_udid}" "${bundle_id}" + +printf 'Launched %s on simulator %s.\n' "${bundle_id}" "${simulator_udid}"