CoreData with background task

前言

雖然在 iOS 上開發了幾年的時間,但一直到最近才開始使用 CoreData;之前在第一份工作的專案之中是使用 FMDB 來處理資料存取,而後續則是用了 Realm。

選擇的原因分別是當時 FMDB 的速度較其餘兩者快速,而後來看上 Realm 的跨平台特色,不過近期開發的感想是能以原生為主的話,就儘量降低對於第三方套件的依賴性。

這篇文章會記錄些什麼

其實這篇文章並不會從頭到尾寫下教學,而把重點放在一些我踩到的雷上,像是⋯⋯

記得要附上 sqlite 的路徑

原先我的 persistentContainer 的產生方式如下

lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "OfflineWallet")
    let description = NSPersistentStoreDescription()
    description.shouldInferMappingModelAutomatically = true
    description.shouldMigrateStoreAutomatically = true
    container.persistentStoreDescriptions = [description]
    container.loadPersistentStores { _, error in
        if let error = error {
            fatalError("Unresolved error \(error), \(String(describing: error._userInfo))")
        }
    }
    return container
}()

在模擬器上存取了幾次,每次都有 save 且第二次進入畫面的時候,都可以 fetch 得到資料,但是只要重開 App 就會從頭來過⋯⋯

也就是說其實都只是像是存在 NSManagedObjectContext 上,而並沒有實際地轉成 sqlite。

補上指定的 URL 即可解決。

...
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let sqliteURL = documentsDirectoryURL?.appendingPathComponent("OfflineWallet.sqlite")
description.url = sqliteURL
...

performBackgroundTask v.s newBackgroundContext

要實作 backgroundTask 有兩種作法

NSPersistentContainer.performBackgroundTask

persistentContainer.performBackgroundTask { context in
    //do something in background thread
}

NSPersistentContainer 會建立一個 context 在這個 closure 裡頭使用,而重點是當這條 thread 結束之後,這個 context 所管理的物件(NSManagedObjectModel)的所有變數會釋放掉,也就是都會變成 nil。

適合用於取得資料並轉型成其他 class / structure 的時候使用。

newBackgroundContext

而如果你必須在後續的程式之中繼續使用 context 所產生的 NSManagedObjectModel 時,你就得要保存其 context;像是建立一個 backgroundContext 並存下來使用:

lazy var backgroundContext = persistentContainer.newBackgroundContext()

//you should use this context to do something what you want
backgroundContext.perform {
//do something like before
}

此時由於這個 context 並沒有被釋放掉,所以其 NSManagedObjectModel 的所有變數便也會持有著;而依然是在其他 thread 上進行,並不會佔據 main thread。

待續

分頁讀取

筆記一下 NSFetchRequest 有提供 fetchLimit 以及 fetchOffset 可以用來做分頁讀取的功能。

comments powered by Disqus