Sending Player Alias (Game Center) via struct with ARC - ios

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
}];

Related

Share Extension loadItemForTypeIdentifier returns error for NSURL

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.

Data loads but won't save iPhone app

I've been stuck on this for approximately two weeks. I hate posting things that have been asked a lot but I really have gone through them all.
I used Ray Wenderlich's tutorial for saving data in an iPhone app.
http://www.raywenderlich.com/tutorials
So that is the setup I have going on in my app. I'm saving very simple objects. My Card object consists of a name, type, and image. That's all. So the tutorial is quite close to mine. Which is making this more frustrating.
The thing is, I have some NSLog statements in there for loading. I have it displaying the folder it's using to load and what objects it does load. Right now it is displaying this.
Loading cards from /Users/zach/Library/Application Support/iPhone Simulator/7.0.3-64/Applications/E3DB01FD-A37E-4A69-840B-43830F2BDE2C/Library/Private Documents
2013-11-04 00:02:50.073 CardMinder[84170:a0b] ()
So it seems to be trying to load them, but there's nothing there to load. Here is my function to save data.
- (void)saveData {
if (_data == nil) return;
[self createDataPath];
NSString *dataPath = [_docPath stringByAppendingPathComponent:kDataFile];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:_data forKey:kDataKey];
[archiver finishEncoding];
NSLog(#"%#",dataPath);
NSLog(#"%#",data);
[data writeToFile:dataPath atomically:YES];
}
Which is really just what's posted in that tutorial. I know if you feel generous enough to help me out i'll have to post some more code but I don't want to flood the post with useless stuff so just let me know and i'll get it out here.
I really appreciate anyone that can help, I have recently entered the desperation state and need help.
Thanks
UPDATE
NSError *error;
[data writeToFile:dataPath options:NSDataWritingAtomic error:&error];
NSLog(#"error: %#", error.localizedFailureReason);
These are the methods for the CardData class. I'm doing the name, type, and a bool here.
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_name forKey:kNameKey];
[aCoder encodeObject:_cardType forKey:kTypeKey];
[aCoder encodeBool:_checkedOut forKey:kOutKey];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSString *name = [aDecoder decodeObjectForKey:kNameKey];
NSString *cardType = [aDecoder decodeObjectForKey:kTypeKey];
BOOL checkedOut = [aDecoder decodeBoolForKey:kOutKey];
return [self initWithName:name cardType:cardType _Bool:checkedOut];
}
UPDATE 2
I just put some more NSLog statements in and I found out that when I press the "Save card" button in my app, it doesn't seem to execute the saveData function at all. I have log statements galore in that saveData function and when I click the saveCard button it doesn't show any of those logs. Why would that be happening?
This is my saveButton code.
- (IBAction)saveNewCard:(id)sender
{
NSString *cardName = self.nameField.text;
_cardDoc.data.name = cardName;
CardDoc *newCard = [[CardDoc alloc] initWithName:cardName cardType:cardTypeString _Bool:NO image:chosenIcon];
[_cardDoc saveData];
NSLog(#"Card save button pressed!");
CardViewController *cardViewController = (CardViewController *)[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
[cardViewController.cards addObject:newCard];
[self.navigationController popToRootViewControllerAnimated:YES];
}
You should use writeToFile:options:error: instead of writeToFile:atomically:; that will give you an error message that should prove helpful. (The equivalent to atomically:YES is the option constant NSDataWritingAtomic.) Make sure you're getting back a return value of YES; if not, the error should be set.
If you're getting a value of NO but the error is not set, it means you're messaging nil. A quirk of Objective-C is that messaging nil is completely valid. If the method is defined to return something, you'll even get a result: 0 or equivalent (NO, nil, etc.)
In this case, you're messaging _cardDoc. There's no return result to detect. This is a bit harder to defensively code around, but [_cardDoc saveData] is actually [nil saveData]. The debugger will just breeze past the line.
Generally, if something absolutely should not be nil, you can use NSAssert:
NSAssert(_cardData, #"_cardData should not be nil");
[_cardData saveData];
But use this sparingly; you'll probably come to usually appreciate this behaviour.
A few things.
Post the results of your log statements so we know what you are seeing.
In order for your approach to work, your _data object needs to conform to the NSCoding protocol. That means you need to add the protocol declaration to your interface, and implement the methods encodeWithCoder and initWithCoder.
In those methods you need to save all the state data for your object / load the state back into your object.
Those methods are the most likely source of problems with your code. Post those methods if you need help with them, and walk though them in the debugger.
You might also look at the NSKeyedArchvier class method archivedDataWithRootObject. That method takes an object and encodes it into an NSData object in one step. The method archiveRootObject:toFile: take it a step further, and writes the data directly to a file for you.
NSKeyedUnarchiver has the corresponding methods unarchiveObjectWithData and unarchiveObjectWithFile to recreate your object from data/a file.

Need a generic way to extract error text

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)

Getting Flickr RecentActivity images

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.

How do I save additional content into my UIManagedDocument file packages?

I'm having a lot of trouble deciphering Apple's documentation around UIManagedDocument, specifically the following methods:
- (id)additionalContentForURL:(NSURL *)absoluteURL error:(NSError **)error
- (BOOL)readAdditionalContentFromURL:(NSURL *)absoluteURL error:(NSError **)error
- (BOOL)writeAdditionalContent:(id)content toURL:(NSURL *)absoluteURL originalContentsURL:(NSURL *)absoluteOriginalContentsURL error:(NSError **)error
Has anyone successfully managed to save additional content into the "addition content" directory inside their UIManagedDocument packages? I'm looking to save straight images (PNGs, JPEGs, etc) and videos (m4v, etc) into this directory using UUIDs as the filenames (with the correct file extension), and storing references to these individual files as NSString file paths within my persistent store.
Credit goes to Apple DTS for helping me understand this class. I'm sharing some of the example they helped me with here (modified slightly).
OK, so basically it works like this: subclass UIManagedDocument, and implement the following methods (where the extraInfo property is just an NSDictionary implemented on our subclass):
- (BOOL)readAdditionalContentFromURL:(NSURL *)absoluteURL error:(NSError **)error
{
NSURL *myURL = [absoluteURL URLByAppendingPathComponent:#"AdditionalInformation.plist"];
self.extraInfo = [NSDictionary dictionaryWithContentsOfURL:myURL];
return YES;
}
- (id)additionalContentForURL:(NSURL *)absoluteURL error:(NSError **)error
{
if (!self.extraInfo) {
return [NSDictionary dictionaryWithObjectsAndKeys: #"Picard", #"Captain", [[NSDate date] description], #"RightNow", nil];
} else {
NSMutableDictionary *updatedFriendInfo = [self.extraInfo mutableCopy];
[updatedFriendInfo setObject:[[NSDate date] description] forKey:#"RightNow"];
[updatedFriendInfo setObject:#"YES" forKey:#"Updated"];
return updatedFriendInfo;
}
}
- (BOOL)writeAdditionalContent:(id)content toURL:(NSURL *)absoluteURL originalContentsURL:(NSURL *)absoluteOriginalContentsURL error:(NSError **)error
{
if (content) {
NSURL *myURL = [absoluteURL URLByAppendingPathComponent:#"AdditionalInformation.plist"];
[(NSDictionary *)content writeToURL:myURL atomically:NO];
}
return YES;
}
UIManagedDocument will call these methods when it needs to, automatically saving whatever you need to save to the document package inside an AdditionalContent directory.
If you need to force a save, simply call the following on your UIManagedDocument instance:
[self updateChangeCount:UIDocumentChangeDone];
At present, I'm not using this for images and videos — but the example should give you enough to go off.
The documentation for -additionalContentForURL:error: indicates that returning a nil supposed to signal an error.
A return value of nil indicates an error condition. To avoid generating
an exception, you must return a value from this method. If it is not always
the case that there will be additional content, you should return a sentinel value (for example, an NSNull instance) that you check for in
writeAdditionalContent:toURL:originalContentsURL:error:.
I override -writeContents:andAttributes:safelyToURL:forSaveOperation:error: for another purpose (doing some stuff on first save of a new document), and calling super invokes the NSException gods because contents value is nil, not an NSDictionary as seemingly expected by UIManagedDocument. Hmm.
The more you know...
P.S. I guess it depends on the time of day with -writeContents:andAttributes:... It once threw an exception complaining about expecting an NSDictionary, but later threw an exception complaining that I didn't pass it an NSData. My eyebrow could not be raised in a more Spock-like fashion than it is right now.

Resources