I'm building a native iOS app with Swift/Xcode that utilizes JOSN data from a 3rd party API. Everything is working fine but the API has restrictions on the number of calls you can make to the API each hour. So now I'm building a function intended to create a weekly database as a CK Asset in CloudKit which will update the JSON from the API every 6 hours. This way the data is still relatively current but also reduces the number of API calls to just 4 a day.
Note: The app is being tested in Production/TestFlight.
The function is working correctly when creating a new CKAsset to save as a new CKRecord in CloudKit. The function also correctly downloads and decodes the CKAsset from CloudKit for use in the app. <- And anyone is able to download this asset and it works just fine.
The issue: Whenever the function checks if the CKAsset is more than 6 hours old, it is supposed to let any user modify the CKRecord by downloading a newer JSON file and replacing it in the CKAsset of the CKRecord using the CKModifyRecordsOperation. The problem is, whenever another user tries to modify the record the app crashes.
Question: Why can't any other users using TestFlight modify the record? Am I wrong to use CKModifyRecordsOperation?
Any help or suggestions is greatly appreciated!
-------------------------------CODE/FUNC--------------------------------
func fetchWeeklyPlayersDB() {
let semaphore = DispatchSemaphore.init(value: 0)
let thisWeek = getCurrentWeekID()
let current = Date()
// fetch current week PlayersDB from CK Database
publicDB.fetch(withRecordID: CKRecord.ID(recordName:thisWeek + "_Players")) { record, error in
// process record
if let r = record { // we know the playersDB has data
let modified = r.modificationDate
let expirationDate = modified?.addingTimeInterval(6*60*60) // add 6 hours
// if CK DB expirationDate is less than now, use it
if expirationDate! > current {
// not outdated - just process
if let data = r["DB"] {
// decode the JSON and set it to rawData
let d = readCKAsset(asset: data as! CKAsset)
let result = try? JSONDecoder().decode(dfsAPIData.self, from: d as! Data)
rawData = result
semaphore.signal()
}
} else { // CK DB is more than 6 hours old, call api and overwite data
// call API
let apiData = callAPI(week: findWeek())
// encode the api data as NSData
let jsonEncoder = JSONEncoder()
do {
let jsonData = try jsonEncoder.encode(apiData)
// save data locally
if let path = saveJSON(data: jsonData) {
// convert result to CKASset using local save filepath
let asset:CKAsset = CKAsset.init(fileURL: path)
r["DB"] = asset
// Modify PlayersDB value in CKRecord
let modifyRecord = CKModifyRecordsOperation(recordsToSave: [r], recordIDsToDelete: nil)
modifyRecord.savePolicy = CKModifyRecordsOperation.RecordSavePolicy.allKeys
modifyRecord.qualityOfService = QualityOfService.userInitiated
modifyRecord.modifyRecordsCompletionBlock = { savedRecords, deletedRecordIDs, error in
if error == nil {
// we did it!
print("PlayersDB Successfully overwritted with update api data")
// delete the file you created
deleteJSON(path: path)
rawData = apiData
semaphore.signal()
} else {
print("ERROR SAVING PlayersDB TO CK" + error!.localizedDescription)
// delete the file you created
deleteJSON(path: path)
// pull from the CK DB anyway so it fails softly
if let data = r["DB"] {
// decode the JSON and set it to rawData
let d = readCKAsset(asset: data as! CKAsset)
let result = try? JSONDecoder().decode(dfsAPIData.self, from: d as! Data)
rawData = result
semaphore.signal()
}
}
}
publicDB.add(modifyRecord)
}
}
catch {
print("Error Encoding JSON - WTF")
// if encoding fails - pull latest db instead to fail softly
if let data = r["DB"] {
// decode the JSON and set it to rawData
let d = readCKAsset(asset: data as! CKAsset)
let result = try? JSONDecoder().decode(dfsAPIData.self, from: d as! Data)
rawData = result
semaphore.signal()
}
}
}
}
// process error - DB doesnt exist, Call API and Create It
if let e = error {
// call API
let apiData = callAPI(week: findWeek())
// create record
let recordID = CKRecord.ID(recordName:thisWeek + "_Players")
let record = CKRecord(recordType: "WeeklyDB", recordID: recordID)
// encode the api data as NSData
let jsonEncoder = JSONEncoder()
do {
let jsonData = try jsonEncoder.encode(apiData)
if let path = saveJSON(data: jsonData) {
// convert result to CKASset using local save filepath
let asset:CKAsset = CKAsset.init(fileURL: path)
record["DB"] = asset
// Save DB to CK
publicDB.save(record, completionHandler: {returnRecord, error in
if let err = error {
// something happened
print("ERROR SAVING PlayersDB TO CK" + err.localizedDescription)
} else {
// we did it!
print("PlayersDB Successfully overwritted with update api data")
// delete the file you just created
deleteJSON(path: path)
rawData = apiData
semaphore.signal()
}
})
}
}
catch {
print("Error Encoding JSON while saving api data to PlayersDB - WTF")
}
}
}
semaphore.wait()
return
}