Server error while resetting the CloudKit badge - ios

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.

Related

The operation couldn’t be completed. (AWSMobileClient.AWSMobileClientError error 20.)

I'm currently running into this error when implementing the AWSMobileClient signUp function. I haven't really altered the code sample from the AWS page describing how to implement it, other than changing the attributes to fit my user pool attribute requirements.
First in viewDidLoad, I initialize the mobile client like so:
AWSMobileClient.sharedInstance().initialize { (userState, error) in
if let userState = userState {
print("UserState: \(userState.rawValue)")
} else if let error = error {
print("error: \(error.localizedDescription)")
}
}
Then I have the function for signing up. This is what the code looks like (I encapsulate this in a function called signUpUser):
AWSMobileClient.sharedInstance().signUp(username: userEmail,
password: userPass,
userAttributes: ["email":userEmail, "given_name":userFirstName, "family_name": userLastName, "custom:school":userSchool]) { (signUpResult, error) in
if let signUpResult = signUpResult {
switch(signUpResult.signUpConfirmationState) {
case .confirmed:
print("User is signed up and confirmed.")
case .unconfirmed:
print("User is not confirmed and needs verification via \(signUpResult.codeDeliveryDetails!.deliveryMedium) sent at \(signUpResult.codeDeliveryDetails!.destination!)")
case .unknown:
print("Unexpected case")
}
} else if let error = error {
if let error = error as? AWSMobileClientError {
switch(error) {
case .usernameExists(let message):
print(message)
default:
break
}
}
print("\(error.localizedDescription)")
}
When I run the app on my iPhone, I call this function when the "Sign Up Button" is clicked. In the debug window, I get the following error:
The operation couldn’t be completed. (AWSMobileClient.AWSMobileClientError error 20.)
That's the only info that appears in the Xcode console. Does anyone know how to go about debugging or fixing this?
EDIT: I'm not sure what the issue was that caused this error. I started a fresh project, set up a new cognito pool and backend services, and ported over the code from this project, which resulted in everything working perfectly. The error may have been from incorrectly setting up the user pool, or perhaps not allowing unauthorized access to the sign up function (not sure if I had that set to "No").
If you exhaust the rest of the switch case there, you will be able to see what exactly is the error coming back from the service.
reference: https://stackoverflow.com/a/59521025/2464632

iOS error with Firebase Auth Sign In using Twitter credentials

I have a iOS Firebase app that supports Twitter login, but occasionally the signIn(with: credential call randomly fails and I can't figure out why.
First the user signs in with Twitter (which never fails), then I use the Twitter session to get the credentials, and then I use those credentials to sign in with Firebase which sometimes fails.
Here's the complete code
func twitterLogin(_ done:#escaping (_ user: FIRUser?, _ err: Error?) -> Void) {
Twitter.sharedInstance().logIn() { session, error in
guard error == nil else {
done(nil, error)
return
}
let credential = FIRTwitterAuthProvider.credential(withToken: session!.authToken, secret: session!.authTokenSecret)
FIRAuth.auth()?.signIn(with: credential, completion: { (signUser, error) in
//User is signed in
// ERROR OCCURS HERE. ANY HELP WOULD BE GREATLY APPRECIATED
done(signUser, error)
})
}
}
Here's the exact error message I'm getting.
UserInfo={NSUnderlyingError=0x600000442c10 {Error Domain=FIRAuthInternalErrorDomain Code=3 "(null)" UserInfo={FIRAuthErrorUserInfoDeserializedResponseKey={
code = 400;
errors = (
{
domain = global;
message = "Network error while to fetch VERIFY_CREDENTIAL from TWITTER.";
reason = invalid;
}
);
message = "Network error while to fetch VERIFY_CREDENTIAL from TWITTER.";
}}}, error_name=ERROR_INTERNAL_ERROR, NSLocalizedDescription=An internal error has occurred, print and inspect the error details for more information.})
I've tried to specifically search for the error message I'm getting "Network error while to fetch VERIFY_CREDENTIAL from TWITTER." but I can't find any solution. I found some posts which I believe were in Japanese but I couldn't understand their solution.
I even looked at Firebases iOS Auth errors here
https://firebase.google.com/docs/auth/ios/errors
but the error key FIRAuthErrorUserInfoDeserializedResponseKey isn't even listed as a possible error for signInWithCredential:completion:
I'm seeing the same error. Example at codepen:
https://codepen.io/mosse/pen/KmGMap
<button class="login">login</button>
<script src="https://www.gstatic.com/firebasejs/4.0.0/firebase.js"></script>
<script>
var config = {
apiKey: "AIzaSyDfJ5KuDofcZpahzl4H3XTl4f6R65gUyrQ",
authDomain: "authtest-3a944.firebaseapp.com",
projectId: "authtest-3a944",
};
firebase.initializeApp(config);
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
$(".login").click(function(){
var providerObj = new firebase.auth.TwitterAuthProvider();
firebase.auth().signInWithPopup(providerObj)
.then(result => {
console.log('success')
console.log(result.user)
})
.catch( err => {
console.log('failure')
console.log(err.message)
})
});
});
</script>
Firebase support suggest the cause is due to an unreliable Twitter endpoint, but haven't suggested any solution.
An older project using Firebase 2.3.2 does not replicate the error, which suggests that there should be a Firebase solution to me.

