Crash when using error.localizedDescription from completion - ios

I'm trying to check if error.localizedDescription contains a certain string but i keep getting a crash
if error.localizedDescription.contains("\"api.error.cardRejected.2000\"") {
failCompletion()
}
I have even tried to even use another way
if let description = (error! as NSError).userInfo[NSLocalizedDescriptionKey] as? String {
if description.contains("api.error.cardRejected.2000") {
failCompletion()
}
}
I still keep getting the same crash in the logs saying
-[__NSDictionaryM domain]: unrecognized selector sent to instance 0x60000046b520
It works when i check using the debugDescription but i would like to check using the localizedDecription since the debug one only works when debugging

NSError localized description is autogenerated from what's inside, here is what API tells:
/* The primary user-presentable message for the error, for instance for NSFileReadNoPermissionError: "The file "File Name" couldn't be opened because you don't have permission to view it.". This message should ideally indicate what failed and why it failed. This value either comes from NSLocalizedDescriptionKey, or NSLocalizedFailureErrorKey+NSLocalizedFailureReasonErrorKey, or NSLocalizedFailureErrorKey. The steps this takes to construct the description include:
1. Look for NSLocalizedDescriptionKey in userInfo, use value as-is if present.
2. Look for NSLocalizedFailureErrorKey in userInfo. If present, use, combining with value for NSLocalizedFailureReasonErrorKey if available.
3. Fetch NSLocalizedDescriptionKey from userInfoValueProvider, use value as-is if present.
4. Fetch NSLocalizedFailureErrorKey from userInfoValueProvider. If present, use, combining with value for NSLocalizedFailureReasonErrorKey if available.
5. Look for NSLocalizedFailureReasonErrorKey in userInfo or from userInfoValueProvider; combine with generic "Operation failed" message.
6. Last resort localized but barely-presentable string manufactured from domain and code. The result is never nil.
*/
open var localizedDescription: String { get }
so, it is crashed (probably at step 6.) then this NSError is incorrectly constructed - so find who & how constructed it, possibly at some layer on underlying errors some key of userInfo unexpectedly is set as NSDictionary instead of NSError.

Related

Object (Optional Any): get elements

I'm trying to pass information with an observed notification in my app.
The standard way to do that, is to set the userinfo. However, the data I want to pass is a Set, not a dictionary.
So, I do this:
NotificationCenter.default.post(name: MY_NOTIFICATION_NAME, object:self.productIds)
The object arrives fine, but now I'm unable to get to it:
in the console I do this:
po notification.object!
▿ 2 elements
- 0 : ZTJ
- 1 : ZTM
However, when I try to get to one of the elements, I get this:
po notification.object![0]
error: <EXPR>:8:21: error: value of type 'Any' has no subscripts
notification.object![0]
What am I doing wrong?
You know that notification.object is a Set, but the compiler doesn't because it's declared as Any, which means it could be anything, and so it can't find which implementation of object[0] it should use.
To read this object you need to cast it to a set.
if let mySet = notification.object as? Set<MyType> {
// Do whatever
}
Keep in mind that the object property of Notification is designed to be used as a filter, If you pass a value when adding an observer, you'll only get notifications that are sent with that exact same object.
The userInfo dictionary is to send related information, like your set. In this case I would send a dictionary like this:
NotificationCenter.default.post(name: MY_NOTIFICATION_NAME, object: nil, userInfo: ["products": productIds])
The notification has an object type of Any?.
When you're poing it in the console, you're asking it to print its description, which Any can do.
When you're asking it to subscript, Any can't do that, because subscripting is not defined on that type. You need to cast it to the expected type:
po (notification.object as? [String])?[0]
In general, it's best to nail down the type of any Any as soon as you can. Think of Any as a box used to send things through the post. The first thing you do is open it and find out what's inside.

Error initialize AZSCloudStorageAccount Swift 3

