Swift CloudKit reading when not logged in to iCloud - ios

So I can read information from my database when (in the simulator) I'm logged into my iCloud, however, everyone who has my app will (obviously) not be. When I try to access database when Im not logged in, this error message appears:
<CKError 0x7fc1e3416510: "Request Rate Limited" (7/2008); "This operation has been rate limited"; Retry after 3.0 seconds>
followed by:
Error Domain=NSCocoaErrorDomain Code=4097 "The operation couldn’t be completed. (Cocoa error 4097.)" (connection to service named com.apple.cloudd) UserInfo=0x7fc1e352f800 {NSDebugDescription=connection to service named com.apple.cloudd}
Code:
//VARIABLES********************************************************
#IBOutlet var questions: UILabel!
var resultsOfDB : String = ""
var indexes : [Int] = []
var counter : Int = 0
var newStr : String = ""
//*****************************************************************
#IBAction func getNewQbutton(sender: AnyObject) {
let container = CKContainer.defaultContainer()
var publicDB = container.publicCloudDatabase
let myQuery = CKQuery(recordType: "QuestionsTable", predicate: NSPredicate(value: true))
publicDB.performQuery(myQuery, inZoneWithID: nil){
results, error in
if error != nil {
println(error)
}
else
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.resultsOfDB = results.description
//for each character in resultsOfDB
for character in self.resultsOfDB{
if(character == "\""){
self.indexes.append(self.counter)
}
self.counter++
}
self.newStr = self.resultsOfDB.substringWithRange(Range<String.Index>(start: advance(self.resultsOfDB.startIndex, self.indexes[0] + 1), end: advance(self.resultsOfDB.endIndex, -(self.counter - self.indexes[1]))))
self.questions.text = self.newStr
})
}
}
}
Does anyone know how someone can read in my database when they aren't logged in to my iCloud account? Thanks!

The public database is only readable with no login in the production environment, not in the development environment.
Apple's documentation says:
In development, when you run your app through Xcode on a simulator or a device, you need to enter iCloud credentials to read records in the public database. In production, the default permissions allow non-authenticated users to read records in the public database but do not allow them to write records.
See CloudKit Quick Start.

This is a simulator bug (The Apple team is likely to fix this in the coming updates).
If your simulator is not logged into iCloud, this error will occur.
However, this error will not occur in the virtual device even if that device does not have iCloud logged in

Related

CloudKit - How to modify the record created by other user

It was OK in development,
but when I distributed my app by TestFlight, I have had this problem.
While I was checking some failures,
I have guessed that when a user who didn’t create the record try to modify it, can’t do that.
By the way, I can fetch all record values on Public Database. Only modification isn’t performed.
In the picture below, iCloud accounts are written in green areas. I predicted that these have to be same.
image: Metadata - CloudKit Dashboard
Now, users are trying to modify a record by the following code:
func modifyRecord() {
let publicDatabase = CKContainer.default().publicCloudDatabase
let predicate = NSPredicate(format: "accountID == %#", argumentArray: [myID!])
let query = CKQuery(recordType: "Accounts", predicate: predicate)
publicDatabase.perform(query, inZoneWith: nil, completionHandler: {(records, error) in
if let error = error {
print("error1: \(error)")
return
}
for record in records! {
/* ↓ New Value */
record["currentLocation"] = CLLocation(latitude: 40.689283, longitude: -74.044368)
publicDatabase.save(record, completionHandler: {(record, error) in
if let error = error {
print("error2: \(error)")
return
}
print("success!")
})
}
})
}
In development, I created and modified all records by myself, so I was not able to find this problem.
Versions
Xcode 11.6 / Swift 5
Summary
I guessed that it is necessary to create and modify record by same user ( = same iCloud Account ) in this code.
Then, could you please tell me how to modify the record created by other user?
In the first place, can I do that?
Thanks.
Looks like you have a permissions problem. In your cloudkit dashboard, go to: Schema > Security Roles
Then under 'Authenticated' (which means a user logged into Icloud)
you'll need to grant 'Write' permissions to the relevant record type. That should fix it!

check if firebase write operation was successful in iOS

How to check if firebase real time write operation was successful in ios?
I am trying to store data to real time database using the following code but it does not work:-
//adding the artist inside the generated unique key
refArtists.child(key!).setValue(data) { (error, dbreference) in
if error != nil{
print(error?.localizedDescription)
} else {
print("success", dbreference)
}
}
and I also tried following:-
let key = refArtists.childByAutoId().key
//creating artist with the given values
let artist = ["id":key,
"latitude": "34234" as String,
"longitude": "67657" as String
]
let data = ["data": artist]
refArtists.child(key!).setValue(data)
I am unable to understand why my write operation is unsuccessful.
Edit from comments:
The closure does not get called. I am getting this error :
Firebase Database connection was forcefully killed by the server. Will not attempt reconnect. Reason: Firebase error. Please ensure that you spelled the name of your Firebase correctly.

