Skip to main content

Coredata With Cloudkit

With iOS 13 we'll get new cool feature - coredata db with cloudkit backup. Exactly what I need. 
Bad news is that my old device will not get the update.

My doctor said that I should track food I eat. What I should do is divide my day into 2-3 hour windows and eat a small meal in every one of them. And write it down and then show it to the doctor so we can talk about what I should and what I should not eat. 
I did the tracking for few weeks in excel sheet but an app would be so much convenient!

I thought about simple logger in iOS and a website to generate reports. This is the place where merged cloudkit and coredata would help a lot. It turns out manually adding cloud backup is really easy. Here is the quick doc of the changes required:


// before... usual stuff
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 []
        }
    }
}



First of all, I have renamed Repository to DataRepository and created CloudRepository and a new Repository class.
Cloud repository looks like this


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)
                }
            }
        }
    }
}

And the new Repository (wchich has the same interface as current DataRepository so I don't have to change other source files) is:

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()
            
            // part below is made so the items I have added before writing cloud sync 
            //would be synced automatically
            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 []
        }
    }
}

Quite simple, working fine. That makes my app minimal but functionable enough to build on it. I'm going to continue with website part to generate reports first, and then continue with polishing my app.

Comments