SeachVC(UIViewController)にJSONデータをダウンロードするための次の関数があります。これは完璧に機能します。
func downloadJSON(){
guard let url = URL(string: "myURL") else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let downloadedCurrencies = try JSONDecoder().decode([Currency].self, from: data)
// Adding downloaded data into Local Array
Currencies = downloadedCurrencies
} catch let jsonErr {
print("Here! Error serializing json", jsonErr)
}
}.resume()
}
バックグラウンドアプリの更新を実装するために、次の関数をAppDelegateに追加しました。
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Background App Refresh Config
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
return true
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let VC = window?.rootViewController as? SearchVC {
// Update JSON data
VC.downloadJSON()
completionHandler(.newData)
}
}
ただし、シミュレーターでバックグラウンドアプリの更新をシミュレートすると、次の警告が表示されます。
警告:アプリケーションデリゲートは-application:performFetchWithCompletionHandler:の呼び出しを受け取りましたが、完了ハンドラーは呼び出されませんでした。
完了ハンドラーを実装する場所と方法は?
ありがとうございました
ダウンロードコードをViewControllerから別のクラスに移動するか、少なくとも現在のバックグラウンド更新メソッドを変更して、必要に応じてViewControllerをインスタンス化する必要があります。アプリがフォアグラウンドで起動されていない場合、バックグラウンドリフレッシュがトリガーされる可能性があるため、if let
は失敗します。
質問のコードを検討してください。
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let VC = window?.rootViewController as? SearchVC {
// Update JSON data
VC.downloadJSON()
completionHandler(.newData)
}
}
if let...
が渡されない場合は、completionHandler
を呼び出さずに関数を終了するため、完了ハンドラーが呼び出されなかったというruntime警告が表示されます。
completionHandler
の場合にelse
への呼び出しを含めるようにコードを変更できますが、この場合、フェッチは行われません。
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let VC = window?.rootViewController as? SearchVC {
// Update JSON data
VC.downloadJSON()
completionHandler(.newData)
} else {
completionHandler(.noData)
}
または、必要に応じてView Controllerをインスタンス化することもできます(または別のデータフェッチクラスをお勧めします)。
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
let vc = (window?.rootViewController as? SearchVC) ?? SearchVC()
// Update JSON data
vc.downloadJSON()
completionHandler(.newData)
}
また、downloadJSON
関数を変更して、JSONのダウンロードが完了したときに呼び出す完了ハンドラー引数を含める必要があります。これにより、実際にデータをダウンロードした後、バックグラウンドフェッチ完了ハンドラーを呼び出すことができます。
func downloadJSON(completion: ((Bool,Error?) -> Void )? = nil)) {
guard let url = URL(string: "myURL") else {
completion?(false, nil)
return
}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard nil == err else {
completion?(false, err)
return
}
guard let data = data else {
completion?(false, nil)
return
}
do {
let downloadedCurrencies = try JSONDecoder().decode([Currency].self, from: data)
// Adding downloaded data into Local Array
Currencies = downloadedCurrencies
completion(true,nil)
} catch let jsonErr {
print("Here! Error serializing json", jsonErr)
completion?(false,jsonErr)
}
}.resume()
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
let vc = (window?.rootViewController as? SearchVC) ?? SearchVC()
// Update JSON data
vc.downloadJSON() { (newData,error) in
if let err = error {
NSLog("Background fetch error: \(err.localizedDescription)")
completionHandler(.fail)
} else {
completionHandler(newData ? .newData:.noData)
}
}
}
2019年9月の更新
IOS 13では、新しいバックグラウンドフェッチおよび処理機能が導入されていることに注意してください。詳細については、これを参照してください WWDCセッション
これはおそらく、else-caseでcompletionHandler
を呼び出さないためです(これは決して発生しませんが、コンパイラーは認識しません)。
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let VC = window?.rootViewController as? SearchVC {
// Update JSON data
VC.downloadJSON()
completionHandler(.newData)
} else {
completionHandler(.failed)
}
}