I tried link to my account with this code
let storageAccount : AZSCloudStorageAccount;
try! storageAccount = AZSCloudStorageAccount(fromConnectionString: config.getAzureConnection())
let blobClient = storageAccount.getBlobClient()
var container : AZSCloudBlobContainer = (blobClient?.containerReference(fromName: config.getContainer()))!
the "config.getAzureConnection()" contains the right path because i used the same for android app.
In this line try! storageAccount = AZSCloudStorageAccount(fromConnectionString: config.getAzureConnection()) the app crash without error, only (lldb) .
Can someone help me.
Does your error look like this?
fatal error: 'try!' expression unexpectedly raised an error: Error Domain=com.Microsoft.AzureStorage.ErrorDomain Code=1 "(null)": file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-802.0.53/src/swift/stdlib/public/core/ErrorType.swift, line 182
(lldb)
Code=1 is AZSEInvalidArgument, which means that your connection string is invalid. I am a bit confused why you said "the right path", since fromConnectionString takes the string directly, not the path to a file. To see an example of what a correct connection string looks like, please refer to the Getting Started Guide. Basically it looks like this:
"DefaultEndpointsProtocol=https;AccountName=your_account_name_here;AccountKey=your_account_key_here"
We will document the error codes properly very soon. Sorry for the confusion!
the app crash without error, only (lldb) .
I am sorry for that SWIFT blob client haven't provide error-handling code whatsoever currently. I will provide some clues to track your issue based on your code.
Before building the storage code, make one change in the project. Go to 'Azure Storage Client Library' -> Build Settings, search for the "Defines Module" setting, and change it to 'YES'.
Please check whether the issue is caused by bad network connection.
You could get error code of this issue by putting your code in a do-catch code block.
do {
//put your code here
} catch let error as NSError {
print("Error code = %ld, error domain = %#, error userinfo = %#", error.code, error.domain, error.userInfo);
}
The SWIFT blob sample has been tested and work well targeting iOS 9.0 and using XCode 7. If you have a different setup, the sample may not run properly. I suggest you use Blob Storage REST API as a workaround.

Why SKProductsRequestDelegate/SKRequestDelegate didFailWithError throws EXC_BAD_ACCESS on NSError?

I use SKProductsRequest to download product infos from App Store.
When I test a connectivity loss on my device, the request fails, but my app crashes within the SKRequestDelegate when I try to NSLog the error:
What am I doing wrong ? Another curious thing to me is that Expression Inspector is able to display NSError.debugDescription...
It fails on the first request, so there is no possible bug relative to multiple uses of productRequest variable (which is a strong ref in my swift class).
I finally found the reason. It is not related to SKProductsRequest!
I think there is a nasty bug with NSLogand string interpolation because when I replace:
NSLog("Failed: \(error.debugDescription)")
by
print("Failed: \(error.debugDescription)")
all is fine!
Apparently, the content of the error message can provoke a EXC_BAD_ADDRESS in NSLog (even without string interpolation in fact: NSLog(error.debugDescription) fails too).
Related anwser: https://stackoverflow.com/a/29631505/249742
NSLog("%#", error.debugDescription)
seems to work fine in every cases.
Perhaps NSLog(variable) is a misuse of NSLog, but I think NSLog(\(variable)) should be interpreted like NSLog("%#", variable). Else, there is no reliable way to interpolate strings with NSLog using the swift way \().

What is wrong with this line of Swift iOS Code?

I have created an iOS app using Swift and everything is working fine and dandy on the simulator. I get no errors or crashes at all, but when I submit my app to put up on the app store Apple rejects it and lets me know that it crashes when the user makes a selection. I cannot recreate this error/crash. I took the crash logs and symbolicated them. This line of code came up as the culprit for the crashes:
linksToPass = getLinks(season) as [String:[String]]
This line is trying to store the resulting Dictionary from the getLinks() function I created. It for sure is getting a dictionary and if there is no dictionary to send back I create a dictionary which has error information in it, so it is for sure returning a dictionary in that format no matter what. Seeing as I cannot recreate the crash, I am just trying to error check this line of code in any way possible so it does't crash when I resubmit to Apple.
I tried checking if the resulting dictionary was nil like so:
if(getLinks(seasons) != nil){
linksToPass = getLinks(season) as [String:[String]]
}
This is not valid though, and XCode lets me know that UInt8 is not compatible with NSDictionary or something of that nature.
I then fixed that line and changed it to this:
if(getLinks(seasons) != ["":[""]]){
linksToPass = getLinks(season) as [String:[String]]
}
I am just not sure if this is even a good way to check for errors. I was wondering if there were any suggestions on how I may go about making sure this line does not fail and result in a crash. Thank you very much.
EDIT:
Here is my getLinks() function if that helps add more info to the problem:
var season = ""
let hymn_links = Hymn_Links()
func getLinks (nameofseason:String) -> NSDictionary
{
switch (nameofseason)
{
default:
return ["Maps Not Found": []]
}
}
EDIT #2:
This is my updated getLinks() function with the use of optionals.
func getLinks (nameofseason:String) -> NSDictionary?
{
switch (nameofseason)
{
default:
return nil
}
}
Also in my statement of linksToPass I changed it to:
if let links = getLinks(season) as? [String:[String]]
{
linksToPass = links
hymnnames = [String] (linksToPass.keys)
}
There are some known issues with the Swift optimiser. Some people have resorted to shipping with debug builds.
My suggestion would be to test with an optimised build to see if you can reproduce it. You can then try shipping a debug build to the App store.
General Code Comments
Why are you returning an NSDictionary rather than a Swift dictionary anyway? Without knowing the contents and creation method for your hymn_links object I can't be sure how good it is.
I would avoid as casts until Swift 1.2 and stick to using as? and then handling the nil case. At least in your "Edit 2" a nil will cause a crash as nil cannot be cast to [String:[String]] although [String:[String]]? should be possible.
Can you guarantee that all of the items returned by the switch statement will never under any circumstances be nil? If not getLinks should return an Optional.
Note that is is virtually impossible for getLinks to know that one of the items will never be nil and in Swift un-handed nils are a crash waiting to happen. Unless all these methods correctly handle nil.
Return an Optional and handle that in the statement that calls getLinks.
Languages handle nils differently, Objective-C handles them rather well, Java and Swift by crashing. But Swift has a mechanism to handle nils without crashing: Optionals, use it.

