Skip to content

Commit 2f95239

Browse files
authored
fix(ios): pin iCloud Sync to the Production CloudKit environment (#1632)
1 parent c023f4e commit 2f95239

3 files changed

Lines changed: 59 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- iCloud Sync between the iPhone and Mac apps: the iOS app now uses the Production CloudKit environment, so a development build no longer syncs into a separate database the Mac never reads.
13+
1014
## [0.50.0] - 2026-06-09
1115

1216
### Added

TableProMobile/TableProMobile/TableProMobileRelease.entitlements

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
<array>
1111
<string>CloudKit</string>
1212
</array>
13+
<key>com.apple.developer.icloud-container-environment</key>
14+
<string>Production</string>
1315
<key>com.apple.security.application-groups</key>
1416
<array>
1517
<string>group.com.TablePro.TableProMobile</string>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// EntitlementsEnvironmentParityTests.swift
3+
// TableProTests
4+
//
5+
// Guards the CloudKit environment pin. The Mac and iOS apps must both target
6+
// the Production environment, or a development iOS build talks to the
7+
// Development database while the Mac talks to Production and sync silently
8+
// moves nothing across devices.
9+
//
10+
11+
import Foundation
12+
import Testing
13+
14+
@Suite("CloudKit environment entitlement parity")
15+
struct EntitlementsEnvironmentParityTests {
16+
private static let environmentKey = "com.apple.developer.icloud-container-environment"
17+
private static let macEntitlements = "TablePro/TablePro.entitlements"
18+
private static let iosEntitlements = "TableProMobile/TableProMobile/TableProMobileRelease.entitlements"
19+
20+
@Test("Mac app pins the Production CloudKit environment")
21+
func macTargetsProduction() throws {
22+
try #expect(environment(in: Self.macEntitlements) == "Production")
23+
}
24+
25+
@Test("iOS app pins the Production CloudKit environment")
26+
func iosTargetsProduction() throws {
27+
try #expect(environment(in: Self.iosEntitlements) == "Production")
28+
}
29+
30+
private func environment(in relativePath: String) throws -> String? {
31+
let url = try repoRoot().appendingPathComponent(relativePath)
32+
let data = try Data(contentsOf: url)
33+
let plist = try PropertyListSerialization.propertyList(from: data, format: nil)
34+
let entitlements = try #require(plist as? [String: Any], "Entitlements at \(relativePath) is not a dictionary")
35+
return entitlements[Self.environmentKey] as? String
36+
}
37+
38+
private func repoRoot(file: StaticString = #filePath) throws -> URL {
39+
var directory = URL(fileURLWithPath: "\(file)").deletingLastPathComponent()
40+
while directory.path != "/" {
41+
let marker = directory.appendingPathComponent("TablePro.xcodeproj")
42+
if FileManager.default.fileExists(atPath: marker.path) {
43+
return directory
44+
}
45+
directory = directory.deletingLastPathComponent()
46+
}
47+
throw EntitlementsParityError.repoRootNotFound
48+
}
49+
50+
private enum EntitlementsParityError: Error {
51+
case repoRootNotFound
52+
}
53+
}

0 commit comments

Comments
 (0)