I've developed some iOS 6.1 code to deal with NSError. But, I'm not happy with it. It is at best a hack:
-(bool) reptErrAtModule: (NSString *) module
atSubr: (NSString *) subr
atFunc: (NSString *) func
withErr: (NSError *) err
{
id value = [[err userInfo] objectForKey: NSUnderlyingErrorKey];
NSString * errDesc = (value != nil) ?
[value localizedDescription]:
(NSString *)[[err userInfo] objectForKey: #"reason"];
NSLog( #"ERR -> %#",[NSString stringWithFormat:
#"(%#>%#) %# failed! %#",module,subr,func,errDesc] );
}
I had a simpler form (without the (NSString *)[[err userInfo] objectForKey: #"reason"] case) and it worked for errors that I got back from calls to removeItemAtPath.
But then I got an error back from this code:
NSPersistentStore * entStor =
[myPerStoCor addPersistentStoreWithType: NSSQLiteStoreType
configuration: nil
URL: [NSURL fileURLWithPath: Path]
options: nil
error: &err];
And my routine failed to extract the error. So I added the #"reason" logic because I could see the text I wanted in the Info data in the debugger.
Now the code works with both types of errors but I'm thinking this is not the way to do this. There must be a better, more generic way to deal with all the types of errors stuff the system can give you back in NSError.
I use this:
NSString *description = error.localizedDescription;
NSString *reason = error.localizedFailureReason;
NSString *errorMessage = [NSString stringWithFormat:#"%# %#", description, reason];
For debugging purposes, you ideally want to log out the entire contents of the error. Roughly speaking this is the domain, code, and userInfo. Bear in mind that userInfo might well include an underlying error, which you want to apply the same logic to. And in some cases, the error might supply a description (or failure reason etc.) which isn't present in the userInfo.
If you scroll down my blog post at http://www.mikeabdullah.net/easier-core-data-error-debugging.html, there's a snippet there showing how to generate a dictionary representation of an NSError object, and then get a string representation of that. This is pretty handy for debugging/logging purposes.
For presentation to users though, -[NSError localizedDescription] is expressly designed for such purposes. -localizedFailureReason serves a similar role, tending to specify what went wrong, without the context of the operation being tried. (One way to think of it is localizedDescription = task desceription + localizedFailureReason)
Related
I'm trying out Snpachat's SnapKit login api, and I've setup my project as described in the documentation/guide. I've allowed the use of all the scopes, i.e. external id, display name and bitmoji in the dashboard and added the required fields in the .plist of my app.
The login and authentication proceed normal and return successfully, but when I try to fetch user data, that request fails every time with the SCOAuth2ClientErrorDomain error.
I'm using the snippet provided within the guide (although that code has a typo and doesn't build as is so I doubt in the validity of that code):
[SCSDKLoginClient loginFromViewController:self completion:^(BOOL success, NSError * _Nullable error) {
NSString *graphQLQuery = [#"{me{displayName, bitmoji{avatar}, externalId}}" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSDictionary *variables = #{#"page": #"bitmoji"};
[SCSDKLoginClient fetchUserDataWithQuery:graphQLQuery
variables:variables
success:^(NSDictionary *resources) {
NSDictionary *data = resources[#"data"];
NSDictionary *me = data[#"me"];
NSString *displayName = me[#"displayName"];
NSDictionary *bitmoji = me[#"bitmoji"];
NSString *bitmojiAvatarUrl = bitmoji[#"avatar"];
} failure:^(NSError * error, BOOL isUserLoggedOut) {
// handle error as appropriate
}];
}];
I've even tried configurin my app without the bitmoji and tried the request without it, it still fails.
[SCSDKLoginClient loginFromViewController:self completion:^(BOOL success, NSError * _Nullable error) {
NSString *graphQLQuery = [#"{me{displayName, externalId}}" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[SCSDKLoginClient fetchUserDataWithQuery:graphQLQuery
variables:nil
success:^(NSDictionary *resources) {
NSDictionary *data = resources[#"data"];
NSDictionary *me = data[#"me"];
NSString *displayName = me[#"displayName"];
} failure:^(NSError * error, BOOL isUserLoggedOut) {
// handle error as appropriate
}];
}];
Anyone have any idea what I might be doing wrong?
Ok, I got it working.
First of all, I re-added the user I was testing with to the demo users in the developer portal. After that, the SCOAuth2ClientErrorDomain error was gone and I was getting the success callback.
But, the response data was an error in the query string. The problem is that they're using a deprecated method stringByAddingPercentEscapesUsingEncoding. I'm not sure what the exact problem is but I've just sent the raw string as a query and I got a valid response.
UPDATE: I think encoding here is not needed. It doesn't make sense for the user of the api to encode the query. The api should handle it internally, and I think that is what might be happening here. So you probably get a double encoded query which then isn't encoded properly and is invalid. I tested encoding with non-deprecated methods for URL queries and it still didn't work. A raw query string is the way to go.
I have the following code to read in passed URLs. I'm testing this with the Pocket app and although hasItemConformingToTypeIdentifier is returning YES for kUTTypeURL, trying to load it in returns a error instead stating
"Unexpected value class."
. If I try to load it as an id<NSSecureCoding> item and debug, I find that the passed in object is indeed just the title of the page and not the URL. How do I read the URL?
NSURL *pageURL = nil;
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier: (NSString*) kUTTypeURL]) {
[itemProvider loadItemForTypeIdentifier:(NSString*) kUTTypeURL options:nil completionHandler:^(id <NSSecureCoding> urlItem, NSError *error) {
if ([((NSObject*)urlItem) isKindOfClass: [NSURL class]]) {
pageURL = [((NSURL*)urlItem) absoluteString];
}
}];
}
}
}
If you read the documentation for:
loadItemForTypeIdentifier(_:options:completionHandler:)
You'll see that:
The type information for the first parameter of your completionHandler
block should be set to the class of the expected type. For example,
when requesting text data, you might set the type of the first
parameter to NSString or NSAttributedString. An item provider can
perform simple type conversions of the data to the class you specify,
such as from NSURL to NSData or NSFileWrapper, or from NSData to
UIImage (in iOS) or NSImage (in OS X). If the data could not be
retrieved or coerced to the specified class, an error is passed to the
completion block’s.
Maybe you can experiment by coercing to different types?
Try this
__block NSURL *pageURL = nil;
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
if ([itemProvider hasItemConformingToTypeIdentifier: (NSString*) kUTTypeURL]) {
[itemProvider loadItemForTypeIdentifier:(NSString*) kUTTypeURL options:nil completionHandler:^(NSURL *urlItem, NSError *error) {
if (urlItem) {
pageURL = urlItem;
}
}];
}
}
}
And now if you want take URL of your current site use
NSString *output = [pageURL absolutestring];
Output - will be your URL.
I stumbled over this issue now myself. The Pocket App seems to be the only App which shows this issue. The strange thing is that there are Apps which can get the URL form Pocket. Like for example Firefox for iOS. Firefox is Open Source so I looked at its code (at Github) and found that it is doing exactly the same to get the URL that is shown here. The only difference is that Firefox is written in Swift, and my code (and the one posted here) is Objective C. So I wonder if the Pocket App is doing something strange that is triggering a bug in the Objective C API of the iOS only, so Swift Apps are not affected? I do not have any Swift experience yet, so I haven’t checked if switching to Swift would „solve“ this.
The fact that the method "hasItemConformingToTypeIdentifier:“ states that there is a URL available but „loadItemForTypeIdentifier:“ can not deliver it, is a clear indication that the iOS itself has a bug here (at least in the Objective C API). But there still must be something special the Pocket App is doing to trigger this bug, because otherwise this would not work in all other Apps.
With google cast iOS SDK, The GCKMediaControlChannel's sendTextMessage method is straightforward and it's hard to mis-use so I am guessing this may be a bug in the SDK ... hopefully someone will prove me wrong so I can get back to work!
Here's the code:
NSDictionary *messageDict = #{
#"message": #"blah",
#"num":[NSNumber numberWithInt:2]
};
NSError *error;
NSData *msgData = [NSJSONSerialization dataWithJSONObject:messageDict
options:0
error:&error];
NSString *message = #"" ;
if (!msgData) {
DDLogError(#"ERROR serializing message: %#", error);
return NO ;
} else {
message = [[NSString alloc] initWithData:msgData encoding:NSUTF8StringEncoding];
[self sendTextMessage:message] ;
}
...the receiver produces this error when the message is received [cast.receiver.mediaManager] Ignoring request, requestId is not an integer: undefined
At first view it seems like GCKMediaControlChannel inherits directly its sendTextMessage method from the GCKCastChannel, failing to implement some of the messaging aspects specific to the media channel (in particular failing to wrap the message in a media-style envelope with the requestId and mediaSessionID attributes)
Has anybody else encountered this? Am I missing something? Is there a workaround?
I followed the recommendation on the ticket I created, messaging to the receiver media app using a custom namespace using GCKCastChannel instead of he dedicated GCKMediaControlChannel to work around the issue. The ticket response confirms "don't use sendTextMessage directly with the GCKMediaControlChannel"
I want to retrieve Recent Activity images from flickr application, please any one suggest me how to retrieve that. thanks in advance.
This is not the best way of doing this but I will provide you a way to retrieve recent pictures on flickr. Standford iTunesU had a few lectures that related to this. I am getting almost all of my information from them. Here is a link to the course material:
http://www.stanford.edu/class/cs193p/cgi-bin/drupal/
For the basic, non multithreaded version look under lecture 10 and download Shutterbug Universal. You will also need a flickr API key which you can currently get here:
http://www.flickr.com/services/api/misc.api_keys.html
I will attempt to outline for you though how to go about completing your request, especially since either of those links may not be around for long.
You will want to create a class FlickrFetcher or something, then have a public class method
+ (NSArray *)latestGeoreferencedPhotos;
Implementation
+ (NSArray *)latestGeoreferencedPhotos
{
NSString *request = [NSString stringWithFormat:#"http://api.flickr.com/services/rest/?method=flickr.photos.search&per_page=500&license=1,2,4,7&has_geo=1&extras=original_format,tags,description,geo,date_upload,owner_name,place_url"];
return [[self executeFlickrFetch:request] valueForKeyPath:#"photos.photo"];
}
Where executeFlickrFetch is implemented:
+ (NSDictionary *)executeFlickrFetch:(NSString *)query
{
query = [NSString stringWithFormat:#"%#&format=json&nojsoncallback=1&api_key=%#", query, FlickrAPIKey];
query = [query stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData *jsonData = [[NSString stringWithContentsOfURL:[NSURL URLWithString:query] encoding:NSUTF8StringEncoding error:nil] dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *results = jsonData ? [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves error:&error] : nil;
if (error) NSLog(#"[%# %#] JSON error: %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), error.localizedDescription);
return results;
}
You will need to get your API key and either define it (#define FlickrAPIKey #"myAPIkey") or just directly insert it here.
In the Standford course they call latestGeoreferencedPhotos from a TVC subclass and set an array of photos in viewDidLoad:
// photos is a defined property
self.photos = [FlickrFetcher latestGeoreferencedPhotos];
Then in the setter for photos they reload a tableView that presents the images:
- (void)setPhotos:(NSArray *)photos
{
_photos = photos;
[self.tableView reloadData];
}
photos is now an array of dictionaries where so you can access specific image data by doing things like:
return [self.photos[row][#"title"] description]; // description because could be NSNull
I did find a github resource that may be valuable:
https://github.com/lukhnos/objectiveflickr.
I did not look into it much but it is probably worth checking into!
Your question did not provide much detail so I hope that this answer is sufficient for your needs.
What is the best approach for sending the local player's alias (or any text for that matter) to another device, since I can't put an NSString in my struct because of ARC/pointers?
So far, I've tried converting to & from a char array, using the __unsafe_unretained- option and trying to create a class to put the text in. Though all three of these attempts worked through compiling, they crashed the device (simulator keeps running but no alias displays.)
Is sending text in multiplayer games really difficult when using ARC? The issues I'm facing are most likely a result of the fact that I am not very experienced at programming... so if anyone could point me in the right direction or provide me with some snips of code, I'd really appreciate it.
You can easily encode and decode strings to NSData objects and send them over Game Center.
To encode: use this method on a string
- (NSData *)dataUsingEncoding:(NSStringEncoding)encoding
With encoding:NSUTF8StringEncoding
This will return a NSData object
To decode;
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
Edit:
Essentially you'll never have to send player aliases explicitly. There are 2 cases:
1: GKTurnBasedMatch
If you're using this, then here's how to get a list of all of the aliases (including yours)
NSMutableArray *playerIds = [NSMutableArray arrayWithCapacity:match.participants.count];
for (GKTurnBasedParticipant *part in match.participants) {
if([participant.playerID isKindOfClass:[NSString class]]){
[playerIds addObject:part.playerID];
}
} //at this point you have an array full of playerID strings, next you call this:
[GKPlayer loadPlayersForIdentifiers:(NSArray *)playerIds withCompletionHandler:(void (^) (NSArray *players, NSError *error))completionHandler {
for (GKPlayer *playa in players) {
NSLog(#"%#",playa.alias); // here i'm just logging the aliases but you can do whatever..
}
}];
2.GKMatch : this case is much easier since your GKMatch has already playerIDs array, same as before:
[GKPlayer loadPlayersForIdentifiers:(NSArray *) match.playerIDs withCompletionHandler:(void (^)(NSArray *players, NSError *error))completionHandler {
//again you get the array players full of GKPlayer objects , simply pull the alias you want
}];