Is it a good practice to extend NSError

Sorry for asking this question. I know in java we are extending Exception class for custom exceptions. But I don't see any scenarios for that in objective c.
So my question, Is it a good practice to extend NSError and introducing custom errors? If so when we should extend NSError class. I checked documentation for this too. But I can't see overriding notes for NSError.
While I agree that you shouldn't subclass NSError, it is very useful to put categories on it, and I do this regularly. For example, say your system often posts errors that come from some JSON block. I'd find it very convenient to create a category like:
#interface NSError (MyErrors)
// Construct an NSError from data in JSON.
// Include full JSON response in the userInfo
+ (NSError *)myErrorWithJSON:(JSON *)json;
// Parse the text out of the user info
- (NSString *)myErrorMessage;
// The full JSON error block as a string
- (NSString *)myErrorJSON;
// BOOLs can be helpful sometimes, or you could return an enum for use in a switch.
- (BOOL)myIsNetworkError;
- (BOOL)myIsAuthError;
#end
I often write little helpers to construct NSError more simply, construct the userinfo the way I want, and the pull data back out of the userinfo without callers needing to know its internal representation. I find this to be a very good form of data-hiding, and encourages the use of more descriptive messages.
Similarly, even for smaller projects, I often create a +myErrorWithCode:localizedDescription: category method. I know my domain, so I usually don't need to pass that, and this makes it a lot easier to set the NSLocalizedDescription key in the user info. Again, this encourages better errors by making them easier to create, and makes it easier to change the implementation details of your error handling.
I've never seen it done and that's because NSError is already very versatile. It allows the type of error to be defined by setting the domain and code properties and allows arbitrary additional information to be attached within the userInfo dictionary.
So, no, it's not good practice.
In the documentation is written that it is ok to subclass:
Applications may choose to create subclasses of NSError, for example,
to provide better localized error strings by overriding
localizedDescription.
In my case I am working with OSStatus which is Int32. NSError constructor supports only Int. So I need to subclass it to support OSSStatus.
It's not a bad idea to extend NSError.
I also have made a category on NSError for my own use. I would like to share it with you.
(1) Make a strings file to define all the error codes:
/* Following are general error messgaes those we can show to user
regarding to Internet connection and request. You can add more
codes. */
"-1001" = "Connection time out";
"-1003" = "Cannot find Host";
"-1004" = "Cannot connect to Host";
"-1005" = "Server is temporarily down";
"-1009" = "The Internet connection appears to be offline";
"-1012" = "Authentication failed";
"2000" = "This is a custom error message"; // custom created error code
/* Always describe unknow error with whatever you want in
except case (i.e. except error codes). If not mentioned
the following line, still you will get message "Unknown error" */
"Unknown error" = "Network error occured";
(2) Make a category on NSError, let say "NSError+ErrorInfo":
#interface NSError (ErrorInfo)
-(NSString *)userDescription;
#end
(3) Define it:
#define ERROR_KEY(code) [NSString stringWithFormat:#"%d",code]
#define ERROR_LOCALIZED_DESCRIPTION(code) NSLocalizedStringFromTable(ERROR_KEY(code),#"Errors",nil)
#implementation NSError (ErrorInfo)
-(NSString *)userDescription
{
NSString *errorDescrption = NSLocalizedStringFromTable(ERROR_KEY(self.code),#"Errors",nil);
if (!errorDescrption || [errorDescrption isEqual:ERROR_KEY(self.code)]){
return NSLocalizedStringFromTable(#"Unknown error",#"Errors",nil);;
}
else{
return ERROR_LOCALIZED_DESCRIPTION(self.code);
}
return nil;
}
#end
(4) Make use of it:
NSError *yourError; // This can be any NSError object you get
yourError = [NSError errorWithDomain:#"yourDomain" code:2000 userInfo:details]; // Just for test
NSLog(#"%#",[yourError userDescription]);

Resources