Accessing another app's CloudKit database?

Suppose John developed App A and Heather developed App B. They each have different Apple Developer's accounts and they are not on the same team or associated in any way. App B is backed by a public CloudKit database. Is there any way for App A to write to App B's public CloudKit database? Namely, can App A do this:
let DB = CKContainer(identifier: "iCloud.com.Heather.AppB").publicCloudDatabase
and then write to or read from this DB?
I'm guessing that this is not allowed out of the box, but is there a way to set up authentication so that this is possible?
This looks/sounds like the solution you seek.
CloudKit share Data between different iCloud accounts but not with everyone as outlined by https://stackoverflow.com/users/1878264/edwin-vermeer an iCloud specialist on SO.
There is third party explaination on this link too. https://medium.com/#kwylez/cloudkit-sharing-series-intro-4fc82dad7a9
Key steps shamelessly cut'n'pasted ... make sure you read and credit Cory on medium.com!
// Add an Info.plist key for CloudKit Sharing
<key>CKSharingSupported</key>
<true/>
More code...
CKContainer.default().discoverUserIdentity(withPhoneNumber: phone, completionHandler: {identity, error in
guard let userIdentity: CKUserIdentity = identity, error == nil else {
DispatchQueue.main.async(execute: {
print("fetch user by phone error " + error!.localizedDescription)
})
return
}
DispatchQueue.main.async(execute: {
print("user identity was discovered \(identity)")
})
})
/// Create a shred the root record
let recordZone: CKRecordZone = CKRecordZone(zoneName: "FriendZone")
let rootRecord: CKRecord = CKRecord(recordType: "Note", zoneID: recordZone.zoneID)
// Create a CloudKit share record
let share = CKShare(rootRecord: rootRecord)
share[CKShareTitleKey] = "Shopping List” as CKRecordValue
share[CKShareThumbnailImageDataKey] = shoppingListThumbnail as CKRecordValue
share[CKShareTypeKey] = "com.yourcompany.name" as CKRecordValue
/// Setup the participants for the share (take the CKUserIdentityLookupInfo from the identity you fetched)
let fetchParticipantsOperation: CKFetchShareParticipantsOperation = CKFetchShareParticipantsOperation(userIdentityLookupInfos: [userIdentity])
fetchParticipantsOperation.fetchShareParticipantsCompletionBlock = {error in
if let error = error {
print("error for completion" + error!.localizedDescription)
}
}
fetchParticipantsOperation.shareParticipantFetchedBlock = {participant in
print("participant \(participant)")
/// 1
participant.permission = .readWrite
/// 2
share.addParticipant(participant)
let modifyOperation: CKModifyRecordsOperation = CKModifyRecordsOperation(recordsToSave: [rootRecord, share], recordIDsToDelete: nil)
modifyOperation.savePolicy = .ifServerRecordUnchanged
modifyOperation.perRecordCompletionBlock = {record, error in
print("record completion \(record) and \(error)")
}
modifyOperation.modifyRecordsCompletionBlock = {records, recordIDs, error in
guard let ckrecords: [CKRecord] = records, let record: CKRecord = ckrecords.first, error == nil else {
print("error in modifying the records " + error!.localizedDescription)
return
}
/// 3
print("share url \(url)")
}
CKContainer.default().privateDB.add(modifyOperation)
}
CKContainer.default().add(fetchParticipantsOperation)
And on the other side of the fence.
let acceptShareOperation: CKAcceptSharesOperation = CKAcceptSharesOperation(shareMetadatas: [shareMeta])
acceptShareOperation.qualityOfService = .userInteractive
acceptShareOperation.perShareCompletionBlock = {meta, share, error in
Log.print("meta \(meta) share \(share) error \(error)")
}
acceptShareOperation.acceptSharesCompletionBlock = {error in
Log.print("error in accept share completion \(error)")
/// Send your user to wear that need to go in your app
}
CKContainer.default().container.add(acceptShareOperation)
Really I cannot hope to do the article justice, go read it... its in three parts!
If the apps were in the same organization, there is a way to set up shared access. But as you described the situation, it is not possible.

Server error while resetting the CloudKit badge

