The 0.1.0 DMG refused to launch with "The application Luma.app can't be opened." because the signed entitlements declared aps-environment but no provisioning profile was embedded to authorize it; amfid kills the process before main(). Split out Luma-Release.entitlements that asks for production (matching APNsRegistration.environment in release builds), wire a new APPLE_DEVELOPER_ID_PROFILE secret into CI, copy it into Contents/embedded.provisionprofile before codesigning, and verify after the fact that both the sealed entitlements and the profile itself say aps-environment=production so a future regression fails the build instead of silently shipping. Luma.entitlements stays with aps-environment=development for local Xcode dev and iOS builds.
52 lines
1.6 KiB
Swift
52 lines
1.6 KiB
Swift
import Foundation
|
|
|
|
/// Holds the APNs device token that `registerForRemoteNotifications()`
|
|
/// hands back via the app delegate, so `Engine` can forward it to the
|
|
/// portal when an authenticated session is established.
|
|
///
|
|
/// The token is per-installation, not per-user, so we keep a single
|
|
/// shared instance. `Engine` subscribes once and re-pushes on every
|
|
/// authenticated `+welcome` so a signed-in user always has this device's
|
|
/// APNs subscription on file.
|
|
@MainActor
|
|
public final class APNsRegistration {
|
|
public static let shared = APNsRegistration()
|
|
|
|
public private(set) var deviceToken: Data?
|
|
public private(set) var lastError: String?
|
|
|
|
private var observers: [(Data) -> Void] = []
|
|
|
|
private init() {}
|
|
|
|
public func setToken(_ data: Data) {
|
|
deviceToken = data
|
|
lastError = nil
|
|
for observer in observers { observer(data) }
|
|
}
|
|
|
|
public func setError(_ message: String) {
|
|
lastError = message
|
|
}
|
|
|
|
public func observe(_ handler: @escaping (Data) -> Void) {
|
|
observers.append(handler)
|
|
if let deviceToken { handler(deviceToken) }
|
|
}
|
|
|
|
public var deviceTokenHex: String? {
|
|
deviceToken?.map { String(format: "%02x", $0) }.joined()
|
|
}
|
|
|
|
/// Which APNs endpoint the token is valid against: sandbox for
|
|
/// debug builds (signed with `aps-environment = development`) and
|
|
/// production for release builds (`aps-environment = production`).
|
|
public static var environment: String {
|
|
#if DEBUG
|
|
return "sandbox"
|
|
#else
|
|
return "production"
|
|
#endif
|
|
}
|
|
}
|