From d2bdf4f5045d4dffba0040d49eb3bbdd2d0c4f31 Mon Sep 17 00:00:00 2001 From: Cameron Ingham Date: Mon, 15 Sep 2025 19:53:03 -0700 Subject: [PATCH 1/2] Enable automatic code signing in Xcode --- Loop.xcodeproj/project.pbxproj | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 412ad40359..81f67b0162 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -4500,7 +4500,9 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = LoopCore/Info.plist; @@ -4526,7 +4528,9 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ""; INFOPLIST_FILE = LoopCore/Info.plist; @@ -4552,7 +4556,9 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = LoopCore/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4579,7 +4585,9 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_IDENTITY = "Apple Development"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = LoopCore/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4696,7 +4704,9 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = "Apple Development"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = LoopUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -4721,7 +4731,9 @@ isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = "Apple Development"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = "$(LOOP_DEVELOPMENT_TEAM)"; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = LoopUI/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; From 2ab2506fa1a65f8bb2b976c9bb4c06b4b6ea5189 Mon Sep 17 00:00:00 2001 From: Cameron Ingham Date: Wed, 29 Jul 2026 15:46:39 -0700 Subject: [PATCH 2/2] Docs --- Documentation/xcode-cloud.md | 97 ++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 Documentation/xcode-cloud.md diff --git a/Documentation/xcode-cloud.md b/Documentation/xcode-cloud.md new file mode 100644 index 0000000000..2b6cea80d5 --- /dev/null +++ b/Documentation/xcode-cloud.md @@ -0,0 +1,97 @@ +# Building Loop with Xcode Cloud + +An alternative to the GitHub Actions "browser build" and the local Mac/Xcode build. Xcode Cloud is Apple's hosted CI service: it clones your fork of `LoopWorkspace`, builds and signs the archive using certificates Apple manages for you, and delivers the result to TestFlight. No self-managed signing certificates, no repository secrets, no `fastlane match`, no personal access tokens. + +--- + +### Prerequisites + +- A paid **Apple Developer Program** membership. Xcode Cloud is not available on a free account. +- A **Mac with Xcode** (a current release). You need it once, to create the first workflow; after that, builds run on Apple's infrastructure and you can trigger and monitor them from App Store Connect or the iOS/iPadOS App Store Connect app. +- A **fork of `LoopWorkspace`** on GitHub that you can push to. +- **Account Holder or Admin** role in App Store Connect, to enable Xcode Cloud the first time. +- Apple includes 25 compute hours per month with the developer program at no extra cost. A full Loop archive typically consumes a meaningful fraction of an hour, so this is comfortable for personal use but not unlimited. Check current allowances in App Store Connect. + +--- + +### Step 1 — Fork and clone + +Fork `LoopKit/LoopWorkspace` to your own account, then clone your fork with submodules: + +```bash +git clone --branch=dev --recurse-submodules https://github.com//LoopWorkspace +cd LoopWorkspace +xed . +``` + +Xcode Cloud builds from the remote repository, not from your working copy, so anything the build needs has to be committed and pushed to your fork. + +### Step 2 — Set your development team + +Select `LoopConfigOverride.xcconfig` in Xcode's project navigator (it's in the root of the workspace) and uncomment the last line, replacing the value with your own Team ID from [developer.apple.com](https://developer.apple.com): + +``` +// Put your team id here for signing +LOOP_DEVELOPMENT_TEAM = ABCDE12345 +``` + +Then commit and push it: + +```bash +git add LoopConfigOverride.xcconfig +git commit -m "Set development team" +git push +``` + +This is the only required edit. Everything else — bundle identifiers, entitlements, app groups, signing style — is derived from it. `Loop.xcconfig` builds the bundle ID as `com.${DEVELOPMENT_TEAM}.loopkit`, so your app and all of its extensions get identifiers that are unique to your team automatically. + +### Step 3 — Build locally once + +Select the **LoopWorkspace** scheme (not the `Loop` scheme) and build to a real device. + +This is not strictly part of Xcode Cloud setup, but it's the cheapest way to get Xcode's automatic signing to register your App IDs, the `group.com..loopkit.LoopGroup` app group, and the rest of the capabilities in the developer portal. Doing it locally surfaces identifier and entitlement problems in seconds instead of in a cloud build several minutes long. + +### Step 4 — Create the workflow + +In Xcode, choose **Integrate → Create Workflow** (or **Product → Xcode Cloud → Create Workflow**). + +1. **Product:** pick the Loop app from the workspace. +2. **Grant source access:** connect your GitHub account and authorize your `LoopWorkspace` fork. +3. **Workflow settings:** + - **Scheme:** `LoopWorkspace`. It's a shared scheme in the repository, so Xcode Cloud can see it. + - **Environment:** pick an Xcode version that can build the branch you're on. + - **Start Conditions:** for a personal build, "Manual" or "Branch Changes" on your working branch is usually what you want. Delete the default "Pull Request Changes" condition unless you actually want PRs building against your quota. + - **Actions:** an **Archive** action with distribution preparation set to + - **TestFlight (Internal Testing Only)**. Remove the Build or Test actions if you don't want to spend compute hours on them. + - **Post-Actions:** TestFlight internal testing, with yourself in the group. + +If the TestFlight option doesn't appear in the initial wizard, create an Archive-only workflow first, run one build, then edit the workflow and add it. + +4. **App record:** Xcode Cloud will offer to register the bundle identifier and create the App Store Connect record if one doesn't exist. Let it. + +### Step 5 — Start a build + +Trigger the workflow from Xcode's Cloud tab, from App Store Connect, or by pushing to the branch you configured. Logs stream live. On success the build shows up under TestFlight within a few minutes, and you install it from the TestFlight app on your phone. + +--- + +## Keeping current + +Xcode Cloud has no equivalent of the browser build's "keep alive" and auto-update jobs, so staying current is on you: + +- **New Loop releases:** merge upstream `LoopKit/LoopWorkspace` into your fork. If your workflow starts on branch changes, the merge itself triggers the build. +- **TestFlight expiry:** internal TestFlight builds expire 90 days after upload. Add a **Scheduled** start condition (for example, monthly) to produce a fresh build off the same code and reset the clock. A scheduled build costs compute hours, so pick an interval, not a nightly cron. +--- + +## How this compares to the browser build + +| | GitHub Actions (browser build) | Xcode Cloud | +| --- | --- | --- | +| Mac required | No | Yes, for initial workflow creation | +| Signing | `fastlane match`, certificates in a private repo you manage | Managed by Apple | +| Secrets to configure | Team ID, App Store Connect API key, match password, PAT | Team ID only | +| Cost | Free tier of GitHub Actions | 25 compute hours/month included with the developer program | +| Auto-update / keep-alive | Built into the workflows | Manual merges, or a scheduled start condition | +| Where builds land | TestFlight | TestFlight | + +Both paths require a paid Apple Developer Program membership. Neither replaces the other — the point of the code change is that all three options (browser build, local Xcode build, Xcode Cloud) remain available from the same source tree.