A GKScore must contain an initialized value? - ios

I am getting a SIGABRT crash sometimes with the crash saying: A GKScore must contain an initialized value.
So it tracked it down to this line:
[localScore reportScoreWithCompletionHandler:^(NSError* error) {}];
And localStore is created like this:
GKScore* localScore = [scores objectForKey:category];
-- category comes from...
for (NSString* category in categories)
-- categories comes from...
[GKLeaderboard loadCategoriesWithCompletionHandler:^(NSArray *categories, NSArray *titles, NSError *error)
-(void) initScores
{
NSString* libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* file = [libraryPath stringByAppendingPathComponent:currentPlayerID];
file = [file stringByAppendingString:kScoresFile];
id object = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
if ([object isKindOfClass:[NSMutableDictionary class]])
{
NSMutableDictionary* loadedScores = (NSMutableDictionary*)object;
scores = [[NSMutableDictionary alloc] initWithDictionary:loadedScores];
}
else
{
scores = [[NSMutableDictionary alloc] init];
}
//NSLog(#"scores initialized: %d", scores.count);
}
Sorry for all the code but pretty much all this code comes from this library's file: https://github.com/csddavies/DDGameKitHelper/blob/master/DDGameKitHelper.m
Anyway how would I fix this?
Thanks!!!

From the GameKit reference:
To report a score to Game Center, your game allocates and initializes a new object, sets the value property to the score the player earned, and then calls the reportScoreWithCompletionHandler: method.
Most likely it's complaining because you haven't set the value property, but it's also possible that you're missing the first step too -- i.e. it doesn't like you submitting GKScore objects that came from a leaderboard instead of ones you create yourself.

Related

iOS error read memory failed

I'm working on an iPad app.
When the app is launched, user have to enter a number. With this id, I check in a csv file to get informations about the user.
Informations are saved with a singleton. This singleton is causing me problems :
step 1 : initialization of my singleton
+(ASMagasin*) sharedInstance {
if (myMagasin == nil){
myMagasin = [[ASMagasin alloc]init];
}
return myMagasin;
}
step 2 : I call my function which work with the csv file
- (id)init {
if (self = [super init]) {
NSError * error;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString * num_magasin = [defaults objectForKey:kUserDefautNumMagasin];
[self loadMagFromCsv:filePathCsv withMagasin:num_magasin];
}
return self;
}
step 3 :
-(void)loadMagFromCsv:(NSString *)filePath withMagasin:(NSString *) num_magasin
{
NSError *error;
NSString *csvData = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
NSArray *gcRawData = [csvData componentsSeparatedByString:#"\n"];
NSArray *singleGC = [NSArray array];
for (int i = 0; i < gcRawData.count; i++)
{
NSString *nextGCString = [NSString stringWithFormat:#"%#", gcRawData[i]];
singleGC = [nextGCString componentsSeparatedByString:#","];
if ([singleGC[0]isEqualToString:num_magasin]){
_num=singleGC[0];
_libelle=singleGC[1];
_client_defaut_nom=singleGC[2];
_client_defaut_prenom=singleGC[3];
_client_defaut_tel=singleGC[4];
_client_defaut_mail=singleGC[5];
_cp=singleGC[6];
_ville=singleGC[7];
_pays=singleGC[8];
}
}
}
In this function, my variable error have this value before I initialize it :
(NSError *) error = 0x0000000000000001 domain: read memory from 0x19
failed (0 of 8 bytes read) - code: read memory from 0x11 failed (0 of
8 bytes read)
I don't know how to solve that and where is this error come from.
You are looking at random memory in the heap. It is meaningless. Your NSError hasn't even been initialised at this point, let alone assigned a value.
You can (and should) check error for a value after you execute the line
NSString *csvData = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
Before that error doesn't have a useful value.
I am posting it with a hope that it might help others facing same issue. Not sure about its exact cause, but I was experiencing it on a variable with _block before it. Removing _block fixed this issue and didn't give any warning either. Not sure what is going on here as I experienced it after updating to iOS 14. Will update if i find anything more interesting.

Exception isn't thrown when nil inserted in NSDictionary

So I was working on pulling photos from user's Facebook album and for some reason NSDictionary doesn't throw an exception when nil is inserted.
I've tested it on a real device and on iOS simulator (both iOS 7).
Maybe debugger crashes, maybe it's Facebook SDK bug.
Anyway, I'm ready to listen to your opinion.
UPDATE: One more thing I forgot to mention. In debugger, after inserting nil object in dictionary, debugger itself continues program execution. And I'm not even pressing the buttong "Continue program execution".
Here's the snippet
- (void)downloadAlbumsWithCompletionBlock:(void (^)(NSArray *albums, NSError *error))completionBlock;
{
[FBRequestConnection startWithGraphPath:#"/me/albums"
parameters:nil
HTTPMethod:#"GET"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error)
{
if (error)
{
NSLog(#"%#", error.localizedDescription);
completionBlock(nil, error);
}
else
{
NSMutableArray *data = [result objectForKey:#"data"];
NSMutableArray *albums = [NSMutableArray new];
for (NSMutableDictionary *dict in data)
{
NSString *albumID = dict[#"id"];
NSString *coverPhotoID = dict[#"cover_photo"];
// coverPhotoID gets nil and then successfully gets put in NSDictionary
// You can just write "NSString *coverPhotoID = nil;".
NSString *description = dict[#"description"];
NSString *name = dict[#"name"];
NSDictionary *album = #{#"album_id": albumID,
#"album_cover_photo_id": coverPhotoID,
#"album_description": description,
#"album_name": name };
// Here an exception should be thrown but for some reason
// we just quit out of the method. Result would be the same if we put
// empty "return;" statement.
// If we explicitly put nil, like
// #"album_cover_photo_id": nil
// we get compiler error.
[albums addObject:album];
}
completionBlock(albums, nil);
}
}];
}
You wrote:
// Here an exception should be thrown but for some reason
// we just quit out of the method. Result would be the same if we put empty "return;" statement.
Most likely the exception is caught somewhere. Try putting a breakpoint on Objective-C exception throws (In Xcode, go to the Breakpoint tab, click + in the bottom left corner and select Add Exception Breakpoint. Make sure the parameters are set to Objective-C exception and break On Throw).
You claim that
NSString *albumID = dict[#"id"];
NSString *coverPhotoID = nil;
NSString *description = dict[#"description"];
NSString *name = dict[#"name"];
NSDictionary *album = #{#"album_id": albumID,
#"album_cover_photo_id": coverPhotoID,
#"album_description": description,
#"album_name": name };
doesn't crash your app.
Honestly, I don't believe it. Either you're trolling or you're misinterpreting whats happening.
As the first line of your for cycle log the dict dictionary:
NSLog(#"dict var: %#", dict);
Does dict contains the key album_cover_photo_id?
If you get something like
"album_cover_photo_id" : null
then NSString *coverPhotoID = dict[#"cover_photo"] is assigning an NSNull instance to coverPhotoID. In that case the app doesn't crash because an NSNull instance is not nil.
NSString *coverPhotoID = nil;
is different then
NSString *coverPhotoID = [NSNull null];
It's very common for servers to return nulls in JSON instead of omitting the key (server people are weird).
You can also log album after you create it
NSLog(#"album var: %#", album);
And if you are 100 % sure you're adding a nil to a dictionary, take it to the Apple developer forum. I bet they'd love to know about that bug.
I hope this helps you in some way.

Load array and display based on state of uiswitch

Im trying to develop an app that displays a random truth or dare type question, however the user has the ability to turn off truths or dares in option. I have successfully managed to get the app to display a random quote from a plist file from either the truth or dare array also i have managed to program two switch buttons in the user options view controller.
My problem is how would i go about displaying only a truth or dare or both if the user has turned on of the uiswitchs off?
- (IBAction)button:(id)sender
{
if (!self.plistArray)
{
NSString *path = [[NSBundle mainBundle] pathForResource:
#"data" ofType:#"plist"];
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
if ([[defaults objectForKey:#"truthonoff"] isEqualToString:#"YES"])
{
NSDictionary *plistDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *plistArray1 = plistDict[#"truth"];
}
if ([[defaults objectForKey:#"dareonoff"] isEqualToString:#"YES"])
{
NSDictionary *plistDict2 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *plistArray2 = plistDict2[#"dare"];
}
self.plistArray = [[plistArray1 arrayByAddingObjectsFromArray:plistArray2] mutableCopy];
}
NSLog(#"%#", plistArray);
//check to see if array is empty and display message
if ([plistArray count] == 0)
{
self.text.text = #"array empty";
}
else
{
//display random quote from array
int randV = arc4random() % self.plistArray.count;
self.text.text = self.plistArray[randV];
[self.plistArray removeObjectAtIndex:randV];
}
}
That is my attempt however it will not run and i have the feeling it wont ddo the job i need.
Basicly i need it to display only truth if the user has selected that to true or only dare if that is selected or both if both are set to true.
EDIT
sorry the problem with the above code is the plist isnt being loaded and it is scipping straight to if array ==0 {
How do i ensure it loads the array and then checks which arrays to load from the plist file?
Any help is greatly appreciated
This is the code before i tried to add if statements. Im so confussed how best to do this
- (IBAction)shownext:(id)sender {
//load array and check then cycle through this untill array is empty. Array will add two arrays from plist file.
if (!self.plistArray) {
NSString *path = [[NSBundle mainBundle] pathForResource:
#"data" ofType:#"plist"];
NSDictionary *plistDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray * plistArray1 = plistDict[#"truth"];
NSDictionary *plistDict2 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *plistArray2 = plistDict2[#"dare"];
self.plistArray = [[plistArray1 arrayByAddingObjectsFromArray:plistArray2] mutableCopy];
}
NSLog(#"%#", plistArray);
//check to see if array is empty and display message
if ([plistArray count] == 0) {
self.text.text = #"array empty";
}
else {
//display random quote from array
int randV = arc4random() % self.plistArray.count;
self.text.text = self.plistArray[randV];
[self.plistArray removeObjectAtIndex:randV];
}
}
First, if you have a switch for truth and one for dare I hope you have something in place to deal with when the user turns both switches off and doesn't understand why they get nothing (trust me it will happen).
For the rest I'm not sure exactly how you app works but I will take a guess. I'm thinking you have a utility style app with the main UI in one view and then an info button that flips to a second view where the switches are. I'm also guessing that there is a button in the main view that retrieves a truth or dare string. My final assumption, based on your code above, is that when the user changes the state of a switch that writes a user default that you've use a #define to keep out spelling mistakes.
When your view loads you should load both arrays in case the user changes their mind in the middle of using your app and turns on both options or changes from one to the other. Depending on how many entries you have in each of those arrays you might consider creating a combined array as well to simplify things.
When the button is pressed you should then look at the defaults and see if you need to look at both arrays or just one (the below is pseudo code)
if(truth && dare) {
// if you have a combined array pick a random element from it.
// otherwise first randomly pick one of the arrays to pick from.
}
else if (truth) {
// get a random element from the truth array
}
else {
// get a random element from the dare array
}
Also, your current checks of the switch values will always return no unless you are doing extra work in the switch view controller. You should be using [defaults setBool:<UISwitch.isOn> forKey:<#definedKeyHere>] and [defaults boolForKey:<#definedKeyHere>].
It would really help to know what part isn't working. For one thing, it might help to store your flags as NSNumber objects instead of strings (could your string comparison be failing?). Then you could do something like:
if ([[defaults objectForKey:#"truthonoff"] boolValue])
Use a literal to add the actual NSNumber - #YES or #NO.
Consider changing your logic to something like:
VERIFY CODE - doing this freehand:
if (!self.plistArray)
{
self.plistArray = [NSMutableArray array];
NSString *path = [[NSBundle mainBundle] pathForResource:
#"data" ofType:#"plist"];
// why are you loading this twice?
NSDictionary *plistDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([[defaults valueForKey:#"truthonoff"] boolValue])
{
[self.plistArray addObject:plistDict[#"truth"]];
}
if ([[defaults valueForKey:#"dareonoff"] boolValue])
{
[self.plistArray addObject:plistDict[#"dare"]];
}
}
I am assuming that your code to load the Plists is working. Verify all your keys match what's in the Plist. Set a breakpoint and verify.
Calling a method on a nil object is a no-op in Objective C. So, it'll happily ignore calls to nil objects without telling you. Verify you have what you think you have.
Also, here:
//display random quote from array
int randV = arc4random() % self.plistArray.count;
self.text.text = self.plistArray[randV];
[self.plistArray removeObjectAtIndex:randV];
Consider using arc4random_uniform(self.plistArray.count) as it avoids modulo bias in the generator.
Now, this just gives you say 0 or 1 if you have two elements. Are you sure the two dictionary keys, "truth" and "dare" actually point to arrays?
Ok, everything working to this point. Now, you have an array of ARRAYS! So you need to randomly pick a question array, and THEN randomly pick a question.
Something like:
//get random array
int randArrayIndex = arc4random_uniform(self.plistArray.count);
NSArray* questionArray = self.plistArray[randArrayIndex];
//get random question
int randQuestionIndex = arc4random_uniform([questionArray count]);
NSString* randomQuestion = questionArray[randQuestionIndex];
self.text.text = randomQuestion;
// remove question
[questionArray removeObjectAtIndex:randQuestionIndex];
Something like that anyway. Of course, assuming you are storing NSStrings in those Plist arrays.

Interrogating an NSArray that appears to have a null entry

I have an iPad app that links to an SQL database to retrieve information in the following way:
NSString *strGetCodeUrl = [NSString stringWithFormat:#"%#", #"http://website/getdevicecode.php?device=" , deviceName];
NSArray *deviceArray = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:strGetCodeUrl]];
This works when there is a device that matches and it retrieves the required information. However if there is not a match it returns
("")
The array however appears to have one record. Ideally I would like to stop this from happening and for the array to be empty if there is no match. Alternatively (although not very tidy) I could check the length of the entry at index 0 but I am struggling with this method.
NSString *deviceCode = [deviceArray objectAtIndex:0];
if ( [deviceCode length] == 0)
{
device does not exist
}
Any advice gratefully received.
What about this:
NSString *deviceCode = [deviceArray objectAtIndex:0];
if ([deviceCode isEqualToString:#""])
{
device does not exist
}
I don't think you can tell the init method to leave out empty strings...
However, you can do this:
NSString *strGetCodeUrl = [NSString stringWithFormat:#"%#", #"http://website/getdevicecode.php?device=" , deviceName];
NSArray *deviceArray = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:strGetCodeUrl]];
[deviceArray removeObject:#""];
Which also isn't as tidy as perhaps you were hoping for, but it will remove all empty strings. But at least its just 1 line of code as opposed to about 3 for the if
Per the documentation:
Removes all occurrences in the array of a given object.

UIDocument loadFromContents fails with EXC_BAD_ACCESS [duplicate]

This question already has an answer here:
EXC_BAD_ACCESS using iCloud on multiple devices
(1 answer)
Closed 9 years ago.
I have a pretty simple IOS app using iCloud document storage. Everything was working and then at some point I began encountering a EXC_BAD_ACCESS error in my document load method for at least one iCloud document, although most files load just fine.
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
file = (NSFileWrapper*) contents;
NSFileWrapper *infoFile = [[file fileWrappers] objectForKey:InfoFile];
NSData *infoData = [infoFile regularFileContents];
if(nil != infoData) {
NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0;
NSError *propertyListError;
// EXC_BAD_ACCESS occurs here
NSDictionary *dictionary = [NSPropertyListSerialization propertyListWithData:infoData options:NSPropertyListImmutable format:&format error:&propertyListError];
if(nil == propertyListError) {
_name = [dictionary objectForKey:#"name"];
_date = [dictionary objectForKey:#"date"];
_index = [dictionary objectForKey:#"index"];
_paperSize = [GritzPaperSizeEnum enumWithType:[dictionary objectForKey:#"paperSize"]];
TFLog(#"loading doc %#", _name);
_pages = [[NSMutableArray alloc] init];
for (NSString *key in file.fileWrappers) {
NSFileWrapper *subDir = [[file fileWrappers] objectForKey:key];
if(subDir.isDirectory) {
GritzPage *page = [[GritzPage alloc] initFromFile:subDir];
[_pages addObject:page];
}
}
_currentPage = [_pages objectAtIndex:0];
return YES;
}
}
return NO;
}
I would expect that I can 'catch' and handle bad data and ignore the corrupt file; but I can't seem to figure out how. A EXC_BAD_ACCESS error causes the app to crash.
What should I be doing differently to determine ahead of time that the data or file is going to fail and skip it (or delete it).
verify it is a NSFileWrapper using isKindOfClass, else treating it as one is weird (also look at the given typeName :))
using a #try { .. } #catch construct to catch any exception wont work in THIS case though as you cause a BAD_ACCESS which is a UNIX SIGNAL
The NSPropertyListFormat variable format should be declare as point. And i think you should call the propertyListWithData: Method with format as pointer not with the address of format.

Resources