Przejdź do głównej zawartości

Coredata i Cloudkit

Z iOS 13 dostaniemy nową fajną rzecz: bazę coredata z backupem do cloudkit. Dokładnie to, czego potrzebuję.
Pech, że moje stare urządzenie nie dostanie tego updatu.

Mój lekarz powiedział, że powinienem zapisywać co jem. Dokładniej - powinienem podzielić mój dzień na przedziały 2-3 godzinne i jeśc mały posiłek w każdym z nich. I zapisywać co jem i pokazać to lekarzowi kiedy się spotkamy żebyśmy mogli porozmawiać o tym co pownieniem jeść, a czego nie. Przez kilka tygodni zapisywałem wszystko w arkuszu Excela ale apka byłaby dużo wygodniejsza!

Myślałem o prostym loggerze na iOS i stronce do generowania raportów. W tym miejscu przydałby się ten bajer z backupem coredata do cloudkit. Okazało się, że zrobienie tego ręcznie też nie jest bardzo pracochłonne. Oto jakie zmiany były potrzebne:


// przed... 
class Repository {
    func createLogItem() -> LogItem {
        return LogItem( context: context!)
    }
    func deleteItem(_ item: LogItem) {
        context?.delete(item)
    }
    func saveChanges() {
        save()
    }
     func getItems() -> [LogItem] {
        do
        {
            let fetchRequest = NSFetchRequest(entityName: "LogItem")
            let sort = NSSortDescriptor(key: "date", ascending: false)
            fetchRequest.sortDescriptors = [sort]
            let items = try context?.fetch(fetchRequest) as! [LogItem]
            
            return items
        }
        catch
        {
            print(error)
            return []
        }
    }
}

Przede wszystkim zmieniłem nazwę klasy Repository na DataRepository i stworzyłem CloudRepository i nowe Repository.
CloudRepository wygląda tak:


class CloudRepository {
    func addLogItem(_ item: LogItem ,callback: @escaping (CKRecord?, Error?) -> Void)
    {
        let container = CKContainer.default()
        let db = container.privateCloudDatabase
        let record =  item.recordId == nil ? 
                            CKRecord(recordType: "LogItem") : 
                            CKRecord(recordType: "LogItem", 
                                recordID: CKRecord.ID(recordName: item.recordId!))
        record["date"] = item.date
        record["text"] = item.text
        if let category = item.category?.recordId {
            record["category"] = CKRecord.Reference(recordID: 
                                    CKRecord.ID(recordName: category), 
                                    action: CKRecord_Reference_Action.deleteSelf)
        }        
        db.save(record) { (record, error) in
            if let e = error
            {
                print (e)
            }
            DispatchQueue.main.async {
                callback(record, error)
            }
        }
    }

    func deleteLogItem(_ item: LogItem) {
        if let rName = item.recordId {
            let container = CKContainer.default()
            let db = container.privateCloudDatabase
            db.delete(withRecordID: CKRecord.ID(recordName: rName)) { (id, err) in
                if let err = err {
                    print (err)
                }
            }
        }
    }
}

A nowe Repository (które ma ten sam interfejs jak DataRepository dzięki czemu nie muszę zmieniać innych plików źródłowych) wygląda tak::

class Repository
{
    let cloud = CloudRepository()
    let db = DataRepository()
    
    func createLogItem() -> LogItem {
        return db.createLogItem()
    }
    func deleteItem(_ item: LogItem) {
        cloud.deleteLogItem(item)
        db.deleteItem(item)
    }
    func saveChanges() {
        db.saveChanges()
    }
    func addItem(_ item: LogItem)
    {
        cloud.addLogItem(item){ cloudItem , error in
            if let rn = cloudItem?.recordID.recordName {
                item.recordId = rn
            }
            self.db.addItem(item)
        }
    }
    func getItems() -> [LogItem] {
        do
        {
            let items = db.getItems()
            
            // częśc poniżej sprawi, że elementy stworzone zanim dodałem cloudkita
            // też się zsynchronizują
            for item in items {
                if item.recordId == nil {
                    cloud.addLogItem(item) { cloudItem, error in
                        item.recordId = cloudItem?.recordID.recordName
                        self.db.saveChanges()
                    }
                }
            }
            return items
        }
        catch
        {
            print(error)
            return []
        }
    }
}

Całkiem proste, działa dobrze. I sprawia, że moja apka pozostaje mała ale na tyle funkcjonalna, że można ją rozwijać. Teraz stworzę stronę do raportów, a kiedy skończę - powrócę do aplikacji, żeby ją rozwijać (i wypolerować).

Komentarze