Skip to content

Commit

Permalink
fix: Support background refresh on iOS (#344)
Browse files Browse the repository at this point in the history
This change adds background refresh to iOS (with a requested frequency
of 15m) to try to ensure the app is up to date on first launch. Ideally
this would be combined with work like #359 to ensure we're as
lightweight as we can be when performing updates.
  • Loading branch information
jbmorley authored Apr 18, 2024
1 parent 137ae72 commit ea97171
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 9 deletions.
6 changes: 6 additions & 0 deletions Builds.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
D8A1224A2BD131C5002BDF42 /* BuildsPreviewHostWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D8A122392BD131C4002BDF42 /* BuildsPreviewHostWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
D8A122502BD131E0002BDF42 /* BuildsCore in Frameworks */ = {isa = PBXBuildFile; productRef = D8A1224F2BD131E0002BDF42 /* BuildsCore */; };
D8A269B12BAA3B6800A0E19A /* ProgressSpinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A269B02BAA3B6800A0E19A /* ProgressSpinner.swift */; };
D8A4658C2BD1D0FF00CA2849 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A4658B2BD1D0FF00CA2849 /* AppDelegate.swift */; };
D8ADD98F2B9E9E2700D314F1 /* DetailsPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8ADD98E2B9E9E2700D314F1 /* DetailsPopover.swift */; };
D8ADD9912B9E9E5600D314F1 /* AnnotationList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8ADD9902B9E9E5600D314F1 /* AnnotationList.swift */; };
D8ADD9932B9EA06800D314F1 /* WorkflowJobsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8ADD9922B9EA06800D314F1 /* WorkflowJobsList.swift */; };
Expand Down Expand Up @@ -241,6 +242,7 @@
D8A122452BD131C5002BDF42 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D8A122472BD131C5002BDF42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D8A269B02BAA3B6800A0E19A /* ProgressSpinner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressSpinner.swift; sourceTree = "<group>"; };
D8A4658B2BD1D0FF00CA2849 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
D8ADD98E2B9E9E2700D314F1 /* DetailsPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsPopover.swift; sourceTree = "<group>"; };
D8ADD9902B9E9E5600D314F1 /* AnnotationList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnotationList.swift; sourceTree = "<group>"; };
D8ADD9922B9EA06800D314F1 /* WorkflowJobsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkflowJobsList.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -499,6 +501,7 @@
isa = PBXGroup;
children = (
D8D413F228045535001BA6C0 /* Action.swift */,
D8A4658B2BD1D0FF00CA2849 /* AppDelegate.swift */,
D8D413EE280454AA001BA6C0 /* ApplicationModel.swift */,
D850F4DA2BAA709800CE1796 /* Confirmable.swift */,
D850F4DC2BAA75F900CE1796 /* ConfirmableAction.swift */,
Expand Down Expand Up @@ -972,6 +975,7 @@
D850F4DB2BAA709800CE1796 /* Confirmable.swift in Sources */,
D866F250280350BC00FC6EBB /* PhoneSettingsView.swift in Sources */,
D854FD852BB615D500DD31DD /* WorkflowCommands.swift in Sources */,
D8A4658C2BD1D0FF00CA2849 /* AppDelegate.swift in Sources */,
D8CA0EF22B94110A00DAE758 /* TimeInterval.swift in Sources */,
D8B49EE82BB76D4D0076E273 /* StatusButton.swift in Sources */,
D86B1D302B9EA78000CA4E21 /* PopoverButton.swift in Sources */,
Expand Down Expand Up @@ -1051,6 +1055,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = QS82QFHKWB;
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
Expand Down Expand Up @@ -1093,6 +1098,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = QS82QFHKWB;
"DEVELOPMENT_TEAM[sdk=macosx*]" = QS82QFHKWB;
Expand Down
2 changes: 1 addition & 1 deletion Builds.xcodeproj/xcshareddata/xcschemes/Builds.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1520"
LastUpgradeVersion = "1530"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
19 changes: 11 additions & 8 deletions Builds/BuildsApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,39 @@ import Diligence
@main
struct BuildsApp: App {

var applicationModel: ApplicationModel!
#if os(iOS)
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
#else
@NSApplicationDelegateAdaptor private var appDelegate: AppDelegate
#endif

@MainActor init() {
applicationModel = ApplicationModel()
}

var body: some Scene {

MainWindow()
.commands {
WorkflowCommands()
AccountCommands(applicationModel: applicationModel)
LifecycleCommands(applicationModel: applicationModel)
AccountCommands(applicationModel: appDelegate.applicationModel)
LifecycleCommands(applicationModel: appDelegate.applicationModel)
InspectorCommands()
#if DEBUG
SummaryCommands(applicationModel: applicationModel)
SummaryCommands(applicationModel: appDelegate.applicationModel)
#endif
}
.environmentObject(applicationModel)
.environmentObject(appDelegate.applicationModel)

#if os(macOS)

About(Legal.contents)


WorkflowsWindow()
.environmentObject(applicationModel)
.environmentObject(appDelegate.applicationModel)

InfoWindow()
.environmentObject(applicationModel)
.environmentObject(appDelegate.applicationModel)

#endif

Expand Down
5 changes: 5 additions & 0 deletions Builds/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>uk.co.jbmorley.builds.tasks.refresh</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand Down Expand Up @@ -36,6 +40,7 @@
</dict>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
</dict>
Expand Down
74 changes: 74 additions & 0 deletions Builds/Models/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2022-2024 Jason Morley
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import BackgroundTasks
import SwiftUI

#if os(iOS)

class AppDelegate: NSObject, UIApplicationDelegate {

let applicationModel = ApplicationModel()

@MainActor func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {

// Background refresh handler.
BGTaskScheduler.shared.register(forTaskWithIdentifier: "uk.co.jbmorley.builds.tasks.refresh", using: nil) { task in
Task {
await self.applicationModel.refresh()
task.setTaskCompleted(success: true)
}
}

NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification,
object: nil,
queue: nil) { _ in
DispatchQueue.main.async { [weak self] in
self?.scheduleBackgroundRefresh()
}
}

return true
}

func scheduleBackgroundRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "uk.co.jbmorley.builds.tasks.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
do {
print("Scheduling background refresh...")
try BGTaskScheduler.shared.submit(request)
print("Success!")
} catch {
print("Could not schedule app refresh: \(error)")
}
}

}

#else

@MainActor class AppDelegate: NSObject, NSApplicationDelegate {

let applicationModel = ApplicationModel()

}

#endif

0 comments on commit ea97171

Please sign in to comment.