For some weeks now my app is unable to reset the CloudKit badge. I am getting a 'Network Failure' error. It did work before and I have not changed any code. I cannot find anything about changed functionality. Is this a CloudKit bug? Should i file a Radar? Or am I doing something wrong?
Here is the code that I use:
public func setBadgeCounter(count:Int) {
let badgeResetOperation = CKModifyBadgeOperation(badgeValue: count)
badgeResetOperation.modifyBadgeCompletionBlock = { (error) -> Void in
func handleError(error: NSError) -> Void {
EVLog("Error: could not reset badge: \n\(error)")
}
self.handleCallback(error, errorHandler: handleError, completionHandler: {
UIApplication.sharedApplication().applicationIconBadgeNumber = count
})
}
CKContainer.defaultContainer().addOperation(badgeResetOperation)
}
internal func handleCallback(error: NSError?, errorHandler: ((error: NSError) -> Void)? = nil, completionHandler: () -> Void) {
if (error != nil) {
EVLog("Error: \(error?.code) = \(error?.description) \n\(error?.userInfo)")
if let handler = errorHandler {
handler(error: error!)
}
} else {
completionHandler()
}
}
The error that I get with this is:
04/15/2015 09:12:28:837 AppMessage)[10181:.] EVCloudKitDao.swift(202) handleCallback(_:errorHandler:completionHandler:):
Error: Optional(4) = Optional("<CKError 0x7fb451c77c10: \"Network Failure\" (4/-1003); \"A server with the specified hostname could not be found.\">")
Optional([NSErrorFailingURLStringKey: https://ckdevice.icloud.com/api/client/badgeUpdate, _kCFStreamErrorCodeKey: 8, NSDebugDescription: NSURLErrorDomain: -1003, NSLocalizedDescription: A server with the specified hostname could not be found., NSErrorFailingURLKey: https://ckdevice.icloud.com/api/client/badgeUpdate, NSUnderlyingError: Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo=0x7fb45351a890 {NSErrorFailingURLStringKey=https://ckdevice.icloud.com/api/client/badgeUpdate, NSErrorFailingURLKey=https://ckdevice.icloud.com/api/client/badgeUpdate, _kCFStreamErrorDomainKey=12, _kCFStreamErrorCodeKey=8, NSLocalizedDescription=A server with the specified hostname could not be found.}, _kCFStreamErrorDomainKey: 12])
04/15/2015 09:12:28:839 AppMessage)[10181:.] EVCloudKitDao.swift(788) handleError:
Error: could not reset badge:
<CKError 0x7fb451c77c10: "Network Failure" (4/-1003); "A server with the specified hostname could not be found.">
A complete functional app with this problem can be found at https://github.com/evermeer/EVCloudKitDao
It's an Apple Bug. I use your Apple based API, timeout a user when attempting to log in from a foreign country. Using VPN from US solves the problem, suggesting Cloudkit isn't friendly to non-US territories. Using the API no problem before, it seems this is their latest change.
Specifically,
CKContainer.fetchUserRecordIDWithCompletionHandler, takes too long or never return.
Reference from Apple Doc
At startup time, fetching the user record ID may take longer while
CloudKit makes the initial iCloud account request. After the initial
fetch, accessing the user record ID should take less time. If no
iCloud account is associated with the device, or if access to the
user’s iCloud account is restricted, this method returns an error of
type CKErrorNotAuthenticated.
#Edwin Vermeer
Please correct me if you have solved it with the updated of your API or Apple fixes it.

Error using CKSubscription by Zone

I have been experimenting with CloudKit, and so far it's working fine. However, I've been having a hard time getting zone based subscription to work. Has anybody worked on it?. Here the code I tried :
// MARK: - Subscriptions
func registerSubscriptionByZone(){
let container = CKContainer(identifier: "iCloud.com.MyDataInCloudKit")
let database = container.publicCloudDatabase
let recordZone = CKRecordZone.defaultRecordZone()
let zoneSubscription = CKSubscription(zoneID: recordZone.zoneID, options:CKSubscriptionOptions.allZeros)
zoneSubscription.notificationInfo = CKNotificationInfo()
zoneSubscription.notificationInfo.alertBody = "Change in Zone"
self.database.saveSubscription(zoneSubscription) {
subscription, error in
if error != nil {
println(error.localizedDescription)
} else {
println("subscription saved!")
}
}
}
I get the error: Error saving record subscription with id EB6C9478-0A38-4C01-A473-6C09A57B2E8B to server: no valid zone specified
From the CloudKit design guide at:
https://developer.apple.com/library/ios/documentation/General/Conceptual/iCloudDesignGuide/DesigningforCloudKit/DesigningforCloudKit.html
Zones are a useful way to arrange a discrete group of records but are
supported only in private databases. Zones cannot created in a public
database.

Resources