field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

commit e058e0e2215fa7753671ec009c863fc0a0162e3a
parent f112a7c07d44ca85cdb7eb3a929663ed7b4d1796
Author: triesap <tyson@radroots.org>
Date:   Sat,  8 Aug 2026 03:40:36 +0000

feat(ios): publish deterministic app package

- Export the app module with an external-consumer API contract.
- Pin public dependencies and validate privacy, version, and FFI locks.
- Route SwiftPM and Xcode outputs through reproducible extbuild lanes.
- Prove standalone bootstrap, offline tests, and bounded cleanup.

Diffstat:
M.gitignore | 1-
MMakefile | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
APackage.resolved | 22++++++++++++++++++++++
APackage.swift | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 50++++++++++++++++++++++++++++++++++++++++++++++++++
MRadroots.xcodeproj/project.pbxproj | 320+++++++++++--------------------------------------------------------------------
MRadroots.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4++--
MRadroots/App/App.swift | 5++---
MRadroots/App/AppEntry.swift | 14++++++++++++++
MRadroots/App/RadrootsAppDelegate.swift | 10+++++++---
MRadroots/Info.plist | 3---
ARadroots/Resources/PrivacyInfo.xcprivacy | 23+++++++++++++++++++++++
MRadroots/radroots.xcconfig | 2--
MRadrootsFFI/Makefile | 3---
ARadrootsFFI/scripts/verify-installed-artifacts.sh | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ARadrootsPublicAPITests/RadrootsAppPublicAPITests.swift | 15+++++++++++++++
MRadrootsTests/RadrootsAddStoreTests.swift | 2+-
MRadrootsTests/RadrootsLifecycleTests.swift | 2+-
MRadrootsTests/RadrootsRootShellTests.swift | 2+-
MRadrootsTests/RadrootsRuntimeClientTests.swift | 2+-
MRadrootsTests/RadrootsStateMigrationTests.swift | 2+-
MRadrootsTests/RadrootsSupportingStoreTests.swift | 2+-
MRadrootsTests/RadrootsTodayStoreTests.swift | 4++--
Aapi/RadrootsApp.symbols.json | 1+
Mproject.yml | 54++++++++++--------------------------------------------
Ascripts/app-api-snapshot.sh | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/generate-project.sh | 31+++++++++++++++++++++++++++++++
Ascripts/swift-package.sh | 28++++++++++++++++++++++++++++
Ascripts/verify-package-contract.sh | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/xcode.sh | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
30 files changed, 697 insertions(+), 349 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -18,7 +18,6 @@ **/*.rs.bk # Generated artifacts -Radroots/radroots.git.xcconfig RadrootsKit/Artifacts/ RadrootsKit/Sources/RadrootsKit/Generated/ Radroots/Frameworks/RadrootsFFI.xcframework/ diff --git a/Makefile b/Makefile @@ -1,9 +1,68 @@ +SHELL := /bin/bash +.SHELLFLAGS := -eu -o pipefail -c + FFI_ROOT := RadrootsFFI +SIMULATOR_NAME ?= iPhone 17 Pro +SIMULATOR_DESTINATION := platform=iOS Simulator,name=$(SIMULATOR_NAME) + +.NOTPARALLEL: + +.PHONY: all bootstrap ffi-bootstrap artifact-check package-contract-check \ + package-resolve package-build package-test project xcodegen xcode-resolve \ + xcode-build-debug xcode-build-release unit-test ui-test api-snapshot-write \ + api-snapshot-check verify clean distclean + +all: verify + +ffi-bootstrap: + cargo extbuild run -- $(MAKE) -C $(FFI_ROOT) verify -.PHONY: all clean distclean sync-source build generate package install api-snapshot-write api-snapshot-check provenance verify print-config project xcodegen +artifact-check: + cargo extbuild run -- $(FFI_ROOT)/scripts/verify-installed-artifacts.sh -all clean distclean sync-source build generate package install api-snapshot-write api-snapshot-check provenance verify print-config: - cargo extbuild run -- $(MAKE) -C $(FFI_ROOT) $@ +package-contract-check: + cargo extbuild run -- scripts/verify-package-contract.sh + +package-resolve: artifact-check package-contract-check + cargo extbuild run -- scripts/swift-package.sh resolve project xcodegen: - cargo extbuild run -- xcodegen generate --spec project.yml + cargo extbuild run -- scripts/generate-project.sh + +xcode-resolve: artifact-check project + cargo extbuild run -- scripts/xcode.sh resolve + +bootstrap: ffi-bootstrap package-resolve xcode-resolve + +package-build: artifact-check package-contract-check + cargo extbuild run -- scripts/xcode.sh package-build + +package-test: artifact-check package-contract-check + cargo extbuild run -- scripts/xcode.sh package-test '$(SIMULATOR_DESTINATION)' + +xcode-build-debug: artifact-check package-contract-check project + cargo extbuild run -- scripts/xcode.sh project-build Debug + +xcode-build-release: artifact-check package-contract-check project + cargo extbuild run -- scripts/xcode.sh project-build Release + +unit-test: artifact-check package-contract-check project + cargo extbuild run -- scripts/xcode.sh project-test '$(SIMULATOR_DESTINATION)' RadrootsTests + +ui-test: artifact-check package-contract-check project + cargo extbuild run -- scripts/xcode.sh project-test '$(SIMULATOR_DESTINATION)' RadrootsUITests + +api-snapshot-write: package-build + cargo extbuild run -- scripts/app-api-snapshot.sh write + +api-snapshot-check: package-build + cargo extbuild run -- scripts/app-api-snapshot.sh check + +verify: artifact-check package-contract-check package-build package-test \ + xcode-build-debug xcode-build-release unit-test ui-test api-snapshot-check + +clean: + cargo extbuild run -- $(MAKE) -C $(FFI_ROOT) clean + +distclean: + cargo extbuild run -- $(MAKE) -C $(FFI_ROOT) distclean diff --git a/Package.resolved b/Package.resolved @@ -0,0 +1,22 @@ +{ + "originHash" : "0a61041d0e86aa3523400309ba62b6cb333fffd0d0e2b52fba38a8554689bff0", + "pins" : [ + { + "identity" : "apple_kit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/radrootslabs/apple_kit.git", + "state" : { + "revision" : "af6047761cd445e8861e11b7df15b1fc52c1fbbf" + } + }, + { + "identity" : "swift-secp256k1", + "kind" : "remoteSourceControl", + "location" : "https://github.com/21-DOT-DEV/swift-secp256k1.git", + "state" : { + "revision" : "e70a10e036a55fffea31568f0af92d69b6d449cd" + } + } + ], + "version" : 3 +} diff --git a/Package.swift b/Package.swift @@ -0,0 +1,85 @@ +// swift-tools-version: 6.0 + +import PackageDescription + +let package = Package( + name: "radroots_ios_app", + platforms: [ + .iOS(.v18), + ], + products: [ + .library(name: "RadrootsApp", targets: ["RadrootsApp"]), + ], + dependencies: [ + .package( + url: "https://github.com/radrootslabs/apple_kit.git", + revision: "af6047761cd445e8861e11b7df15b1fc52c1fbbf" + ), + ], + targets: [ + .binaryTarget( + name: "RadrootsFFI", + path: "Radroots/Frameworks/RadrootsFFI.xcframework" + ), + .target( + name: "RadrootsKitBindings", + dependencies: ["RadrootsFFI"], + path: "Radroots/Generated", + swiftSettings: [ + .swiftLanguageMode(.v5), + ] + ), + .target( + name: "RadrootsApp", + dependencies: [ + "RadrootsKitBindings", + .product(name: "RadrootsKit", package: "apple_kit"), + ], + path: "Radroots", + exclude: [ + "App/App.swift", + "Config", + "Frameworks", + "Generated", + "Info.plist", + "radroots.xcconfig", + ], + sources: [ + "App/AppEntry.swift", + "App/RadrootsAppDelegate.swift", + "App/RadrootsAppModel.swift", + "App/RadrootsProvider.swift", + "App/RadrootsRootShell.swift", + "Runtime/RadrootsAddMediaCoordinator.swift", + "Runtime/RadrootsGeneratedRuntimeBackend.swift", + "Runtime/RadrootsLifecycleCoordinator.swift", + "Runtime/RadrootsRuntimeClient.swift", + "Runtime/RadrootsRuntimeModels.swift", + "State/RadrootsAddStore.swift", + "State/RadrootsConfigurationStore.swift", + "State/RadrootsIdentityStore.swift", + "State/RadrootsSessionStore.swift", + "State/RadrootsSupportingStores.swift", + "State/RadrootsTodayStore.swift", + "Views/RadrootsAddView.swift", + "Views/RadrootsSupportingViews.swift", + "Views/RadrootsTodayView.swift", + "Views/RuntimeStatusView.swift", + ], + resources: [ + .process("Resources/PrivacyInfo.xcprivacy"), + ] + ), + .testTarget( + name: "RadrootsAppTests", + dependencies: ["RadrootsApp"], + path: "RadrootsTests" + ), + .testTarget( + name: "RadrootsAppPublicAPITests", + dependencies: ["RadrootsApp"], + path: "RadrootsPublicAPITests" + ), + ], + swiftLanguageModes: [.v6] +) diff --git a/README.md b/README.md @@ -0,0 +1,50 @@ +# Radroots iOS App + +Radroots is a public iOS 18 app for discovering and publishing local farm +updates, asks, in-person events, and food listings over Nostr. The product has +exactly two bottom tabs: Today for discovery and Add for authored operations. + +The current public release is `0.1.0-alpha`. + +## Requirements + +- macOS with Xcode and an iOS 18-or-newer simulator +- XcodeGen +- Rust `1.97.1-aarch64-apple-darwin` with the iOS device and simulator targets +- `cargo-extbuild` configured for the checkout + +## Bootstrap and verify + +The first bootstrap requires network access. It checks out the exact Rust +source revision, builds the deterministic UniFFI XCFramework and Swift +bindings, resolves exact Swift package revisions, and generates the Xcode +project: + +```sh +cargo extbuild doctor +make bootstrap +``` + +After bootstrap, the complete package and Xcode build/test lane uses the +resolved revisions without automatic dependency updates: + +```sh +make verify +``` + +Use `SIMULATOR_NAME="Device Name" make verify` when the default simulator is +not installed. `make clean` removes only rebuildable external build output and +the ignored XCFramework; it preserves tracked generated bindings, locks, +snapshots, project sources, and user files. + +## Package surface + +`Package.swift` publishes the `RadrootsApp` library used by the generated Xcode +application wrapper. It pins AppleKit by exact HTTPS Git revision and consumes +the locally bootstrapped `RadrootsFFI.xcframework`. Ordinary Xcode compilation +never writes repository source: a read-only preflight rejects missing or stale +FFI artifacts and directs the developer to run `make bootstrap`. + +The Rust source lock, generated bindings, XCFramework hashes, provenance, Swift +package locks, privacy manifests, and public API snapshots are checked as part +of the release lane. diff --git a/Radroots.xcodeproj/project.pbxproj b/Radroots.xcodeproj/project.pbxproj @@ -7,51 +7,23 @@ objects = { /* Begin PBXBuildFile section */ - 0486BFE6223D0FF0ACFA4249 /* RadrootsAddStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2E3E5B1D1A0E1A32A83100 /* RadrootsAddStore.swift */; }; + 0376BB3ECA35B44625702BFE /* RadrootsApp in Frameworks */ = {isa = PBXBuildFile; productRef = E17BA8C6979DF56FB5F385B8 /* RadrootsApp */; }; 27B7FB991398E2BE7034C72E /* RadrootsAddStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C296DF1E35100FE59C04D3 /* RadrootsAddStoreTests.swift */; }; - 27F796571E92A6589E0111E3 /* RadrootsKitBindings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF8CEF5491290B1218ACFCE7 /* RadrootsKitBindings.framework */; }; - 3233513A881D056514142457 /* RadrootsAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE3D78A3EDB345DB46C8E547 /* RadrootsAppDelegate.swift */; }; 3BCAFE2B1A96BBE5D741D8FC /* RadrootsSupportingStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41B94CEAFD958CE92D6B9265 /* RadrootsSupportingStoreTests.swift */; }; - 3C16EC0B109F76EEE8D3031A /* RadrootsSupportingStores.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478127DF070D6065A282C44A /* RadrootsSupportingStores.swift */; }; - 41B5CB28ACA51221A1BA59D5 /* RadrootsSessionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76F84D4706216F743E373F2A /* RadrootsSessionStore.swift */; }; - 47D9564F81A5E7CEA86B759C /* RadrootsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57C4A7EB045E43F109C4DCA1 /* RadrootsProvider.swift */; }; - 499BCC8FC845FDD77E74FD99 /* RadrootsTodayStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6A6D1850A21E5516060E5E /* RadrootsTodayStore.swift */; }; - 509FF909EFFD7FD9BAEC537C /* RadrootsRootShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF529B919921D63157110E44 /* RadrootsRootShell.swift */; }; 51FE0A95B71A5D4EDCEB0748 /* RadrootsStateMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26D512B6CBD1A9BE4780682 /* RadrootsStateMigrationTests.swift */; }; - 5DAE76BBC71E4A192E029785 /* RadrootsGeneratedRuntimeBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B14CE9958D4C71AFB41D2DD /* RadrootsGeneratedRuntimeBackend.swift */; }; - 69326B7DA05C115D0055AD13 /* RadrootsKitBindings.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FF8CEF5491290B1218ACFCE7 /* RadrootsKitBindings.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 74082509C257766A166662FA /* RadrootsAppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 343F115F383EB9C8A796AC9C /* RadrootsAppModel.swift */; }; - 75275CDCE16FB99248793AB4 /* RadrootsSupportingViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29DD502141AA86FD5C948618 /* RadrootsSupportingViews.swift */; }; + 60B853393C8DF4FDF2C01CAF /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 81E991EAE0A0EAB4853747C6 /* PrivacyInfo.xcprivacy */; }; 75C044403AF08F7230DE9485 /* RadrootsRootShellUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF484F988B3D1D3D81880388 /* RadrootsRootShellUITests.swift */; }; - 7A1131CC1E9308706ECE4B7B /* RadrootsLifecycleCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DAAFF0BC944ECC04D0BDF41 /* RadrootsLifecycleCoordinator.swift */; }; 7CF6D05AC3F7C8CAD56E3A91 /* RadrootsRootShellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F89E4B3A775E88F661B891 /* RadrootsRootShellTests.swift */; }; - 86DF51D41F92E1A695D83205 /* RadrootsTodayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78F57CD1A43D46EE8CB35591 /* RadrootsTodayView.swift */; }; - AA7D80A73CD6C22ED5080781 /* RadrootsKitBindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879C89400666972D90996AE5 /* RadrootsKitBindings.swift */; }; - B356719A54375706DBBDC118 /* RadrootsRuntimeModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DDB24124BB8917DBB49EC2C /* RadrootsRuntimeModels.swift */; }; - BB932CEEA2713588E14AC3ED /* RadrootsAddView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0C073F20D8716C60FCCF2C /* RadrootsAddView.swift */; }; C356F2A068722087ED91256C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 298DA81B0A000E307098C840 /* Assets.xcassets */; }; - CA827A410369AC55B0FEE305 /* RuntimeStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9469790A906080D90F8735CB /* RuntimeStatusView.swift */; }; - CAD7B38817DC65AAF19576F7 /* RadrootsFFI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD7B47A576C4D5CE9318D3E6 /* RadrootsFFI.xcframework */; }; CD88BB6222151E0B8242F84A /* RadrootsLifecycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D95CB1F4A964D87D2256DA9C /* RadrootsLifecycleTests.swift */; }; CEED5B97CB1F94445162D662 /* RadrootsRuntimeClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0451E8A8521D320172FFA35 /* RadrootsRuntimeClientTests.swift */; }; - D03C81CCF4FBC6D7D5634D35 /* RadrootsConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF9FBA655BDA52E08FB62EED /* RadrootsConfigurationStore.swift */; }; + D86E464EADF0C7904AB2FF90 /* RadrootsKit in Frameworks */ = {isa = PBXBuildFile; productRef = 54C7F50B545852A8B313DAA7 /* RadrootsKit */; }; E236CCF8D8F8AC12AF59932E /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 397ED29DF8E9BF73B4BBFDEC /* App.swift */; }; - ED326EC0067A550727A1680D /* AppEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0D9C4E6CAE584304521D08 /* AppEntry.swift */; }; - F3E40E5A76B4EA19AC7603D2 /* RadrootsKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2DAD90EBF8EB00ACDD7611CD /* RadrootsKit */; }; - F6F36A06BD77BCFA64FC3541 /* RadrootsIdentityStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0BC5AE5000DE34D9303379 /* RadrootsIdentityStore.swift */; }; - F7EEF54776B037D98F389AD6 /* RadrootsRuntimeClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57F5216F7C18E2EE21B8528A /* RadrootsRuntimeClient.swift */; }; - F9EA6E6935307271FF920B7D /* RadrootsAddMediaCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7098C419BAC4E8E058A2B3C2 /* RadrootsAddMediaCoordinator.swift */; }; + F3E40E5A76B4EA19AC7603D2 /* RadrootsApp in Frameworks */ = {isa = PBXBuildFile; productRef = 5EB99D569A31C7F8987A9440 /* RadrootsApp */; }; FE9376121B0780485673EC06 /* RadrootsTodayStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D91770F919A06DB39186281B /* RadrootsTodayStoreTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 51F4BD021D143679140FF35F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 572C41532B5066EC7641561C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 1C22115E511334B291A71594; - remoteInfo = RadrootsKitBindings; - }; 5B242C3EC56D6F3216103D77 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 572C41532B5066EC7641561C /* Project object */; @@ -68,76 +40,41 @@ }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 271D40E26A903BC9F058633E /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 69326B7DA05C115D0055AD13 /* RadrootsKitBindings.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 298DA81B0A000E307098C840 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; - 29DD502141AA86FD5C948618 /* RadrootsSupportingViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsSupportingViews.swift; sourceTree = "<group>"; }; - 2A6A6D1850A21E5516060E5E /* RadrootsTodayStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsTodayStore.swift; sourceTree = "<group>"; }; - 2D0BC5AE5000DE34D9303379 /* RadrootsIdentityStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsIdentityStore.swift; sourceTree = "<group>"; }; 30A8A8321A8A261F6D1B480D /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; 318804462AADCA05FB04ACD9 /* RadrootsTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = RadrootsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 343F115F383EB9C8A796AC9C /* RadrootsAppModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsAppModel.swift; sourceTree = "<group>"; }; 397ED29DF8E9BF73B4BBFDEC /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; }; - 3DDB24124BB8917DBB49EC2C /* RadrootsRuntimeModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsRuntimeModels.swift; sourceTree = "<group>"; }; 41B94CEAFD958CE92D6B9265 /* RadrootsSupportingStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsSupportingStoreTests.swift; sourceTree = "<group>"; }; 42C296DF1E35100FE59C04D3 /* RadrootsAddStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsAddStoreTests.swift; sourceTree = "<group>"; }; 43B0EA447E213CD1B96846D6 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; - 478127DF070D6065A282C44A /* RadrootsSupportingStores.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsSupportingStores.swift; sourceTree = "<group>"; }; - 4E0D9C4E6CAE584304521D08 /* AppEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppEntry.swift; sourceTree = "<group>"; }; - 57C4A7EB045E43F109C4DCA1 /* RadrootsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsProvider.swift; sourceTree = "<group>"; }; - 57F5216F7C18E2EE21B8528A /* RadrootsRuntimeClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsRuntimeClient.swift; sourceTree = "<group>"; }; - 5B14CE9958D4C71AFB41D2DD /* RadrootsGeneratedRuntimeBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsGeneratedRuntimeBackend.swift; sourceTree = "<group>"; }; - 7098C419BAC4E8E058A2B3C2 /* RadrootsAddMediaCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsAddMediaCoordinator.swift; sourceTree = "<group>"; }; - 76F84D4706216F743E373F2A /* RadrootsSessionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsSessionStore.swift; sourceTree = "<group>"; }; - 78F57CD1A43D46EE8CB35591 /* RadrootsTodayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsTodayView.swift; sourceTree = "<group>"; }; - 879C89400666972D90996AE5 /* RadrootsKitBindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsKitBindings.swift; sourceTree = "<group>"; }; - 8DAAFF0BC944ECC04D0BDF41 /* RadrootsLifecycleCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsLifecycleCoordinator.swift; sourceTree = "<group>"; }; + 81E991EAE0A0EAB4853747C6 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; }; 93AA285819DD1269C3EAD80A /* Radroots.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = Radroots.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 9469790A906080D90F8735CB /* RuntimeStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeStatusView.swift; sourceTree = "<group>"; }; + 9F54FC4930051FC4611B37D3 /* ios_app */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ios_app; path = .; sourceTree = SOURCE_ROOT; }; B8F89E4B3A775E88F661B891 /* RadrootsRootShellTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsRootShellTests.swift; sourceTree = "<group>"; }; - BD7B47A576C4D5CE9318D3E6 /* RadrootsFFI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = RadrootsFFI.xcframework; path = Radroots/Frameworks/RadrootsFFI.xcframework; sourceTree = "<group>"; }; C26D512B6CBD1A9BE4780682 /* RadrootsStateMigrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsStateMigrationTests.swift; sourceTree = "<group>"; }; CAD74320A58874A7F7370505 /* RadrootsUITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = RadrootsUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - CE0C073F20D8716C60FCCF2C /* RadrootsAddView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsAddView.swift; sourceTree = "<group>"; }; CF484F988B3D1D3D81880388 /* RadrootsRootShellUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsRootShellUITests.swift; sourceTree = "<group>"; }; - CF529B919921D63157110E44 /* RadrootsRootShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsRootShell.swift; sourceTree = "<group>"; }; D0451E8A8521D320172FFA35 /* RadrootsRuntimeClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsRuntimeClientTests.swift; sourceTree = "<group>"; }; D91770F919A06DB39186281B /* RadrootsTodayStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsTodayStoreTests.swift; sourceTree = "<group>"; }; D95CB1F4A964D87D2256DA9C /* RadrootsLifecycleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsLifecycleTests.swift; sourceTree = "<group>"; }; - DA2E3E5B1D1A0E1A32A83100 /* RadrootsAddStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsAddStore.swift; sourceTree = "<group>"; }; - DF9FBA655BDA52E08FB62EED /* RadrootsConfigurationStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsConfigurationStore.swift; sourceTree = "<group>"; }; - EE3D78A3EDB345DB46C8E547 /* RadrootsAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadrootsAppDelegate.swift; sourceTree = "<group>"; }; - FF8CEF5491290B1218ACFCE7 /* RadrootsKitBindings.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RadrootsKitBindings.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 044C68780F358BC2C58657B8 /* Frameworks */ = { + 1384F9BE8BB29250A1B4A26A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CAD7B38817DC65AAF19576F7 /* RadrootsFFI.xcframework in Frameworks */, + F3E40E5A76B4EA19AC7603D2 /* RadrootsApp in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 1384F9BE8BB29250A1B4A26A /* Frameworks */ = { + ACCF9244C9199579A4D93444 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 27F796571E92A6589E0111E3 /* RadrootsKitBindings.framework in Frameworks */, - F3E40E5A76B4EA19AC7603D2 /* RadrootsKit in Frameworks */, + 0376BB3ECA35B44625702BFE /* RadrootsApp in Frameworks */, + D86E464EADF0C7904AB2FF90 /* RadrootsKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -172,66 +109,34 @@ isa = PBXGroup; children = ( 93AA285819DD1269C3EAD80A /* Radroots.app */, - FF8CEF5491290B1218ACFCE7 /* RadrootsKitBindings.framework */, 318804462AADCA05FB04ACD9 /* RadrootsTests.xctest */, CAD74320A58874A7F7370505 /* RadrootsUITests.xctest */, ); name = Products; sourceTree = "<group>"; }; - 64C889EFCEB42611797110F7 /* Runtime */ = { + 9458C318B571871852A3FD1B /* Packages */ = { isa = PBXGroup; children = ( - 7098C419BAC4E8E058A2B3C2 /* RadrootsAddMediaCoordinator.swift */, - 5B14CE9958D4C71AFB41D2DD /* RadrootsGeneratedRuntimeBackend.swift */, - 8DAAFF0BC944ECC04D0BDF41 /* RadrootsLifecycleCoordinator.swift */, - 57F5216F7C18E2EE21B8528A /* RadrootsRuntimeClient.swift */, - 3DDB24124BB8917DBB49EC2C /* RadrootsRuntimeModels.swift */, - ); - name = Runtime; - path = Radroots/Runtime; + 9F54FC4930051FC4611B37D3 /* ios_app */, + ); + name = Packages; sourceTree = "<group>"; }; 94F94915631E6DC54AAB0B89 /* Resources */ = { isa = PBXGroup; children = ( 298DA81B0A000E307098C840 /* Assets.xcassets */, + 81E991EAE0A0EAB4853747C6 /* PrivacyInfo.xcprivacy */, ); name = Resources; path = Radroots/Resources; sourceTree = "<group>"; }; - 97FA23F0FD7E25C1AF2585FB /* Frameworks */ = { - isa = PBXGroup; - children = ( - BD7B47A576C4D5CE9318D3E6 /* RadrootsFFI.xcframework */, - ); - name = Frameworks; - sourceTree = "<group>"; - }; - A2DE5BE1D645F19DA46B3FC5 /* State */ = { - isa = PBXGroup; - children = ( - DA2E3E5B1D1A0E1A32A83100 /* RadrootsAddStore.swift */, - DF9FBA655BDA52E08FB62EED /* RadrootsConfigurationStore.swift */, - 2D0BC5AE5000DE34D9303379 /* RadrootsIdentityStore.swift */, - 76F84D4706216F743E373F2A /* RadrootsSessionStore.swift */, - 478127DF070D6065A282C44A /* RadrootsSupportingStores.swift */, - 2A6A6D1850A21E5516060E5E /* RadrootsTodayStore.swift */, - ); - name = State; - path = Radroots/State; - sourceTree = "<group>"; - }; C22A3ECCB9E9AF9E57B74576 /* App */ = { isa = PBXGroup; children = ( 397ED29DF8E9BF73B4BBFDEC /* App.swift */, - 4E0D9C4E6CAE584304521D08 /* AppEntry.swift */, - EE3D78A3EDB345DB46C8E547 /* RadrootsAppDelegate.swift */, - 343F115F383EB9C8A796AC9C /* RadrootsAppModel.swift */, - 57C4A7EB045E43F109C4DCA1 /* RadrootsProvider.swift */, - CF529B919921D63157110E44 /* RadrootsRootShell.swift */, ); name = App; path = Radroots/App; @@ -242,39 +147,14 @@ children = ( C22A3ECCB9E9AF9E57B74576 /* App */, 401A3C62BEECB8BD5C500756 /* Config */, - D17ED36BEBE689794F31C2BB /* Generated */, + 9458C318B571871852A3FD1B /* Packages */, 31C88176C5674CFF5F9CFBEA /* RadrootsTests */, FD341F0384225E202A0845B1 /* RadrootsUITests */, 94F94915631E6DC54AAB0B89 /* Resources */, - 64C889EFCEB42611797110F7 /* Runtime */, - A2DE5BE1D645F19DA46B3FC5 /* State */, - E05061D0ADB560FA929B4D9A /* Views */, - 97FA23F0FD7E25C1AF2585FB /* Frameworks */, 6240123423927396E47D6B3E /* Products */, ); sourceTree = "<group>"; }; - D17ED36BEBE689794F31C2BB /* Generated */ = { - isa = PBXGroup; - children = ( - 879C89400666972D90996AE5 /* RadrootsKitBindings.swift */, - ); - name = Generated; - path = Radroots/Generated; - sourceTree = "<group>"; - }; - E05061D0ADB560FA929B4D9A /* Views */ = { - isa = PBXGroup; - children = ( - CE0C073F20D8716C60FCCF2C /* RadrootsAddView.swift */, - 29DD502141AA86FD5C948618 /* RadrootsSupportingViews.swift */, - 78F57CD1A43D46EE8CB35591 /* RadrootsTodayView.swift */, - 9469790A906080D90F8735CB /* RuntimeStatusView.swift */, - ); - name = Views; - path = Radroots/Views; - sourceTree = "<group>"; - }; FD341F0384225E202A0845B1 /* RadrootsUITests */ = { isa = PBXGroup; children = ( @@ -283,52 +163,25 @@ path = RadrootsUITests; sourceTree = "<group>"; }; - "TEMP_C9716F76-7D47-4886-AA36-A6BB8236F43C" /* Localizations */ = { - isa = PBXGroup; - children = ( - ); - path = Localizations; - sourceTree = "<group>"; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 1C22115E511334B291A71594 /* RadrootsKitBindings */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4709AFBD0CA5DE7FC73BFC95 /* Build configuration list for PBXNativeTarget "RadrootsKitBindings" */; - buildPhases = ( - 21C962D4AD2665D5ED66E70F /* Sources */, - 044C68780F358BC2C58657B8 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = RadrootsKitBindings; - packageProductDependencies = ( - ); - productName = RadrootsKitBindings; - productReference = FF8CEF5491290B1218ACFCE7 /* RadrootsKitBindings.framework */; - productType = "com.apple.product-type.framework"; - }; 469EC50C49E7266DF1C2000B /* Radroots */ = { isa = PBXNativeTarget; buildConfigurationList = 2513910CE2A2ED545A78A163 /* Build configuration list for PBXNativeTarget "Radroots" */; buildPhases = ( - 1687531B35A5B613B89DA261 /* Generate git SHA xcconfig */, + 7C10FB7358EE7DB857A84E7B /* Verify installed FFI artifacts */, 78F7810CC77628CB591F8CD2 /* Sources */, 0FB0BFB846269AC353E4AD0E /* Resources */, 1384F9BE8BB29250A1B4A26A /* Frameworks */, - 271D40E26A903BC9F058633E /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( - 3AF61EED138E0994701CAA79 /* PBXTargetDependency */, ); name = Radroots; packageProductDependencies = ( - 2DAD90EBF8EB00ACDD7611CD /* RadrootsKit */, + 5EB99D569A31C7F8987A9440 /* RadrootsApp */, ); productName = Radroots; productReference = 93AA285819DD1269C3EAD80A /* Radroots.app */; @@ -339,6 +192,7 @@ buildConfigurationList = DCDE79513C7AE684F9540473 /* Build configuration list for PBXNativeTarget "RadrootsTests" */; buildPhases = ( 4948BBB44D48B961DE81F508 /* Sources */, + ACCF9244C9199579A4D93444 /* Frameworks */, ); buildRules = ( ); @@ -347,6 +201,8 @@ ); name = RadrootsTests; packageProductDependencies = ( + E17BA8C6979DF56FB5F385B8 /* RadrootsApp */, + 54C7F50B545852A8B313DAA7 /* RadrootsKit */, ); productName = RadrootsTests; productReference = 318804462AADCA05FB04ACD9 /* RadrootsTests.xctest */; @@ -395,13 +251,13 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( 5696E24009739E3BD13D6580 /* XCRemoteSwiftPackageReference "apple_kit" */, + 03D9E5A7B556A6CD41FCF80D /* XCLocalSwiftPackageReference "." */, ); preferredProjectObjectVersion = 77; productRefGroup = 6240123423927396E47D6B3E /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 1C22115E511334B291A71594 /* RadrootsKitBindings */, 469EC50C49E7266DF1C2000B /* Radroots */, 67B2EF8D69F6CEA9BE5A8B04 /* RadrootsTests */, D87D8771FD63904A45FAFB4D /* RadrootsUITests */, @@ -415,13 +271,14 @@ buildActionMask = 2147483647; files = ( C356F2A068722087ED91256C /* Assets.xcassets in Resources */, + 60B853393C8DF4FDF2C01CAF /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1687531B35A5B613B89DA261 /* Generate git SHA xcconfig */ = { + 7C10FB7358EE7DB857A84E7B /* Verify installed FFI artifacts */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; @@ -431,14 +288,14 @@ ); inputPaths = ( ); - name = "Generate git SHA xcconfig"; + name = "Verify installed FFI artifacts"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "GIT_SHA=$(git rev-parse --short HEAD || echo \"unknown\")\necho \"GIT_SHA = $GIT_SHA\" > \"$SRCROOT/Radroots/radroots.git.xcconfig\"\n"; + shellScript = "\"$SRCROOT/RadrootsFFI/scripts/verify-installed-artifacts.sh\"\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -451,14 +308,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 21C962D4AD2665D5ED66E70F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - AA7D80A73CD6C22ED5080781 /* RadrootsKitBindings.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 4948BBB44D48B961DE81F508 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -478,37 +327,12 @@ buildActionMask = 2147483647; files = ( E236CCF8D8F8AC12AF59932E /* App.swift in Sources */, - ED326EC0067A550727A1680D /* AppEntry.swift in Sources */, - F9EA6E6935307271FF920B7D /* RadrootsAddMediaCoordinator.swift in Sources */, - 0486BFE6223D0FF0ACFA4249 /* RadrootsAddStore.swift in Sources */, - BB932CEEA2713588E14AC3ED /* RadrootsAddView.swift in Sources */, - 3233513A881D056514142457 /* RadrootsAppDelegate.swift in Sources */, - 74082509C257766A166662FA /* RadrootsAppModel.swift in Sources */, - D03C81CCF4FBC6D7D5634D35 /* RadrootsConfigurationStore.swift in Sources */, - 5DAE76BBC71E4A192E029785 /* RadrootsGeneratedRuntimeBackend.swift in Sources */, - F6F36A06BD77BCFA64FC3541 /* RadrootsIdentityStore.swift in Sources */, - 7A1131CC1E9308706ECE4B7B /* RadrootsLifecycleCoordinator.swift in Sources */, - 47D9564F81A5E7CEA86B759C /* RadrootsProvider.swift in Sources */, - 509FF909EFFD7FD9BAEC537C /* RadrootsRootShell.swift in Sources */, - F7EEF54776B037D98F389AD6 /* RadrootsRuntimeClient.swift in Sources */, - B356719A54375706DBBDC118 /* RadrootsRuntimeModels.swift in Sources */, - 41B5CB28ACA51221A1BA59D5 /* RadrootsSessionStore.swift in Sources */, - 3C16EC0B109F76EEE8D3031A /* RadrootsSupportingStores.swift in Sources */, - 75275CDCE16FB99248793AB4 /* RadrootsSupportingViews.swift in Sources */, - 499BCC8FC845FDD77E74FD99 /* RadrootsTodayStore.swift in Sources */, - 86DF51D41F92E1A695D83205 /* RadrootsTodayView.swift in Sources */, - CA827A410369AC55B0FEE305 /* RuntimeStatusView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 3AF61EED138E0994701CAA79 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 1C22115E511334B291A71594 /* RadrootsKitBindings */; - targetProxy = 51F4BD021D143679140FF35F /* PBXContainerItemProxy */; - }; 54F8C314CE8A0D06346F56DE /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 469EC50C49E7266DF1C2000B /* Radroots */; @@ -522,37 +346,6 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 065194E2B92BBDACF3D27647 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = x86_64; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "\"Radroots/Frameworks\"", - ); - GENERATE_INFOPLIST_FILE = YES; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = org.radroots.RadrootsKitBindings; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_STRICT_CONCURRENCY = minimal; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; 5486087B252C6EA76ADD9BB8 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 30A8A8321A8A261F6D1B480D /* Debug.xcconfig */; @@ -572,37 +365,6 @@ }; name = Debug; }; - 762521A89ECC390CEE5533D2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = x86_64; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "\"Radroots/Frameworks\"", - ); - GENERATE_INFOPLIST_FILE = YES; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = org.radroots.RadrootsKitBindings; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_STRICT_CONCURRENCY = minimal; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; 8113D02CF1312FB10249F707 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -829,15 +591,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; }; - 4709AFBD0CA5DE7FC73BFC95 /* Build configuration list for PBXNativeTarget "RadrootsKitBindings" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 762521A89ECC390CEE5533D2 /* Debug */, - 065194E2B92BBDACF3D27647 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Debug; - }; B3DDD079CA522503021BDB85 /* Build configuration list for PBXNativeTarget "RadrootsUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -867,23 +620,38 @@ }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 03D9E5A7B556A6CD41FCF80D /* XCLocalSwiftPackageReference "." */ = { + isa = XCLocalSwiftPackageReference; + relativePath = .; + }; +/* End XCLocalSwiftPackageReference section */ + /* Begin XCRemoteSwiftPackageReference section */ 5696E24009739E3BD13D6580 /* XCRemoteSwiftPackageReference "apple_kit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/radrootslabs/apple_kit.git"; requirement = { kind = revision; - revision = 265015fc537bfe507f12d801b9765be9764fbc95; + revision = af6047761cd445e8861e11b7df15b1fc52c1fbbf; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 2DAD90EBF8EB00ACDD7611CD /* RadrootsKit */ = { + 54C7F50B545852A8B313DAA7 /* RadrootsKit */ = { isa = XCSwiftPackageProductDependency; package = 5696E24009739E3BD13D6580 /* XCRemoteSwiftPackageReference "apple_kit" */; productName = RadrootsKit; }; + 5EB99D569A31C7F8987A9440 /* RadrootsApp */ = { + isa = XCSwiftPackageProductDependency; + productName = RadrootsApp; + }; + E17BA8C6979DF56FB5F385B8 /* RadrootsApp */ = { + isa = XCSwiftPackageProductDependency; + productName = RadrootsApp; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 572C41532B5066EC7641561C /* Project object */; diff --git a/Radroots.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Radroots.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,12 +1,12 @@ { - "originHash" : "7d8372bdb86640cb80ede08ccb1cc767f2f406a11d037ca50a3b404cd7bd96f2", + "originHash" : "eee4aa714e42372e2c603bbeff5ceec11e88161ceda9eac253b2c2683f9873dc", "pins" : [ { "identity" : "apple_kit", "kind" : "remoteSourceControl", "location" : "https://github.com/radrootslabs/apple_kit.git", "state" : { - "revision" : "265015fc537bfe507f12d801b9765be9764fbc95" + "revision" : "af6047761cd445e8861e11b7df15b1fc52c1fbbf" } }, { diff --git a/Radroots/App/App.swift b/Radroots/App/App.swift @@ -1,3 +1,4 @@ +import RadrootsApp import SwiftUI @main @@ -6,9 +7,7 @@ struct RadrootsApp: App { var body: some Scene { WindowGroup { - RadrootsProvider { - AppEntry() - } + RadrootsAppView() } } } diff --git a/Radroots/App/AppEntry.swift b/Radroots/App/AppEntry.swift @@ -1,5 +1,19 @@ import SwiftUI +public enum RadrootsAppRelease: Sendable { + public static let version = "0.1.0-alpha" +} + +public struct RadrootsAppView: View { + public init() {} + + public var body: some View { + RadrootsProvider { + AppEntry() + } + } +} + struct AppEntry: View { @EnvironmentObject private var appModel: RadrootsAppModel diff --git a/Radroots/App/RadrootsAppDelegate.swift b/Radroots/App/RadrootsAppDelegate.swift @@ -1,7 +1,11 @@ import UIKit -final class RadrootsAppDelegate: NSObject, UIApplicationDelegate { - func application( +public final class RadrootsAppDelegate: NSObject, UIApplicationDelegate { + override public init() { + super.init() + } + + public func application( _: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void @@ -15,7 +19,7 @@ final class RadrootsAppDelegate: NSObject, UIApplicationDelegate { } } - func applicationWillTerminate(_: UIApplication) { + public func applicationWillTerminate(_: UIApplication) { Task { await RadrootsLifecycleBridge.shared.requestShutdown() } diff --git a/Radroots/Info.plist b/Radroots/Info.plist @@ -26,9 +26,6 @@ <string>Radroots accesses only the photos you choose for a farm update, ask, event, or food post.</string> <key>NSFaceIDUsageDescription</key> <string>Radroots uses Face ID only to unlock the local Nostr identity or approve a post or media upload you choose to sign.</string> - <key>GIT_SHA</key> - <string>$(GIT_SHA)</string> - <key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> diff --git a/Radroots/Resources/PrivacyInfo.xcprivacy b/Radroots/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,23 @@ +<?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>NSPrivacyTracking</key> + <false/> + <key>NSPrivacyTrackingDomains</key> + <array/> + <key>NSPrivacyCollectedDataTypes</key> + <array/> + <key>NSPrivacyAccessedAPITypes</key> + <array> + <dict> + <key>NSPrivacyAccessedAPIType</key> + <string>NSPrivacyAccessedAPICategoryUserDefaults</string> + <key>NSPrivacyAccessedAPITypeReasons</key> + <array> + <string>CA92.1</string> + </array> + </dict> + </array> +</dict> +</plist> diff --git a/Radroots/radroots.xcconfig b/Radroots/radroots.xcconfig @@ -1,3 +1 @@ PRODUCT_BUNDLE_IDENTIFIER = org.radroots.field-ios - -#include? "radroots.git.xcconfig" diff --git a/RadrootsFFI/Makefile b/RadrootsFFI/Makefile @@ -90,9 +90,6 @@ print-config: clean: rm -rf $(TARGET_DIR) $(OUT_DIR) $(ARTIFACTS_DIR)/$(FFI_OUTPUT_NAME).xcframework - if [ -d $(GENERATED_SWIFT_DIR) ]; then \ - find $(GENERATED_SWIFT_DIR) -maxdepth 1 -type f -name '*.swift' -delete; \ - fi distclean: clean rm -rf $(SOURCE_ROOT) diff --git a/RadrootsFFI/scripts/verify-installed-artifacts.sh b/RadrootsFFI/scripts/verify-installed-artifacts.sh @@ -0,0 +1,81 @@ +#!/bin/sh +set -eu + +script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +ios_root=$(CDPATH='' cd -- "$script_dir/../.." && pwd) +lock_file="$ios_root/RadrootsFFI/source.lock" + +lock_value() { + key=$1 + awk -v key="$key" '$2 == key && $3 == ":=" { print $4 }' "$lock_file" +} + +require_sha256() { + path=$1 + expected=$2 + label=$3 + if [ ! -f "$path" ]; then + echo "error: missing $label; run 'make bootstrap' from $ios_root" >&2 + exit 1 + fi + actual=$(shasum -a 256 "$path" | awk '{print $1}') + if [ "$actual" != "$expected" ]; then + echo "error: stale $label; run 'make bootstrap' from $ios_root" >&2 + exit 1 + fi +} + +framework="$ios_root/Radroots/Frameworks/RadrootsFFI.xcframework" +if [ ! -d "$framework" ]; then + echo "error: missing RadrootsFFI.xcframework; run 'make bootstrap' from $ios_root" >&2 + exit 1 +fi + +require_sha256 \ + "$framework/ios-arm64/libradroots_mobile_ffi.a" \ + "$(lock_value RADROOTS_FIELD_FFI_DEVICE_SHA256)" \ + "device FFI library" +require_sha256 \ + "$framework/ios-arm64-simulator/libradroots_mobile_ffi.a" \ + "$(lock_value RADROOTS_FIELD_FFI_SIMULATOR_SHA256)" \ + "simulator FFI library" +require_sha256 \ + "$ios_root/Radroots/Generated/RadrootsKitBindings.swift" \ + "$(lock_value RADROOTS_FIELD_FFI_SWIFT_SHA256)" \ + "generated Swift bindings" +require_sha256 \ + "$framework/ios-arm64-simulator/Headers/RadrootsFFI.h" \ + "$(lock_value RADROOTS_FIELD_FFI_HEADER_SHA256)" \ + "FFI header" +require_sha256 \ + "$framework/ios-arm64-simulator/Headers/module.modulemap" \ + "$(lock_value RADROOTS_FIELD_FFI_MODULEMAP_SHA256)" \ + "FFI module map" +require_sha256 \ + "$ios_root/RadrootsFFI/api/RadrootsKitBindings.symbols.json" \ + "$(lock_value RADROOTS_FIELD_FFI_API_SHA256)" \ + "generated bindings API snapshot" + +framework_sha256=$( + cd "$ios_root/Radroots/Frameworks" + find RadrootsFFI.xcframework -type f -print | LC_ALL=C sort | while IFS= read -r file; do + shasum -a 256 "$file" + done | shasum -a 256 | awk '{print $1}' +) +if [ "$framework_sha256" != "$(lock_value RADROOTS_FIELD_FFI_XCFRAMEWORK_SHA256)" ]; then + echo "error: stale XCFramework aggregate; run 'make bootstrap' from $ios_root" >&2 + exit 1 +fi + +expected_revision=$(lock_value RADROOTS_FIELD_LIB_GIT_REV) +expected_version=$(lock_value RADROOTS_FIELD_FFI_CRATE_VERSION) +if ! grep -Fq "\"revision\": \"$expected_revision\"" "$ios_root/RadrootsFFI/provenance.json"; then + echo "error: stale FFI provenance revision; run 'make bootstrap' from $ios_root" >&2 + exit 1 +fi +if ! grep -Fq "version = \"$expected_version\"" "$ios_root/radroots.lib.source-lock.v1.toml"; then + echo "error: source-lock version differs from FFI source.lock" >&2 + exit 1 +fi + +echo "installed FFI artifacts match source.lock" diff --git a/RadrootsPublicAPITests/RadrootsAppPublicAPITests.swift b/RadrootsPublicAPITests/RadrootsAppPublicAPITests.swift @@ -0,0 +1,15 @@ +import RadrootsApp +import SwiftUI +import UIKit +import XCTest + +final class RadrootsAppPublicAPITests: XCTestCase { + @MainActor + func testSupportedPublicSurfaceCompilesForAnExternalConsumer() { + XCTAssertEqual(RadrootsAppRelease.version, "0.1.0-alpha") + let appView: any View = RadrootsAppView() + let appDelegate: any UIApplicationDelegate = RadrootsAppDelegate() + _ = appView + _ = appDelegate + } +} diff --git a/RadrootsTests/RadrootsAddStoreTests.swift b/RadrootsTests/RadrootsAddStoreTests.swift @@ -1,4 +1,4 @@ -@testable import Radroots +@testable import RadrootsApp import XCTest final class RadrootsAddStoreTests: XCTestCase { diff --git a/RadrootsTests/RadrootsLifecycleTests.swift b/RadrootsTests/RadrootsLifecycleTests.swift @@ -1,5 +1,5 @@ import Foundation -@testable import Radroots +@testable import RadrootsApp import RadrootsKit import XCTest diff --git a/RadrootsTests/RadrootsRootShellTests.swift b/RadrootsTests/RadrootsRootShellTests.swift @@ -1,4 +1,4 @@ -@testable import Radroots +@testable import RadrootsApp import XCTest final class RadrootsRootShellTests: XCTestCase { diff --git a/RadrootsTests/RadrootsRuntimeClientTests.swift b/RadrootsTests/RadrootsRuntimeClientTests.swift @@ -1,4 +1,4 @@ -@testable import Radroots +@testable import RadrootsApp import XCTest final class RadrootsRuntimeClientTests: XCTestCase { diff --git a/RadrootsTests/RadrootsStateMigrationTests.swift b/RadrootsTests/RadrootsStateMigrationTests.swift @@ -1,5 +1,5 @@ import Foundation -@testable import Radroots +@testable import RadrootsApp import RadrootsKit import XCTest diff --git a/RadrootsTests/RadrootsSupportingStoreTests.swift b/RadrootsTests/RadrootsSupportingStoreTests.swift @@ -1,4 +1,4 @@ -@testable import Radroots +@testable import RadrootsApp import XCTest final class RadrootsSupportingStoreTests: XCTestCase { diff --git a/RadrootsTests/RadrootsTodayStoreTests.swift b/RadrootsTests/RadrootsTodayStoreTests.swift @@ -1,4 +1,4 @@ -@testable import Radroots +@testable import RadrootsApp import XCTest final class RadrootsTodayStoreTests: XCTestCase { @@ -182,7 +182,7 @@ final class RadrootsTodayStoreTests: XCTestCase { @MainActor private func startedClient(backend: TodayBackend) async throws -> RadrootsRuntimeClient { let client = RadrootsRuntimeClient { _ in - RadrootsRuntimeBackendStart(backend: backend, snapshot: await backend.snapshot()) + await RadrootsRuntimeBackendStart(backend: backend, snapshot: backend.snapshot()) } _ = try await client.start(configuration: makeConfiguration()) return client diff --git a/api/RadrootsApp.symbols.json b/api/RadrootsApp.symbols.json @@ -0,0 +1 @@ +{"module":{"name":"RadrootsApp","platform":{"architecture":"arm64","environment":"simulator","operatingSystem":{"minimumVersion":{"major":18,"minor":0},"name":"ios"},"vendor":"apple"}},"relationships":[{"kind":"conformsTo","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"c:objc(pl)NSObject"},{"kind":"conformsTo","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"c:objc(pl)UIApplicationDelegate"},{"kind":"conformsTo","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"s:SH"},{"kind":"conformsTo","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"s:SQ"},{"kind":"conformsTo","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"s:s16SendableMetatypeP"},{"kind":"conformsTo","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"s:s23CustomStringConvertibleP"},{"kind":"conformsTo","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"s:s28CustomDebugStringConvertibleP"},{"kind":"conformsTo","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"s:s7CVarArgP"},{"kind":"conformsTo","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"s:s8SendableP"},{"kind":"conformsTo","source":"s:11RadrootsApp0aB4ViewV","target":"s:7SwiftUI4ViewP"},{"kind":"conformsTo","source":"s:11RadrootsApp0aB4ViewV","target":"s:s16SendableMetatypeP"},{"kind":"conformsTo","source":"s:11RadrootsApp0aB4ViewV","target":"s:s8SendableP"},{"kind":"conformsTo","source":"s:11RadrootsApp0aB7ReleaseO","target":"s:s16SendableMetatypeP"},{"kind":"conformsTo","source":"s:11RadrootsApp0aB7ReleaseO","target":"s:s8SendableP"},{"kind":"inheritsFrom","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate","target":"c:objc(cs)NSObject"},{"kind":"memberOf","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate(im)application:handleEventsForBackgroundURLSession:completionHandler:","target":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate"},{"kind":"memberOf","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate(im)applicationWillTerminate:","target":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate"},{"kind":"memberOf","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate(im)init","target":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate"},{"kind":"memberOf","source":"s:11RadrootsApp0aB4ViewV4bodyQrvp","target":"s:11RadrootsApp0aB4ViewV"},{"kind":"memberOf","source":"s:11RadrootsApp0aB4ViewVACycfc","target":"s:11RadrootsApp0aB4ViewV"},{"kind":"memberOf","source":"s:11RadrootsApp0aB7ReleaseO7versionSSvpZ","target":"s:11RadrootsApp0aB7ReleaseO"},{"kind":"overrides","source":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate(im)init","target":"c:objc(cs)NSObject(im)init"}],"schema":"radroots.swift-api-snapshot.v1","symbols":[{"access":"public","declaration":[{"kind":"attribute","spelling":"@"},{"kind":"attribute","preciseIdentifier":"s:ScM","spelling":"MainActor"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"final"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"class"},{"kind":"text","spelling":" "},{"kind":"identifier","spelling":"RadrootsAppDelegate"}],"kind":"swift.class","path":["RadrootsAppDelegate"],"precise":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate"},{"access":"public","declaration":[{"kind":"attribute","spelling":"@"},{"kind":"attribute","preciseIdentifier":"s:ScM","spelling":"MainActor"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"func"},{"kind":"text","spelling":" "},{"kind":"identifier","spelling":"application"},{"kind":"text","spelling":"("},{"kind":"externalParam","spelling":"_"},{"kind":"text","spelling":": "},{"kind":"typeIdentifier","preciseIdentifier":"c:objc(cs)UIApplication","spelling":"UIApplication"},{"kind":"text","spelling":", "},{"kind":"externalParam","spelling":"handleEventsForBackgroundURLSession"},{"kind":"text","spelling":" "},{"kind":"internalParam","spelling":"identifier"},{"kind":"text","spelling":": "},{"kind":"typeIdentifier","preciseIdentifier":"s:SS","spelling":"String"},{"kind":"text","spelling":", "},{"kind":"externalParam","spelling":"completionHandler"},{"kind":"text","spelling":": "},{"kind":"attribute","spelling":"@escaping "},{"kind":"text","spelling":"() -> "},{"kind":"typeIdentifier","preciseIdentifier":"s:s4Voida","spelling":"Void"},{"kind":"text","spelling":")"}],"kind":"swift.method","path":["RadrootsAppDelegate","application(_:handleEventsForBackgroundURLSession:completionHandler:)"],"precise":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate(im)application:handleEventsForBackgroundURLSession:completionHandler:"},{"access":"public","declaration":[{"kind":"attribute","spelling":"@"},{"kind":"attribute","preciseIdentifier":"s:ScM","spelling":"MainActor"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"func"},{"kind":"text","spelling":" "},{"kind":"identifier","spelling":"applicationWillTerminate"},{"kind":"text","spelling":"("},{"kind":"externalParam","spelling":"_"},{"kind":"text","spelling":": "},{"kind":"typeIdentifier","preciseIdentifier":"c:objc(cs)UIApplication","spelling":"UIApplication"},{"kind":"text","spelling":")"}],"kind":"swift.method","path":["RadrootsAppDelegate","applicationWillTerminate(_:)"],"precise":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate(im)applicationWillTerminate:"},{"access":"public","declaration":[{"kind":"attribute","spelling":"@"},{"kind":"attribute","preciseIdentifier":"s:ScM","spelling":"MainActor"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"override"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"init"},{"kind":"text","spelling":"()"}],"kind":"swift.init","path":["RadrootsAppDelegate","init()"],"precise":"c:@M@RadrootsApp@objc(cs)RadrootsAppDelegate(im)init"},{"access":"public","declaration":[{"kind":"attribute","spelling":"@"},{"kind":"attribute","preciseIdentifier":"s:ScM","spelling":"MainActor"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"struct"},{"kind":"text","spelling":" "},{"kind":"identifier","spelling":"RadrootsAppView"}],"kind":"swift.struct","path":["RadrootsAppView"],"precise":"s:11RadrootsApp0aB4ViewV"},{"access":"public","declaration":[{"kind":"attribute","spelling":"@"},{"kind":"attribute","preciseIdentifier":"s:ScM","spelling":"MainActor"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"var"},{"kind":"text","spelling":" "},{"kind":"identifier","spelling":"body"},{"kind":"text","spelling":": "},{"kind":"keyword","spelling":"some"},{"kind":"text","spelling":" "},{"kind":"typeIdentifier","preciseIdentifier":"s:7SwiftUI4ViewP","spelling":"View"},{"kind":"text","spelling":" { "},{"kind":"keyword","spelling":"get"},{"kind":"text","spelling":" }"}],"kind":"swift.property","path":["RadrootsAppView","body"],"precise":"s:11RadrootsApp0aB4ViewV4bodyQrvp"},{"access":"public","declaration":[{"kind":"attribute","spelling":"@"},{"kind":"attribute","preciseIdentifier":"s:ScM","spelling":"MainActor"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"init"},{"kind":"text","spelling":"()"}],"kind":"swift.init","path":["RadrootsAppView","init()"],"precise":"s:11RadrootsApp0aB4ViewVACycfc"},{"access":"public","declaration":[{"kind":"keyword","spelling":"enum"},{"kind":"text","spelling":" "},{"kind":"identifier","spelling":"RadrootsAppRelease"}],"kind":"swift.enum","path":["RadrootsAppRelease"],"precise":"s:11RadrootsApp0aB7ReleaseO"},{"access":"public","declaration":[{"kind":"keyword","spelling":"static"},{"kind":"text","spelling":" "},{"kind":"keyword","spelling":"let"},{"kind":"text","spelling":" "},{"kind":"identifier","spelling":"version"},{"kind":"text","spelling":": "},{"kind":"typeIdentifier","preciseIdentifier":"s:SS","spelling":"String"}],"kind":"swift.type.property","path":["RadrootsAppRelease","version"],"precise":"s:11RadrootsApp0aB7ReleaseO7versionSSvpZ"}]} diff --git a/project.yml b/project.yml @@ -9,54 +9,21 @@ configs: Release: release packages: + RadrootsApp: + path: . RadrootsKit: url: https://github.com/radrootslabs/apple_kit.git - revision: 265015fc537bfe507f12d801b9765be9764fbc95 + revision: af6047761cd445e8861e11b7df15b1fc52c1fbbf targets: - RadrootsKitBindings: - type: framework - platform: iOS - deploymentTarget: "18.0" - sources: - - path: Radroots/Generated/RadrootsKitBindings.swift - settings: - base: - "EXCLUDED_ARCHS[sdk=iphonesimulator*]": x86_64 - GENERATE_INFOPLIST_FILE: true - PRODUCT_BUNDLE_IDENTIFIER: org.radroots.RadrootsKitBindings - SWIFT_VERSION: 5.0 - SWIFT_STRICT_CONCURRENCY: minimal - dependencies: - - framework: Radroots/Frameworks/RadrootsFFI.xcframework - embed: false Radroots: type: application platform: iOS deploymentTarget: "18.0" sources: - path: Radroots/App/App.swift - - path: Radroots/App/RadrootsAppDelegate.swift - - path: Radroots/App/AppEntry.swift - - path: Radroots/App/RadrootsAppModel.swift - - path: Radroots/App/RadrootsProvider.swift - - path: Radroots/App/RadrootsRootShell.swift - - path: Radroots/Runtime/RadrootsGeneratedRuntimeBackend.swift - - path: Radroots/Runtime/RadrootsAddMediaCoordinator.swift - - path: Radroots/Runtime/RadrootsLifecycleCoordinator.swift - - path: Radroots/Runtime/RadrootsRuntimeClient.swift - - path: Radroots/Runtime/RadrootsRuntimeModels.swift - - path: Radroots/State/RadrootsConfigurationStore.swift - - path: Radroots/State/RadrootsAddStore.swift - - path: Radroots/State/RadrootsIdentityStore.swift - - path: Radroots/State/RadrootsSessionStore.swift - - path: Radroots/State/RadrootsSupportingStores.swift - - path: Radroots/State/RadrootsTodayStore.swift - - path: Radroots/Views/RadrootsTodayView.swift - - path: Radroots/Views/RadrootsAddView.swift - - path: Radroots/Views/RadrootsSupportingViews.swift - - path: Radroots/Views/RuntimeStatusView.swift - - path: Radroots/Resources + - path: Radroots/Resources/Assets.xcassets + - path: Radroots/Resources/PrivacyInfo.xcprivacy settings: base: "EXCLUDED_ARCHS[sdk=iphonesimulator*]": x86_64 @@ -66,15 +33,12 @@ targets: Debug: Radroots/Config/Debug.xcconfig Release: Radroots/Config/Release.xcconfig dependencies: - - target: RadrootsKitBindings - embed: true - - package: RadrootsKit + - package: RadrootsApp preBuildScripts: - - name: Generate git SHA xcconfig + - name: Verify installed FFI artifacts basedOnDependencyAnalysis: false script: | - GIT_SHA=$(git rev-parse --short HEAD || echo "unknown") - echo "GIT_SHA = $GIT_SHA" > "$SRCROOT/Radroots/radroots.git.xcconfig" + "$SRCROOT/RadrootsFFI/scripts/verify-installed-artifacts.sh" RadrootsTests: type: bundle.unit-test platform: iOS @@ -94,6 +58,8 @@ targets: GENERATE_INFOPLIST_FILE: true dependencies: - target: Radroots + - package: RadrootsApp + - package: RadrootsKit RadrootsUITests: type: bundle.ui-testing platform: iOS diff --git a/scripts/app-api-snapshot.sh b/scripts/app-api-snapshot.sh @@ -0,0 +1,59 @@ +#!/bin/bash +set -euo pipefail + +operation=${1:-} +case "$operation" in + write|check) ;; + *) echo "usage: $0 {write|check}" >&2; exit 64 ;; +esac + +for variable in XCODE_DERIVED_DATA XCODE_SOURCE_PACKAGES; do + if [[ -z "${!variable:-}" ]]; then + echo "error: $variable is required; run this command through cargo extbuild" >&2 + exit 1 + fi +done + +repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) +work_dir="$XCODE_DERIVED_DATA/radroots-app-api" +symbol_dir="$work_dir/symbols" +rendered="$work_dir/RadrootsApp.symbols.json" +snapshot="$repo_root/api/RadrootsApp.symbols.json" +products="$XCODE_DERIVED_DATA/Build/Products/Debug-iphonesimulator" +generated_module_maps="$XCODE_DERIVED_DATA/Build/Intermediates.noindex/GeneratedModuleMaps-iphonesimulator" +secp_headers="$XCODE_SOURCE_PACKAGES/checkouts/swift-secp256k1/Sources/libsecp256k1/include" +ffi_headers="$repo_root/Radroots/Frameworks/RadrootsFFI.xcframework/ios-arm64-simulator/Headers" +sdk=$(xcrun --sdk iphonesimulator --show-sdk-path) + +rm -rf "$work_dir" +mkdir -p "$symbol_dir" +xcrun swift-symbolgraph-extract \ + -module-name RadrootsApp \ + -target arm64-apple-ios18.0-simulator \ + -sdk "$sdk" \ + -I "$products" \ + -I "$ffi_headers" \ + -F "$products" \ + -Xcc "-fmodule-map-file=$generated_module_maps/libsecp256k1.modulemap" \ + -Xcc "-I$secp_headers" \ + -minimum-access-level public \ + -skip-inherited-docs \ + -skip-synthesized-members \ + -output-dir "$symbol_dir" + +jq -cS \ + '{schema:"radroots.swift-api-snapshot.v1",module:.module,symbols:(.symbols | map({kind:.kind.identifier,precise:.identifier.precise,path:.pathComponents,access:.accessLevel,declaration:.declarationFragments}) | sort_by(.precise)),relationships:(.relationships | map({kind,source,target}) | sort_by(.kind,.source,.target))}' \ + "$symbol_dir/RadrootsApp.symbols.json" > "$rendered" + +if [[ "$operation" == write ]]; then + mkdir -p "$(dirname -- "$snapshot")" + cp "$rendered" "$snapshot" +else + if [[ ! -f "$snapshot" ]]; then + echo "error: missing public API snapshot; run 'make api-snapshot-write'" >&2 + exit 1 + fi + cmp "$rendered" "$snapshot" +fi + +echo "RadrootsApp public API snapshot $operation succeeded" diff --git a/scripts/generate-project.sh b/scripts/generate-project.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -eu + +project_file=Radroots.xcodeproj/project.pbxproj +localizations_id=A11CE10CA11A710A00000001 + +xcodegen generate --spec project.yml + +temporary_count=$(grep -Ec '"TEMP_[0-9A-F-]+" /\* Localizations \*/' "$project_file" || true) +case "$temporary_count" in + 0) ;; + 1) + perl -0pi -e \ + 's/"TEMP_[0-9A-F-]+"( \/\* Localizations \*\/)/A11CE10CA11A710A00000001$1/g' \ + "$project_file" + ;; + *) + echo "error: unexpected XcodeGen Localizations group inventory" >&2 + exit 1 + ;; +esac + +if grep -Eq '"TEMP_[0-9A-F-]+" /\* Localizations \*/' "$project_file"; then + echo "error: XcodeGen temporary Localizations identifier remains" >&2 + exit 1 +fi +deterministic_count=$(grep -Ec "$localizations_id /\* Localizations \*/" "$project_file" || true) +if [ "$deterministic_count" -gt 1 ]; then + echo "error: deterministic Localizations identifier is duplicated" >&2 + exit 1 +fi diff --git a/scripts/swift-package.sh b/scripts/swift-package.sh @@ -0,0 +1,28 @@ +#!/bin/sh +set -eu + +for variable in SWIFTPM_SCRATCH SWIFTPM_CACHE; do + case "$variable" in + SWIFTPM_SCRATCH) value=${SWIFTPM_SCRATCH:-} ;; + SWIFTPM_CACHE) value=${SWIFTPM_CACHE:-} ;; + esac + if [ -z "$value" ]; then + echo "error: $variable is required; run this command through cargo extbuild" >&2 + exit 1 + fi +done + +operation=${1:-} +case "$operation" in + resolve) + exec swift package \ + --scratch-path "$SWIFTPM_SCRATCH" \ + --cache-path "$SWIFTPM_CACHE" \ + --disable-sandbox \ + resolve + ;; + *) + echo "usage: $0 resolve" >&2 + exit 64 + ;; +esac diff --git a/scripts/verify-package-contract.sh b/scripts/verify-package-contract.sh @@ -0,0 +1,67 @@ +#!/bin/sh +set -eu + +repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) +package="$repo_root/Package.swift" +source_lock="$repo_root/RadrootsFFI/source.lock" +consumer_lock="$repo_root/radroots.lib.source-lock.v1.toml" + +make_value() { + key=$1 + awk -v key="$key" '$2 == key && $3 == ":=" { print $4 }' "$source_lock" +} + +release_version=$(make_value RADROOTS_FIELD_FFI_CRATE_VERSION) +lib_revision=$(make_value RADROOTS_FIELD_LIB_GIT_REV) +apple_revision=$(sed -n '/url: "https:\/\/github.com\/radrootslabs\/apple_kit.git"/{n;s/.*revision: "\([0-9a-f]*\)".*/\1/p;}' "$package") + +if ! printf '%s\n' "$apple_revision" | grep -Eq '^[0-9a-f]{40}$'; then + echo "error: apple_kit must use an exact 40-character Git revision" >&2 + exit 1 +fi + +grep -Fq "repository = \"https://github.com/radrootslabs/lib\"" "$consumer_lock" +grep -Fq "revision = \"$lib_revision\"" "$consumer_lock" +grep -Fq "version = \"$release_version\"" "$consumer_lock" +grep -Fq "public static let version = \"$release_version\"" "$repo_root/Radroots/App/AppEntry.swift" +grep -Fq "revision: $apple_revision" "$repo_root/project.yml" +grep -Fq "XCTAssertEqual(RadrootsAppRelease.version, \"$release_version\")" \ + "$repo_root/RadrootsPublicAPITests/RadrootsAppPublicAPITests.swift" +grep -Fq "NSPrivacyAccessedAPICategoryUserDefaults" \ + "$repo_root/Radroots/Resources/PrivacyInfo.xcprivacy" +grep -Fq "CA92.1" "$repo_root/Radroots/Resources/PrivacyInfo.xcprivacy" +plutil -lint "$repo_root/Radroots/Resources/PrivacyInfo.xcprivacy" >/dev/null + +for resolved in \ + "$repo_root/Package.resolved" \ + "$repo_root/Radroots.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" +do + if [ ! -f "$resolved" ]; then + echo "error: missing Swift package lock: $resolved" >&2 + exit 1 + fi + jq -e --arg apple_revision "$apple_revision" ' + (.version == 3) and + (.pins | length == 2) and + (all(.pins[]; + (.kind == "remoteSourceControl") and + (.location | startswith("https://")) and + (.state.revision | test("^[0-9a-f]{40}$")))) and + (any(.pins[]; + .location == "https://github.com/radrootslabs/apple_kit.git" and + .state.revision == $apple_revision)) and + (any(.pins[]; + .location == "https://github.com/21-DOT-DEV/swift-secp256k1.git" and + .state.revision == "e70a10e036a55fffea31568f0af92d69b6d449cd")) + ' "$resolved" >/dev/null +done + +package_pins=$(jq -cS '.pins | sort_by(.identity)' "$repo_root/Package.resolved") +project_pins=$(jq -cS '.pins | sort_by(.identity)' \ + "$repo_root/Radroots.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved") +if [ "$package_pins" != "$project_pins" ]; then + echo "error: Swift package and Xcode project locks disagree" >&2 + exit 1 +fi + +echo "package contracts agree at $release_version; apple_kit@$apple_revision" diff --git a/scripts/xcode.sh b/scripts/xcode.sh @@ -0,0 +1,85 @@ +#!/bin/bash +set -euo pipefail + +for variable in XCODE_DERIVED_DATA XCODE_SOURCE_PACKAGES XCODE_PACKAGE_CACHE; do + if [[ -z "${!variable:-}" ]]; then + echo "error: $variable is required; run this command through cargo extbuild" >&2 + exit 1 + fi +done + +output_args=( + -quiet + -derivedDataPath "$XCODE_DERIVED_DATA" + -clonedSourcePackagesDirPath "$XCODE_SOURCE_PACKAGES" + -packageCachePath "$XCODE_PACKAGE_CACHE" +) +offline_args=( + -disableAutomaticPackageResolution + -onlyUsePackageVersionsFromResolvedFile + -skipPackagePluginValidation +) + +operation=${1:-} +case "$operation" in + resolve) + exec xcodebuild \ + -resolvePackageDependencies \ + -project Radroots.xcodeproj \ + -scheme Radroots \ + "${output_args[@]}" + ;; + package-build) + exec xcodebuild \ + -scheme RadrootsApp \ + -destination 'generic/platform=iOS Simulator' \ + "${output_args[@]}" \ + "${offline_args[@]}" \ + ARCHS=arm64 \ + build + ;; + package-test) + destination=${2:?package-test requires a simulator destination} + exec xcodebuild \ + -scheme RadrootsAppPublicAPITests \ + -destination "$destination" \ + "${output_args[@]}" \ + "${offline_args[@]}" \ + test + ;; + project-build) + configuration=${2:?project-build requires a configuration} + case "$configuration" in + Debug|Release) ;; + *) echo "error: unsupported configuration: $configuration" >&2; exit 1 ;; + esac + exec xcodebuild \ + -project Radroots.xcodeproj \ + -scheme Radroots \ + -configuration "$configuration" \ + -destination 'generic/platform=iOS Simulator' \ + "${output_args[@]}" \ + "${offline_args[@]}" \ + build + ;; + project-test) + destination=${2:?project-test requires a simulator destination} + test_target=${3:?project-test requires a test target} + case "$test_target" in + RadrootsTests|RadrootsUITests) ;; + *) echo "error: unsupported test target: $test_target" >&2; exit 1 ;; + esac + exec xcodebuild \ + -project Radroots.xcodeproj \ + -scheme Radroots \ + -destination "$destination" \ + "${output_args[@]}" \ + "${offline_args[@]}" \ + "-only-testing:$test_target" \ + test + ;; + *) + echo "usage: $0 {resolve|package-build|package-test|project-build|project-test}" >&2 + exit 64 + ;; +esac