Build iOS App / Build .ipa and publish source (push) Successful in 31s
Sideloaded apps can't use APNs (no push entitlement without Apple signing), so deliver notifications via an iOS BGAppRefreshTask that polls the monitored repos every ~15 min and fires local notifications (no Apple push needed): - new PRs that appear in the open set - CI status changes on existing PRs Persistence: NotificationState tracks announced PR ids + last-known CI status so we only fire on actual changes, not every poll. AppDelegate registers the background task and reschedules the next one. Info.plist gets UIBackgroundModes fetch + the BGTask identifier. Settings gains a Notifications toggle; also poll after a foreground inbox load so state seeds and changes are caught promptly. Bump to 3.6.
31 lines
1.1 KiB
Swift
31 lines
1.1 KiB
Swift
import Foundation
|
|
|
|
/// Persisted "what we've already notified about", so the background poller
|
|
/// only fires a notification when something actually changes:
|
|
/// - a PR that wasn't there before → "new PR"
|
|
/// - a PR whose CI aggregate status changed → "CI status changed"
|
|
struct NotificationState: Codable {
|
|
/// PR ids (repo.fullName#number) we've already announced as new.
|
|
var announcedNewPRs: Set<String> = []
|
|
/// PR id → last notified CI status string ("success"/"failure"/"running"/etc).
|
|
var ciStatusByPR: [String: String] = [:]
|
|
|
|
private static let url: URL = {
|
|
let dir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
|
return dir.appendingPathComponent("notification-state.json")
|
|
}()
|
|
|
|
func save() {
|
|
guard let data = try? JSONEncoder().encode(self) else { return }
|
|
try? data.write(to: Self.url)
|
|
}
|
|
|
|
static func load() -> NotificationState {
|
|
guard let data = try? Data(contentsOf: url),
|
|
let state = try? JSONDecoder().decode(NotificationState.self, from: data) else {
|
|
return NotificationState()
|
|
}
|
|
return state
|
|
}
|
|
}
|