Skip to content

Instantly share code, notes, and snippets.

@notoroid
Last active December 22, 2021 10:45
Show Gist options
  • Save notoroid/4da1c50d7f578fc04ea389d7659929fe to your computer and use it in GitHub Desktop.
Save notoroid/4da1c50d7f578fc04ea389d7659929fe to your computer and use it in GitHub Desktop.
watchOS8.0 interval background task sample.
import SwiftUI
struct RandomFox: Equatable, Codable { let image: String; let link: String }
class ExtensionDelegate: NSObject, ObservableObject, WKExtensionDelegate {
var foregroundDataTask: URLSessionDataTask?; var backgroundDataTask: URLSessionDataTask?
var timer: Timer?
let url = URL(string: "https://randomfox.ca/floof/")!
@Published var randomFox: RandomFox? = nil
// utilities
func taskRandomFox(completionHandler: @escaping (Error?) -> Void) -> URLSessionDataTask { // DataTask create function
DispatchQueue.main.async { self.randomFox = nil }
let dataTask = URLSession.shared.dataTask(with: URLRequest( url: url, cachePolicy: .reloadIgnoringLocalCacheData), completionHandler: { [unowned self] jsonData, response, error in
guard error == nil, let jsonData = jsonData else { completionHandler(error); return }
DispatchQueue.main.async {
self.randomFox = try? JSONDecoder().decode(RandomFox.self, from: jsonData)
completionHandler(nil)
}
})
return dataTask
}
func systemDateFormatter(_ dateFormat: String ) -> DateFormatter { // system date formatter for UTC + 00:00:00
let dateFormatter = DateFormatter();dateFormatter.locale = NSLocale.system; dateFormatter.dateFormat = dateFormat; return dateFormatter
}
lazy var dateFormatterMinute: DateFormatter = { return systemDateFormatter("m") }()
lazy var dateFormatterSecond: DateFormatter = { return systemDateFormatter("ss") }()
func ceil13MinitesTimeInterval(_ date: Date) -> TimeInterval {
let ref = Int(dateFormatterMinute.string(from: date)) ?? 0
let second = Double(dateFormatterSecond.string(from: date)) ?? 0.0
let diff = 5 - (ref % 5)
let newDate = date.addingTimeInterval(60 * Double(diff) - second)
let timeinterval = newDate.timeIntervalSince(date)
return timeinterval
}
func scheduleBackgroundRefreshTasks(_ timeInterval: TimeInterval) {
let targetDate = Date().addingTimeInterval(timeInterval)
WKExtension.shared().scheduleBackgroundRefresh(withPreferredDate: targetDate, userInfo: nil) { (error) in
if let error = error {
print("*** An background refresh error occurred: \(error.localizedDescription) ***")
return
}
}
}
// Extension Delegate methods
func applicationDidFinishLaunching() {
foregroundDataTask = taskRandomFox(completionHandler: { _ in })
foregroundDataTask?.resume()
}
func applicationDidBecomeActive() {
backgroundDataTask = nil
timer?.invalidate()
timer = Timer.scheduledTimer(timeInterval: ceil13MinitesTimeInterval(Date()), target: self, selector: #selector(Self.onTimer), userInfo: nil, repeats: false)
}
@objc func onTimer() {
self.timer = Timer.scheduledTimer(timeInterval: ceil13MinitesTimeInterval(Date()), target: self, selector: #selector(Self.onTimer), userInfo: nil, repeats: false)
foregroundDataTask = taskRandomFox(completionHandler: { _ in })
foregroundDataTask?.resume()
}
func applicationWillResignActive() {
timer?.invalidate()
foregroundDataTask = nil
}
func applicationDidEnterBackground() {
backgroundDataTask = nil
let timeInterval = ceil13MinitesTimeInterval(Date())
scheduleBackgroundRefreshTasks(timeInterval)
}
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
switch task {
case let backgroundTask as WKApplicationRefreshBackgroundTask:
backgroundDataTask = taskRandomFox(completionHandler: { [unowned self] _ in
self.scheduleBackgroundRefreshTasks(ceil13MinitesTimeInterval(Date()))
backgroundTask.setTaskCompletedWithSnapshot(false)
self.backgroundDataTask = nil
})
backgroundDataTask?.resume()
default:
task.setTaskCompletedWithSnapshot(false)
}
}
}
}
// SwiftUI - content view / app
struct ContentView: View {
@StateObject var extensionDelegate: ExtensionDelegate
var body: some View {
if let randomFox = extensionDelegate.randomFox {
AsyncImage(url: URL(string: randomFox.image)) { image in
image.resizable().aspectRatio(nil, contentMode: .fill).ignoresSafeArea()
} placeholder: { ProgressView() }
} else { Text("🦊").font(.largeTitle).padding() }
}
}
@main
struct WatchOSIntervalUpdateApp: App {
@WKExtensionDelegateAdaptor(ExtensionDelegate.self) var extensionDelegate
var body: some Scene { WindowGroup { ContentView(extensionDelegate: extensionDelegate)} }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment