Getting the error code and description from a session.dataTask call - swift

I am calling some APIs from my app. I'd like to get and process the error code. There seems to be a lot of information on how to retrieve and process the response codes, but very little on the error code. I'd like to access the error code in case, say the user has no internet connection so the call to the API fails. I am using this code:
session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in
if let error = error {
print("Tide Station API call failed with error \(error)")
return
}
if let response = response as? HTTPURLResponse {
print("Tide Station API call response is \(response.statusCode)")
}
if let data = data
{
do {
let result = try JSONDecoder().decode(TideStations.self, from: data)
self.tideStations = result.stations
print("\(result.stations.count) Tide Stations successfully loaded")
self.showTideStationsOnMap()
} catch {
print("Error while stations parsing: \(error)")
}
}
}).resume()
Which generates the following output in the debug area when my device is is airplane mode, i.e. I am forcing one sure way of the API call failing: (note I have bolded the area from my error handling print statement)
2020-02-26 17:57:58.235368+0000 APITest[5464:2590011] Task <006E7876-214F-41E3-8BDE-26960AF1F554>.<1> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={NSUnderlyingError=0x283a918f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, NSErrorFailingURLStringKey=https://api.sunrise-sunset.org/json?lat=57.5081&lng=-1.7841&date=2020-06-21&formatted=0, NSErrorFailingURLKey=https://api.sunrise-sunset.org/json?lat=57.5081&lng=-1.7841&date=2020-06-21&formatted=0, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=50, NSLocalizedDescription=The Internet connection appears to be offline.}
Astronomical Times API call failed with error Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={NSUnderlyingError=0x283a918f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, NSErrorFailingURLStringKey=https://api.sunrise-sunset.org/json?lat=57.5081&lng=-1.7841&date=2020-06-21&formatted=0, NSErrorFailingURLKey=https://api.sunrise-sunset.org/json?lat=57.5081&lng=-1.7841&date=2020-06-21&formatted=0, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=50, NSLocalizedDescription=The Internet connection appears to be offline.}
In this example, I want to extract from this the fact that the internet connection appears down and advise the user. I would have thought that there would be similar support for error code extraction as there is for result code extraction. Would it be more normal to just say an error occurred (easy to detect) and handle elsewhere (e.g. detect no internet before calling the API)? If so, what shout I be guarding for?

The Error class in swift does not contain the error code and the error domain in it. However, if you print the error you can definitely see the error code and domain in the logs.
You can access those by simply casting the Swift's Error to the old Objective-C NSError. This classes are toll-free bridgeable, so there is no need to check if the cast fails or forcefully perform the cast with as!. Simply do:
let nsError = error as NSError
print("Error code is: \(nsError.code)")

Related

Swift: "NSCocoaErrorDomain Code=134092" when executing CNSaveRequest of Contact Store

I'm writing a command line program with Swift Package Manager that uses Contacts API to automatically add phonetic names for the contacts. However, I found I was unable to do so for contacts that has notes. First, when I was trying to enumerate the contacts, there is a strange error output:
2023-01-22 18:58:36.085525+0800 PhoneticNames[18591:98356] [core] Attempted to register account monitor for types client is not authorized to access: {(
"com.apple.account.CardDAV",
"com.apple.account.Exchange",
"com.apple.account.LDAP"
)}
2023-01-22 18:58:36.085580+0800 PhoneticNames[18591:98356] [accounts] CNAccountCollectionUpdateWatcher 0x6000003ccc40: Store registration failed: Error Domain=com.apple.accounts Code=7 "(null)"
2023-01-22 18:58:36.085606+0800 PhoneticNames[18591:98356] [accounts] CNAccountCollectionUpdateWatcher 0x6000003ccc40: Update event received, but store registration failed. This event will be handled, but the behavior is undefined.
2023-01-22 18:58:36.136236+0800 PhoneticNames[18591:98344] [api] Attempt to read notes by an unentitled app
According to this document, I need to add the notes entitlement only if I was requesting the note field with CNContactNoteKey. However, I was not requesting it and I still get that "Attempt to read notes by an unentitled app" error.
Here is my code for enumerating the contacts:
let keys: [CNKeyDescriptor] = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactFormatter.descriptorForRequiredKeys(for: .phoneticFullName)]
let fetchRequest = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
fetchRequest.mutableObjects = true
do {
try store.enumerateContacts(with: fetchRequest) { (contact, stop) in
contacts.append(contact.mutableCopy() as! CNMutableContact)
}
} catch {
print("unable to list contacts. \(error)")
}
And this is how I update the contacts later:
let saveRequest = CNSaveRequest()
contact.phoneticFamilyName = phoneticFamilyName
contact.phoneticMiddleName = phoneticMiddleName
contact.phoneticGivenName = phoneticGivenName
saveRequest.update(contact)
do {
try store.execute(saveRequest)
} catch {
print("Fail to execute store request: \(error)")
}
This code works as expected for contacts without the note field. But for contact with a non-empty note field, the saveRequest fails and outputs the following error:
2023-01-22 18:58:36.197026+0800 PhoneticNames[18591:98344] [error] error: Unhandled error occurred during faulting: Error Domain=NSCocoaErrorDomain Code=134092 "(null)" ({
})
CoreData: error: Unhandled error occurred during faulting: Error Domain=NSCocoaErrorDomain Code=134092 "(null)" ({
})
I am using Swift 5.7, Xcode 14.2, macOS 13.1. I looked for all the documents but couldn't find anything about NSCocoaErrorDomain Code 134092.
I shouldn't receive the error "Attempt to read notes by an unentitled app" when I am not requesting notes. And I should be able to successfully execute the saveRequest that doesn't modify the note field. Does anyone knows how these error comes?

Can't get SPC key for DRM video - AVPlayer [Swift]

We have successfully integrated video DRM logic using AVAssetResourceLoaderDelegate.
It worked well. But lately, after playing the same movie several times in a row, we started getting this error when trying to get SPC.
do {
let spcData = try loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: assetIDData, options: [AVAssetResourceLoadingRequestStreamingContentKeyRequestRequiresPersistentKey: true as AnyObject])
return spcData
} catch {
print(error)
return nil
}
Error:
Domain=AVFoundationErrorDomain Code=-11879 "(null)" UserInfo={NSUnderlyingError=0x2805f9980 {Error Domain=NSOSStatusErrorDomain Code=-15841 "(null)"}}
I found that this code -11879 means that the request has been canceled.
I don't know what the second code means.
Why is the device not issuing more SPCs for content?
Maybe it's cached and needs to be updated somehow.

Firebase Phone Authentication works on Simulator but not phone

I finally decided its time for testflight with my app. Authentication works perfectly in the simulator. I've tried it with test numbers (works great), I've tried it with my phone (sends verification code), and I've tried it with my parents phone (a device that has never been set up with my firebase) and it worked great.
As soon as I set it up on my device and tried to authenticate the same way, I encountered an error
here is the very simple code I took straight from firebase docs
public func startAuth(phoneNumber: String, completion: #escaping (Bool) -> Void) {
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate: nil) { [weak self] verificationId, error in
guard error == nil else {
print("error verifying phone numer, Error: \n\(error!)")
completion(false)
return
}
self?.verificationId = verificationId
completion(true)
}
}
When running in the simulator, it works great, when running on my phone, I get this error:
error verifying phone numer, Error:
Error Domain=FIRAuthErrorDomain Code=17999 "An internal error has occurred, print and inspect the error details for more information." UserInfo={FIRAuthErrorUserInfoNameKey=ERROR_INTERNAL_ERROR, NSLocalizedDescription=An internal error has occurred, print and inspect the error details for more information., NSUnderlyingError=0x283f43c30 {Error Domain=FIRAuthInternalErrorDomain Code=3 "(null)" UserInfo={NSUnderlyingError=0x283f9bae0 {Error Domain=com.google.HTTPStatus Code=503 "(null)" UserInfo={data={length = 207, bytes = 0x7b0a2020 22657272 6f72223a 207b0a20 ... 5d0a2020 7d0a7d0a }, data_content_type=application/json; charset=UTF-8}}, FIRAuthErrorUserInfoDeserializedResponseKey={
code = 503;
errors = (
{
domain = global;
message = "Error code: 33";
reason = backendError;
}
);
message = "Error code: 33";
}}}}
I've tried adding the firebase auth SDK: https://github.com/firebase/firebase-ios-sdk to the project and I get an error in a random file that says "redfinition of module 'Firebase'" and other stack overflow posts say to just delete the SDK and it'll solve your issue.
I'm really confused, why does it only work in the simulator and not my device?
Is the SDK really the solution and I need to find another way around the redefinition issue?
Thanks for your help in advance!

Get string value of Alamofire AFError error Swift

I'm making a request to my server with AlamoFire, and I have a setup something like this, where "self.error" is a String variable.
session.request("https://localhost:3000/my-route", method: .post, parameters: parameters, encoding: JSONEncoding.default).responseJSON { response in
switch response.result {
case .success(let result):
print("nice", result)
case .failure(let err):
self.error = err //self.error is a String state variable
print("failure")
}
}
However, I get the (sensible) error "Cannot assign value of type 'AFError' to type 'String'", because I'm trying to set self.error = err, and err is of type AFError, whereas self.error is a String state variable.
I realize I could just do something like self.error = "There was an error", but I'd like to provide the user with some feedback as to what the error actually was, and I can't seem to find the correct properties (i.e. something like err.stringValue, err.description, etc.) to do so. Seems like there's some source code here https://github.com/Alamofire/Alamofire/blob/master/Source/AFError.swift but I'm not sure how this relates to the string value I'd like to obtain.
Any help here would be much appreciated.
For such cases you can always use String interpolation e.g. self.error = "\(err)", but this is not a good idea because err is not just a string and could contain something like:
sessionTaskFailed(error: Error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo={_kCFStreamErrorCodeKey=61, NSUnderlyingError=0x280e37750 {Error Domain=kCFErrorDomainCFNetwork Code=-1004 "(null)" UserInfo={_kCFStreamErrorCodeKey=61, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <78576E38-D021-4ADC-87D4-1C9D81FF0E3A>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalDataTask <78576E38-D021-4ADC-87D4-1C9D81FF0E3A>.<1>" ), NSLocalizedDescription=Could not connect to the server., NSErrorFailingURLStringKey=https://localhost:3000/my-route, NSErrorFailingURLKey=https://localhost:3000/my-route, _kCFStreamErrorDomainKey=1})
Like #Menaim already mentioned, you can use err.localizedDescription, then you could get something like URLSessionTask failed with error: Could not connect to the server.
Generally, I would not pass the error directly to the user. This depends at your users, but do they really should know what e.g. URLSessionTask is?
In my opinion you should define error string for user, that every user can understand and you should log err.localizedDescription to the console, so you can know what is happening.
If your app supports many languages, you can define localizable.strings files and translate the errors.
If you do this print("failure"), every time you have an error, how can you distinguish the errors?
My suggestion would be something like:
case .failure(let err):
self.error = "There was an error connecting to the server"
print("Failed to connect to backend: \(err.localizedDescription)")
You can try err.localizedDescription or you can get the error in the completion of the function and return it completion(error)

Alamofire GET request from Server API

I'm just getting acquainted with Alamofire (using Alamofire 4 with Xcode 8, Swift3, iOS 10)
I'm attempting to use HTTP protocol methods on a on a server API given the an ip address of XXX.XXX.X.XX
To take a photo with the SDK I'm using, I must use a GET request. The documentation states it like this:
Take the photo
GET takepic/true
Trigger the camera to take a photo
Return http status code 200 means OK, 500 means failed
The thing is, I'm not sure how to successfully get the data from the server, and also do not know how to use this "takepic/true" property I'm given.
I attempted the issue like so:
Alamofire.request("http://myIPAddress", method: .get).responseJSON { (response) in
if let status = response.response?.statusCode
{
switch(status)
{
case 200:
print("example success")
case 500:
print("The response failed")
default:
print("error with response status: \(status)")
}
}
if let result = response.result.value
{
print(result)
}
}
After running the app, I get a bunch of text in my console. Here's some of the stuff it says:
nw_socket_set_common_sockopts setsockopt SO_NOAPNFALLBK failed: [42] Protocol not available, dumping backtrace:
nw_connection_endpoint_report [1 192.168.9.67:80 in_progress socket-flow (satisfied)] reported event flow:start_connect
I searched for my print statement which meant success, but could not find it. I could not find any of my other print statements either.
EDIT: The text in my console window comes from me installing SISocket to my project via Cocoapods.
My "result" returns this:
expression produced error: error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x8).
The process has been returned to the state before expression evaluation.