Handling CloudKit Errors

I am looking for general advice on handling CloudKit errors in Swift and am having trouble finding good examples online. Here are the things I'm wondering:
1) Should I account for every single error type each time the possibility for an error arises, or is that not really necessary?
2) I have read that one common way of handling CloudKit errors is by retrying to execute the operation after the time interval the error message provides. Should this retry basically be my standard procedure for all errors?
3) Do different CloudKit operations (save, fetch, etc.) produce different types of errors, or is there one standard set of CloudKit errors?
Thanks in advance! I'm just looking for general info on how to go about tackling error handling with CloudKit because I'm not really sure where to start.
Yes, you want to check every cloudkit call for errors. Apple stresses this point in the cloudkit-related WWDC videos.
What you do when you detect an error varies greatly. Retry is sometimes an option, but sometimes not appropriate. If you're using batch operations, retrying may require some additional work to extract the just the records that failed. So, yes you may sometimes want to retry, but no, you probably won't automatically retry every operation that fails.
There is one set of errors, defined in CKError.h. But, you don't always just get a CKError. Sometimes, especially with CKErrorPartialFailure, you get a top-level error that contains nested errors that you also have to unwrap. As of IOS 10, the list of errors in CKError.h looks like:
typedef NS_ENUM(NSInteger, CKErrorCode) {
CKErrorInternalError = 1, /* CloudKit.framework encountered an error. This is a non-recoverable error. */
CKErrorPartialFailure = 2, /* Some items failed, but the operation succeeded overall. Check CKPartialErrorsByItemIDKey in the userInfo dictionary for more details. */
CKErrorNetworkUnavailable = 3, /* Network not available */
CKErrorNetworkFailure = 4, /* Network error (available but CFNetwork gave us an error) */
CKErrorBadContainer = 5, /* Un-provisioned or unauthorized container. Try provisioning the container before retrying the operation. */
CKErrorServiceUnavailable = 6, /* Service unavailable */
CKErrorRequestRateLimited = 7, /* Client is being rate limited */
CKErrorMissingEntitlement = 8, /* Missing entitlement */
CKErrorNotAuthenticated = 9, /* Not authenticated (writing without being logged in, no user record) */
CKErrorPermissionFailure = 10, /* Access failure (save, fetch, or shareAccept) */
CKErrorUnknownItem = 11, /* Record does not exist */
CKErrorInvalidArguments = 12, /* Bad client request (bad record graph, malformed predicate) */
CKErrorResultsTruncated NS_DEPRECATED(10_10, 10_12, 8_0, 10_0, "Will not be returned") = 13,
CKErrorServerRecordChanged = 14, /* The record was rejected because the version on the server was different */
CKErrorServerRejectedRequest = 15, /* The server rejected this request. This is a non-recoverable error */
CKErrorAssetFileNotFound = 16, /* Asset file was not found */
CKErrorAssetFileModified = 17, /* Asset file content was modified while being saved */
CKErrorIncompatibleVersion = 18, /* App version is less than the minimum allowed version */
CKErrorConstraintViolation = 19, /* The server rejected the request because there was a conflict with a unique field. */
CKErrorOperationCancelled = 20, /* A CKOperation was explicitly cancelled */
CKErrorChangeTokenExpired = 21, /* The previousServerChangeToken value is too old and the client must re-sync from scratch */
CKErrorBatchRequestFailed = 22, /* One of the items in this batch operation failed in a zone with atomic updates, so the entire batch was rejected. */
CKErrorZoneBusy = 23, /* The server is too busy to handle this zone operation. Try the operation again in a few seconds. */
CKErrorBadDatabase = 24, /* Operation could not be completed on the given database. Likely caused by attempting to modify zones in the public database. */
CKErrorQuotaExceeded = 25, /* Saving a record would exceed quota */
CKErrorZoneNotFound = 26, /* The specified zone does not exist on the server */
CKErrorLimitExceeded = 27, /* The request to the server was too large. Retry this request as a smaller batch. */
CKErrorUserDeletedZone = 28, /* The user deleted this zone through the settings UI. Your client should either remove its local data or prompt the user before attempting to re-upload any data to this zone. */
CKErrorTooManyParticipants NS_AVAILABLE(10_12, 10_0) = 29, /* A share cannot be saved because there are too many participants attached to the share */
CKErrorAlreadyShared NS_AVAILABLE(10_12, 10_0) = 30, /* A record/share cannot be saved, doing so would cause a hierarchy of records to exist in multiple shares */
CKErrorReferenceViolation NS_AVAILABLE(10_12, 10_0) = 31, /* The target of a record's parent or share reference was not found */
CKErrorManagedAccountRestricted NS_AVAILABLE(10_12, 10_0) = 32, /* Request was rejected due to a managed account restriction */
CKErrorParticipantMayNeedVerification NS_AVAILABLE(10_12, 10_0) = 33, /* Share Metadata cannot be determined, because the user is not a member of the share. There are invited participants on the share with email addresses or phone numbers not associated with any iCloud account. The user may be able to join the share if they can associate one of those email addresses or phone numbers with their iCloud account via the system Share Accept UI. Call UIApplication's openURL on this share URL to have the user attempt to verify their information. */
} NS_ENUM_AVAILABLE(10_10, 8_0);
One approach, while you're developing and testing the app, is to check every cloudkit operation for an error, and if detected, fire an NSAssert to stop the app. Then, examine the error, underlying errors and context to determine why it failed and what you need to do about it. Most likely, over time, you'll see common patterns emerge and you can then consider building a generic error handler.
I've written a CloudKit help that makes it much easier to process errors. This is just a starting point and there is a lot more that can be done.
The main focus of this helper, in its current state, is to make it easy to retry errors that should be retried after an appropriate timeout.
But you still need to deal with errors that shouldn't be retried such as the user's iCloud storage being full. Even with this helper, every call to one of these helper methods needs to properly handle the result and possibly report an error to the user. Of course you can add a help method that checks all of the possible error types and shows an appropriate message. Then all uses of the CloudKit code can call that one helper method.
This also only covers a few of the possible operations. You would want to add support for other operations as well. Lastly, this doesn't handle partial errors yet. That would be another useful enhancement.
import Foundation
import CloudKit
public class CloudKitHelper {
private static func determineRetry(error: Error) -> Double? {
if let ckerror = error as? CKError {
switch ckerror {
case CKError.requestRateLimited, CKError.serviceUnavailable, CKError.zoneBusy, CKError.networkFailure:
let retry = ckerror.retryAfterSeconds ?? 3.0
return retry
default:
return nil
}
} else {
let nserror = error as NSError
if nserror.domain == NSCocoaErrorDomain {
if nserror.code == 4097 {
print("cloudd is dead")
return 6.0
}
}
print("Unexpected error: \(error)")
}
return nil
}
public static func modifyRecordZonesOperation(database: CKDatabase, recordZonesToSave: [CKRecordZone]?, recordZoneIDsToDelete: [CKRecordZoneID]?, modifyRecordZonesCompletionBlock: #escaping (([CKRecordZone]?, [CKRecordZoneID]?, Error?) -> Void)) {
let op = CKModifyRecordZonesOperation(recordZonesToSave: recordZonesToSave, recordZoneIDsToDelete: recordZoneIDsToDelete)
op.modifyRecordZonesCompletionBlock = { (savedRecordZones: [CKRecordZone]?, deletedRecordZoneIDs: [CKRecordZoneID]?, error: Error?) -> Void in
if let error = error {
if let delay = determineRetry(error: error) {
DispatchQueue.global().asyncAfter(deadline: .now() + delay) {
CloudKitHelper.modifyRecordZonesOperation(database: database, recordZonesToSave: recordZonesToSave, recordZoneIDsToDelete: recordZoneIDsToDelete, modifyRecordZonesCompletionBlock: modifyRecordZonesCompletionBlock)
}
} else {
modifyRecordZonesCompletionBlock(savedRecordZones, deletedRecordZoneIDs, error)
}
} else {
modifyRecordZonesCompletionBlock(savedRecordZones, deletedRecordZoneIDs, error)
}
}
database.add(op)
}
public static func modifyRecords(database: CKDatabase, records: [CKRecord], completion: #escaping (([CKRecord]?, Error?) -> Void)) {
CloudKitHelper.modifyAndDeleteRecords(database: database, records: records, recordIDs: nil) { (savedRecords, deletedRecords, error) in
completion(savedRecords, error)
}
}
public static func deleteRecords(database: CKDatabase, recordIDs: [CKRecordID], completion: #escaping (([CKRecordID]?, Error?) -> Void)) {
CloudKitHelper.modifyAndDeleteRecords(database: database, records: nil, recordIDs: recordIDs) { (savedRecords, deletedRecords, error) in
completion(deletedRecords, error)
}
}
public static func modifyAndDeleteRecords(database: CKDatabase, records: [CKRecord]?, recordIDs: [CKRecordID]?, completion: #escaping (([CKRecord]?, [CKRecordID]?, Error?) -> Void)) {
let op = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: recordIDs)
op.savePolicy = .allKeys
op.modifyRecordsCompletionBlock = { (savedRecords: [CKRecord]?, deletedRecordIDs: [CKRecordID]?, error: Error?) -> Void in
if let error = error {
if let delay = determineRetry(error: error) {
DispatchQueue.global().asyncAfter(deadline: .now() + delay) {
CloudKitHelper.modifyAndDeleteRecords(database: database, records: records, recordIDs: recordIDs, completion: completion)
}
} else {
completion(savedRecords, deletedRecordIDs, error)
}
} else {
completion(savedRecords, deletedRecordIDs, error)
}
}
database.add(op)
}
}

Firebase Anonymous Login fails after Xcode Update

My anonymous login for Firebase was working for months; however, when Xcode forced me to install some updates, it couldn't find some pods. After deleting those pods after running pod update, the project will now build; however, while attempting the anonymous login, I get this error:
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
If I wait long enough, I get error messages like this:
[Client] Discarding message for event <private> because of too many unprocessed messages
Here is my login method:
func login(onCompletion: #escaping (NSError?) -> Void) {
print("authenticating user")
FIRAuth.auth()?.signInAnonymously(completion: { result, error in
guard error == nil else {
print("error while authenticating user")
onCompletion(loginError)
return
}
if let user = result {
self.defaults.set(user.uid, forKey: "uid")
onCompletion(nil)
} else {
onCompletion(loginError)
}
})
}
which is called in the root view controller's viewDidLoad.
I still don't know what the issue was, but it was specific to the project. I created a new project and pulled the code from github and now it works like normal.

Swift CloudKit reading when not logged in to iCloud

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

Resources