NSInvalidArgumentException / unrecognized selector sent to instance - ios

My app is crashing for some reason after pressing a tableView cell. It doesn't crash the first time but it crashes after couple of time navigating through the table.
Here is my console:
After doing some debugging, if I removed this line of code, it doesn't crash.
[[NSTimeZone localTimeZone] secondsFromGMT] / 30
Here is my viewDidLoad for DetailViewController:
titleLabel.text = [newsArticle objectForKey:#"Series"];
timeLabel.text = [newsArticle objectForKey:#"Duration"];
guestsLabel.text = [newsArticle objectForKey:#"Guests"];
descTextView.text = [newsArticle objectForKey:#"Content"];
"newsArticle" is: NSDictionary variable.
Any ideas why is this happening? Any advice will be appreciated :)
Thanks

As the error message states, you are attempting to basically call setText: with NSNull instead of a string; i.e. [someTextView setText:[NSNull null]];.
Frame #7 is from your app, which is called JSON. So, assuming you are loading a JSON document.
Your JSON document has a NULL value where your code expects there to be a string. Go look at the JSON and figure out where the document contains an unexpected construct.
While fixing your JSON document is part of the solution, I would also suggest hardening your code against such failures.

Found the issue:
There were some items that weren't available on all options so I I used this code to prevent it from crashing and letting the user know that there is no data available:
if (![dictionary objectForKey:#"Guests"] isKindOfClass:[NSNull class]]) {
gueststxt.text = [newsArticle objectForKey:#"Guests"];
} else {
gueststxt.text = #"No Data";
}
Just posted this in case someone comes across this issue.

Related

What is the proper syntax for Google Analytics Exception tracking in Swift?

I'm trying to use Exception Tracking for my app in Google Analytics.
https://developers.google.com/analytics/devguides/collection/ios/v3/exceptions
I am just trying to figure out the syntax for this in Swift (not super familiar with Obj-C):
#try {
// Request some scores from the network.
NSArray *highScores = [self getHighScoresFromCloud];
}
#catch (NSException *exception) {
// May return nil if a tracker has not already been initialized with a
// property ID.
id tracker = [[GAI sharedInstance] defaultTracker];
[tracker send:[[GAIDictionaryBuilder
createExceptionWithDescription:#"Connection timout %d: %#", connectionError, errorDescription // Exception description. May be truncated to 100 chars.
withFatal:#NO] build]]; // isFatal (required). NO indicates non-fatal exception.
}
I have set up my tracker okay, and it is working fine saving other data to GA, it is just the syntax calling createExceptionWithDescription() in Swift that I'm not certain of.
There sure doesn't appear to be much in the way of examples / docs for using Swift for Google Analytics... =/ If you know of any, please let me know!
Thanks.
Thanks, David Wong, your post helped a lot to get me on the right track with the syntax.
This post also helped me a lot:
Cannot convert value of type 'NSMutableDictionary' to type '[NSObject: AnyObject]' in coercion for google ios Analytics
This is what ended up working for me:
let tracker = GAI.sharedInstance().defaultTracker
let eventTracker: NSObject = GAIDictionaryBuilder.createExceptionWithDescription("No internet connection.", withFatal: false).build()
tracker.send(eventTracker as! [NSObject : AnyObject])
Thanks again!
I'd imagine its something along the lines of:
let dictionaryToSend = GAIDictionaryBuilder.createExceptionWithDescription("Connection timeout \(connectionError): \(errorDescription)", withFatal: NSNumber(bool: false)).build()
If its a class function in Obj-C written like
[GAIDictionaryBuilder createExceptionWithDescription:...]; // objc
Its written like
GAIDictionaryBuilder.createExceptionWithDescription(...); // swift
Each of the colons in obj-c denotes an argument variable.
// Broken into two lines to make it easier to read
[GAIDictionaryBuilder createExceptionWithDescription: #"String here"
withFatal: #NO];
You can do a similar thing in swift:
//Comma separated
GAIDictionaryBuilder.createExceptionWithDescription("Description String",
withFatal: NSNumber(bool:false));
I suggest you learn a little bit of the ObjC messaging syntax since a lot of iOS code is still in ObjC but don't worry a huge amount about it. Swift is a better language.

XMPP last activity time shows in strange form. How to fix it?

I have added to the Bridging file XMPPLastActivity.h file and in Swift file I did the next:
let abc = XMPPLastActivity()
let a = abc.sendLastActivityQueryToJID(user.jid)
print("A is: \(a)")
but it returns me response like
1686F740-C50C-477B-BAE2-02C897826B97
How can I return it in human readable format?
UPDATE
Please, join to chat to help me: https://chat.stackoverflow.com/rooms/90972/ios-errors-fixing
You will get a response via the XMPP delegate. You need to implement:
func xmppLastActivityDidReceiveResponse(sender:XMPPLastActivity, response:XMPPIQ)
And get the time lag to now in seconds with:
let time : NSUInteger = response.lastActivitySeconds
Then it's NSDate all the way.

Parse.com always returns a maximum of 100 records, same having "limit = 1000"

I have an iOS app that receives data from the PARSE.COM.
How did not know nothing about 'parse.com' , I used the tutorial "http://www.raywenderlich.com/15916/how-to-synchronize-core-data-with-a-web-service-part-1".
The synchronization occurs only from the server to the device (iOS), and one time the object is added to the device, it should not be inserted again.
Turns out I got 131 objects in a class and 145 in another, but the Parse.com always returns me the first 100 items, even those already are in the device (iOS).
The worst thing is that in my code I have a variable "limit" in "request" that should work, but does not work for me.
I need your help, please ...
Code:
- (NSMutableURLRequest *)GETRequestForAllRecordsOfClass:(NSString *)className updatedAfterDate:(NSDate *)updatedDate
{
NSMutableURLRequest *request = nil;
NSDictionary *paramters = nil;
if (updatedDate) {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.'999Z'"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"GMT"]];
NSString *jsonString = [NSString
stringWithFormat:#"{\"updatedAt\":{\"$gte\":{\"__type\":\"Date\",\"iso\":\"%#\"}}}",
[dateFormatter stringFromDate:updatedDate]];
//That's line of 'paramters' is from original tutorial Raywenderlich
// paramters = [NSDictionary dictionaryWithObject:jsonString forKey:#"where"];
//This line was add for the autors of tutorial in a comment from your blog, and he say that has work, but not for me =(
paramters = #{#"where" : jsonString, #"limit" : #(1000)};
}
request = [self GETRequestForClass:className parameters:paramters];
return request;
}​
The print of variable "request" after this method is this:
URL: https://api.parse.com/1/classes/Substancia?where=%7B%22updatedAt%22%3A%7B%22%24gte%22%3A%7B%22__type%22%3A%22Date%22%2C%22iso%22%3A%222014-09-23T02%3A13%3A01.999Z%22%7D%7D%7D&limit=1000
Why do same having the variable "LIMIT = 1000", the parse.com every returns me 100 items?
And even that returns 100 items, why do in the next time he does the "request" he it does not catch the next 100 since the other 100 registers earlier have already been entered?
Can anyone help me?
(Answering here since I don't have enough reputation to comment.)
For the limit=1000 not seeming to work: perhaps the "where" clause (i.e. constraining to items with updatedAt value >= 2014-09-23T02:13:01.999Z) is limiting results to less than 1000?
(To Ian's point) There is a 'skip' parameter that tells the API how many items to skip ahead, for pagination. i.e. limit=100&skip=100 to see page 2.
I'm not sure, but I think this is what you're looking for. A great solution on how to retrieve all the objects from Parse instead of the max limit i.e. 1000.

iCloud Sync - Core Data Duplicate Entries (Desperate Help)

I've been having this bug for several weeks already. I searched on many forums every reply on duplicates and I implemented some of the normal approaches and still it doesn't work properly.
So to give you some context I'm working on a recipe application that scraps html recipes from the web and stores it in core data, simple right? Well when the client asked adding support for iCloud Sync I though it was going to be easy specially working on iOS 7 only which solves most of the problems for you.
The problems arises when the app populates initial data in the application. I have two related entities called MainCategory[e1] and Category[e2], there is one to many relationship between them (e1 <->>> e2).
The first the app starts it will create 5 Main Categories and for each Main Category it will add 5 Categories
+ (BOOL)initialLoad
{
DLog(#"Initial Load");
//Create main and sub categories to database
NSDictionary * categoriesDic = #{
CAT_MEAL_TYPE: #[C_STARTER,C_MAINS,C_DESSERT,C_SOUPS,C_SALAD],
CAT_INGREDIENT: #[C_BEEF,C_CHICKEN,C_PASTA,C_SALMON,C_CHOCOLATE],
CAT_CUISINE : #[C_CHINESE,C_FRENCH,C_INDIAN,C_ITALIAN,C_MOROCCAN],
CAT_SEASON : #[C_CHRISTMAS,C_SUNDAY_ROAST,C_DINNER,C_BBQ,C_NIBBLES],
CAT_DIET : #[C_WHEATFREE,C_VEGETARIAN,C_LOW_FAT,C_LOW_GI,C_DAIRY_FREE]
};
NSArray * mainCategoryKeys = #[CAT_MEAL_TYPE,CAT_INGREDIENT,CAT_CUISINE,CAT_SEASON,CAT_DIET];
for(NSString * eachMainCategoryName in mainCategoryKeys)
{
//Create Main category
MainCategory * eachMainCategory = [MainCategory mainCategoryWithName:eachMainCategoryName];
NSArray * subCategories = [categoriesDic objectForKey:eachMainCategoryName];
//Create Sub categories and adds them to main category
for(NSString * eachCategoryName in subCategories)
{
/*Category got renamed to zCategory given it's a reserver name in the framework and
can not be used */
zCategory * eachCategory = [zCategory categoryWithName:eachCategoryName];
[eachMainCategory addCategoriesObject:eachCategory];
}
}
[((AppDelegate *)[UIApplication sharedApplication].delegate) saveContext];
return TRUE;
}`
Then after saving the context all this initial data will sync with the database in iCloud, so far so good. The problem comes when on the second device it runs the same initialLoad code and gets sync once again. The result is getting double MainCategories and Categories as many of you know this problem.
After reading several threads about how to remove them I used the dateCreated approach where you add a NSDate property to each entity so every time you create one instance it will have a timestamp to track which one is older and which one is newer. Then I simply add an observer from NSNotificationCenter checking the iCloud import notification NSPersistentStoreCoordinatorStoresDidChangeNotification and runs a timerCheck that after 5 seconds will execute on the mainThread a clean duplicates method.
- (void)checkTimer{
if(self.cleanTimer)
{
[self.cleanTimer invalidate];
self.cleanTimer = nil;
}//schedule timer to clean iCloud duplicates of database
self.cleanTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(cleanDuplicates:) userInfo:nil repeats:FALSE];
}
- (void)cleanDuplicates:(NSTimer*)timer{
[self performSelectorOnMainThread:#selector(cleanCron) withObject:nil waitUntilDone:TRUE];}
I'm invalidating the timer every time checkTimer method gets call in order to restart it again because you normally get several NSPersistentStoreCoordinatorStoresDidChangeNotification when content gets updated/inserted/deleted, this way I know it will run once after all the notifications have gone through.
btw cleanCron just calls a class method cleanDuplicates
- (void)cleanCron
{
[CTFetchCoreData cleanDuplicates];
}`
Here is where the non magic happens, I get all the MainCategories which will be 10 given they have been duplicated and order them with the oldest ones at the beginning, then it iterates and save them in an dictionary with their name as the key so whenever it finds another MainCategory with the same name it just deletes it. Btw in the relationship e1<->>e2 there is a cascade delete rule so every time you delete a MainCategory item it deletes all the related Categories with it so there shouldn't be a problem.
+ (BOOL)cleanDuplicates
{
#synchronized(self){
//Fetch mainCategories from coreData
NSArray * mainCategories = [CTFetchCoreData fetchAllMainCategories];
// Clean duplicate Main Categories
NSMutableDictionary * uniqueMainCatDic = [NSMutableDictionary dictionary];
// Sorts the array with the oldest dateCreated one
mainCategories = [mainCategories sortedArrayUsingComparator:^NSComparisonResult(MainCategory* obj1,MainCategory * obj2) {
if(obj1.dateCreated == nil || obj2.dateCreated == nil)
{
DLog(#"ERROR Date Created");
}
return [obj1.dateCreated compare:obj2.dateCreated];
}];
// if there are more than five MainCategories it procedes the clenaup
if(mainCategories.count > 5)
{
for(MainCategory* eachMainCat in mainCategories)
{
MainCategory * originalMainCat = [uniqueMainCatDic objectForKey:eachMainCat.name];
if( originalMainCat == nil)
{
DLog(#"-> %# = %#",eachMainCat.name, eachMainCat.dateCreated);
[uniqueMainCatDic setObject:eachMainCat forKey:eachMainCat.name];
}else{
// Clean duplicate Categories
[[self managedObjectContext] deleteObject:eachMainCat];
DLog(#"x %# = %#",eachMainCat.name, eachMainCat.dateCreated);
}
}
DLog(#"Cleaning Main Categories");
}
}
[[AppDelegate sharedInstance] saveContext];
return TRUE;
}
It turns out that after I run it on the second device I will get this output :
Sesame[4145:60b] -> Cuisine = 2014-02-06 16:15:38 +0000
Sesame[4145:60b] -> Meal = 2014-02-06 17:15:54 +0000
Sesame[4145:60b] x Meal = 2014-02-06 17:15:54 +0000
Sesame[4145:60b] -> Ingredients = 2014-02-06 17:15:54 +0000
Sesame[4145:60b] x Ingredients = 2014-02-06 17:15:54 +0000
Sesame[4145:60b] x Cuisine = 2014-02-06 17:15:54 +0000
Sesame[4145:60b] x Cuisine = 2014-02-06 17:15:54 +0000
Sesame[4145:60b] -> Occasion = 2014-02-06 17:15:54 +0000
Sesame[4145:60b] -> Diet = 2014-02-06 17:15:54 +0000
Sesame[4145:60b] x Diet = 2014-02-06 17:15:54 +0000
which means that the same MainCategories are getting deleted, they have the same timestamp! I'm wondering how iCloud gets the information merged.
Please if you know a better approach to clean duplicated apart from the dateCreated property please tell me because I've tried it a lot without luck, there should be a better approach.
Thanks in advance!
Update :
Finally I've managed to solve my problem after all, crazy as it sounds I was getting duplicate instances from iCloud! that's what the dates were the same. I just added an if to check if both dates are the same then don't delete the MainCategory, and so next time you open your app Core Data will refix the merge and update the database with the correct instances and different date values as it was supposed to be.
I can't see anything obviously wrong with your code, though I would recommend using UUIDs instead of dates for ordering your duplicates. But that is unlikely to be related to what you are seeing.
To be honest, it looks to me that Core Data is really messing things up. (Eg It also seems there are 3 Cuisine categories.)
I experienced this type of issue when working with Core Data sync if I tried to delete the cloud data files, and didn't give it time to thoroughly remove the files from all devices. You end up with old transaction logs in there, which trigger extra objects to be inserted.
Core Data also tries to handle all merging on its own. How that happens is anyone's guess.
Core Data + iCloud is a bit unusual in that it is one of the only sync frameworks which has no concept of global identity. There are actually good reasons for Apple not doing it, which are too subtle to discuss here, but it does make it difficult for developers. Deduping post-merge is an ugly solution IMO. Your store has to become invalid before it can become valid again.
I much prefer the approach of frameworks like Wasabi Sync, TICDS, and Ensembles, which all have a concept of global identity and do not require deduping as a result.
(Disclosure: I founded and develop the Ensembles framework)
Also avoid using this
NSMutableDictionary * uniqueMainCatDic = [NSMutableDictionary dictionary];
rather use
NSMutableDictionary * uniqueMainCatDic = [[NSMutableDictionary alloc] init];
I think your weirdness of duplicates may go away if you always alloc the mutable dictionary. It took me weeks to figure this out - not sure if its a bug.

"Dead Store" Warning in Xcode

I am getting a dead store warning when I analyze my project but the project does not crash.
Here is what I am doing
NSString *graphUrl = nil;
if ([graphArray count] == 1)
{
objTrial = [graphArray objectAtIndex:0];
graphUrl = #"http://chart.apis.google.com/chart?cht=s:nda&chf=bg,s,FFFFFF&chs=";
graphUrl = [graphUrl stringByAppendingString:#"&chd=t:"];
graphUrl = [graphUrl stringByAppendingString:objTrial.highValue];// get the dead store error here
}
else
{
//someother operation is done and a value is loaded to aURL
}
I get a dead store warning as mentioned in the code.. How can I prevent this?
It would be great if someone could help me out in this
The warning is telling you that the store that you do in the first line gets thrown away (i.e assigning an empty string to the variable and then reassigning it afterwards without using the original value). Just change the first line to the following and the warning should go away:
NSString *aUrl;
Edit:
you should change the line where you use it also:
aURL = [aValue copy];
"dead store" means something that's not used, or rather something useless.
You get it when you have a variable defined that you never do anything with. So, the Analyzer tells you that you have wasted some storage.
Here you haven't used the aUrl object after assigning it.
It won't cause any problems other than a few bytes of wasted memory. Of course if it's a large object that could be more.
Perhaps someone could chip in with knowledge of compilers, as compiler optimization might take care of dead stores in any case.
Dead Store is a value that is assigned but never used. There is nothing to worry about it. But if you can't control yourself from worrying ;-) you can change your code to,
NSString aUrl = nil;
if ([anArray count] == 1) {
// a value is store in aValue
// then that value is appended to aURL
aURL = [aURL stringByAppendingString:aValue];
} else {
aUrl = #"";
//someother operation is done and a value is loaded to aURL
}

Resources