I'm trying to get some app info using Apple API, that gives me an JSON file containing many objects.
I tried to determine the type of the app (Universal/iPhone only/iPad only) like this
if(([[appDetails objectForKey:#"screenshotUrls"] count]>0) && ([[appDetails objectForKey:#"ipadScreenshotUrls"] count]>0))
{
cell.appDeviceLabel.text = #"Universal";
cell.appDeviceLabel.backgroundColor = [UIColor colorWithRed:0.012 green:0.467 blue:0.784 alpha:1];
}
else if(([[appDetails objectForKey:#"screenshotUrls"] count]==0) && ([[appDetails objectForKey:#"ipadScreenshotUrls"] count]>0))
{
cell.appDeviceLabel.text = #"iPad";
cell.appDeviceLabel.backgroundColor = [UIColor colorWithRed:0.941 green:0.58 blue:0.016 alpha:1];
}
else if(([[appDetails objectForKey:#"screenshotUrls"] count]>0) && ([[appDetails objectForKey:#"ipadScreenshotUrls"] count]==0))
{
cell.appDeviceLabel.text = #"iPhone";
cell.appDeviceLabel.backgroundColor = [UIColor colorWithRed:0.016 green:0.459 blue:0.129 alpha:1];
}
Note : screenshotUrls is an array containing the images for the iphone version
ipadScreenshotUrls is the one for iPad photos.
I used the code above in my app and Apple accept it, but I get crash reports showing a problem at these lines.
May be because I'm testing the count of an array that is not found? because if the app is iphone only for exemple, the array for iPad images won't exist. Any idea how can I solve this ?
Thanks.
Does the JSON contains the value null for some key?
If yes, this value is converted to NSNull in Obj-C and any method passed to this objects results in a crash. (NSNull is different from nil in this respect.)
I usually encounter crashes with JSON in Obj-C for this reason very frequently. You should put a check in place before using any value.
if (value == (typecast)[NSNull null]) {
// use the value
}
Note that type casting is done only to avoid compiler warning.
Related
Preamble: I'm working with some code I didn't write and I have next to no knowledge of iOS.
The app has a webview which handles most of the app; internal links are loaded in the webview and links to other sites are opened in Safari which is expected. However, embedding a google maps also causes safari to open. I found the relevant controller code, I think, and it looks like this:
// a bit higher up
static NSString *const siteURL = #"http://my.example.com"
if (![request.URL.absoluteString hasPrefix:siteUrl])
{
if ([[UIApplication sharedApplication] canOpenURL:request.URL])
{
[[UIApplication sharedApplication] openURL:request.URL];
}
return NO;
}
Now because 'maps.google.com' doesn't start with 'my.example.com' it farms out to safari - and that makes sense to me. However, I tried modifying this:
static NSString *const googleMaps = #"http://maps.google.com"
if (! ([request.URL.absoluteString hasPrefix:siteUrl] || [request.URL.absoluteString hasPrefix:googleMaps]))
{
if ([[UIApplication sharedApplication] canOpenURL:request.URL])
{
[[UIApplication sharedApplication] openURL:request.URL];
}
return NO;
}
This also works. But I need to add some more URLs. Is my only choice to keep adding 'or' and string constants? Or is there a way to make an array and say "if it matches any of these"? Sorry if this question seems simplistic.
In PHP I'd just do something like in_array('string', $array) as my condition, I guess I'm looking for how to do this in iOS
One solution would be to maintain a NSSet of objects, being all the strings that you want to be handled in your internal webview.
NSSet *internalURLSet = [NSSet setWithObjects:#"http://my.example.com", #"http://maps.google.com", nil];
Use a mutable set if you will be adding to the set over time but you will likely know which URLs you want to handle internally as soon as your app starts and this will not change. If you are adding sets as the app runs then add them to the mutable set as they become known.
When you check a URL, use [internalURLSet containsObject:urlToTest] and it will match any of the strings you have added to the set.
Checking against the set is very efficient. Adding objects to the set is less so, but you will very likely add URLs to the set only once while you check many times.
(Doing indexOfObject: against a NSArray is very inefficient...)
You could try using a predicate. Here's a tutorial link with an example (older, but should still work):
http://arstechnica.com/apple/2009/04/cocoa-dev-the-joy-of-nspredicates-and-matching-strings/
I do a lot of .NET development too and always miss LINQ when I go back to iOS. Predicates help a little, more lines of code but handy since you can do all kinds of searches.
I see your problem. In IOS, don't have a function in_array('string',$array) to check a string is contained in array.
I have a suggestion: you create a array with all urls. And implement 'for' or 'while' loop to get value of array check it. If it's same, you call function OpenUrl.
You could do this by creating an NSArray of your URLS and then using the indexOfObjectPassingTest: method to test your URL against each one in the array. It would go something like this:
NSArray *testURLs = #[ #"http://maps.google.com", #"http://www.example.com" ];
NSString *urlString = request.URL.absoluteString;
BOOL urlMatchedTest = NO;
for ( NSString *testURL in testURLs ) {
if [urlString hasPrefix:testURL] ) {
urlMatchedTest = YES;
}
}
if ( urlMatchedTest ){
// the url contains one of the test URLS
} else {
// it didn't match any of the test URLs
}
I am new to IPhone programming and am I having trouble solving the following memory leak.
while(numDeckCounter < numDecks){
int cardCounter=1;
for (int i =1; i<=52; i++) {
tempCard = [Card new]; //leaks tool says that this is leaking object
if(i>=1 && i<=13)
{
tempCard.suit = CLUBS;
tempCard.faceValue = cardCounter;
[deckArr addObject:tempCard]; //reference count 2
cardCounter++;
}
else if(i>=14 && i<=26)
{
tempCard.suit = DIAMONDS;
tempCard.faceValue = cardCounter;
[deckArr addObject:tempCard];
cardCounter++;
}
else if(i>=27 && i<=39)
{
tempCard.suit = HEARTS;
tempCard.faceValue = cardCounter;
[deckArr addObject:tempCard];
cardCounter++;
}
else
{
tempCard.suit = SPADES;
tempCard.faceValue = cardCounter;
[deckArr addObject:tempCard];
cardCounter++;
}
if(cardCounter ==14){
cardCounter=1;
}
[tempCard release]; //this causes an EXC_BAD_ACCESS -reference count should be 1
}
numDeckCounter++;
}
I was under the impression that adding an object to the array would increase its reference count by one, then it would be safe to release the object you just added because it would not be deallocated until the array was released bumping which would then release each object in the array. This is when the object should finally be deallocated.
When I add the [tempCard release]; it crashes my app because it can't access the memory location because it has already been deallocated.
From everything I have read, I think what I said above is true. Someone please correct me if I am wrong. Thanks.
I don't see any leaks in the code you presented. (There are some opportunities to slim it down, though, say by moving the identical operations out of the conditionals).
The Leaks tool output is pretty tricky to read. Is it possible that the Card object is leaking one of it's ivars?
Instead of leaks, run static analysis on your product (Product->Analyze). I think it will flag a different part of your code.
Perhaps try [[Card alloc] init] instead of [Card new]. This is just a guess. However trying the IMO more common method of object creation could be helpful.
Check this out: Use of alloc init instead of new
You could also try removing all the code for adding the Cards to the array. So you'd essentially have:
card = [Card new];
[card release];
This could help you find memory issues associated w/ the array retaining the object perhaps?
I've a UITextField say txtTitle. I want to check for not blank of that field at the time of inserting data into database.
For that I written
if(![txtTitle.text isEqualToString:#""])
{
//Save
}
But where I am shocked is its not working! I did these type of checking before and its working properly. But not with this case. So that I checking it using following,
if(txtTitle.text!=NULL)
{
//Save
}
It's working properly.
Now here I am confusing about this. I used to print NSLog(#"%#",txtTitle.text) without inputting anything into it. Its printed (null).
Someone please justify the difference between two IF conditions.
Thanks
Maybe you can check for the length property of the string instead, using
if([txtTitle.text length] > 0){
// Save
}
I think the difference is between a completely uninitialized string and a string that has been initialized, but is simply empty.
Hope this helps
#Hemang
As you have mentioned that NSLog gives you (null)..you have to compare like
[txtTitle.text isEqualToString:#"(null)"]
other wise use
if([txtTitle.text length] > 0)
{
}
In my app i'm using a users feeds to retrieve al the post done by my app. I simply retrieve al the posts, and the compare on every post the id number of the app.
This work ok. But i've found a bug in this method. Since the application node isn't always consistent. Normally when there is a post which is not done by an app, the entry in the dictionary just says (null), there isn't any data. This doesn't give any problems.
But there is an app which has other data in the this application node. This one has data in this node which specifically says (note the difference between () and <> ). But I can't seem any way to check if the dictionary with that post has in it. i've tried the following:
NSDictionary *resultPost1 =[resultPost objectForKey:#"application"];
NSLog(#"result%#", [resultPost objectForKey:#"application"]);
if ([resultPost1 count] != 0) {
This one gives a sigabrt, with the following nslog before the sigabrt:
result(null)
result{
id = 1957711133323244365557378;
name = "app";
}
result< null > (added space for visibility)
I've also tried isEqualtoString:#"< null>" Also without success.
It looks like sometimes, their is an dictionary in the application node, and sometimes a string .
Anyone has clue??? Thanks!!!
You will have to do some checking as you don't have a guarantee as to what sort of object is returned from the dictionary.
NSDictionary *resultPost1 = [resultPost objectForKey:#"application"];
if ([[resultPost1 class] isKindOfClass:[NSDictionary class]) {
//Treat as a dictionary
}
else if ([[resultPost1 class] isKindOfClass:[NSString class]) {
//Treat as a string
}
else if ([resultPost1 isEqual:[NSNull null] || !resultPost) {
//Treat as Null, note the json library Facebook uses might set
//a json NULL into a NSNull object instead of nil
}
i'm new to iphone programming and i encountered/noticed some problems while i was coding. which was after i typed a statement like
if (label.text > label2.text) {
do something...}
however, after typing my application can be compiled and run however when i try to validate it by comparing the values, my specified actions can run and i can change my image view image, however the conditions is not true but the specified actions can be run. Do enlighten me thanks! i will post my codes at the bottom, do comment if you spot any better practices? thanks once again.
Oh and how can i specify to check in my label that the default value is not "Label" or empty because i would like the values to be populated with number before commencing.
-(IBAction) beginMatch {
if (resultP1.text, resultP2.text = #"Label") {
errorMsg.text = #"Please Press Roll (:";
}
else
if (resultP1.text > resultP2.text) {
MG = [MainGameController alloc];
MG.player1 = playerName.text;
}
else {
MG.player1 = playerNameP2.text;
}
[self.view addSubview:MG.view];
}
this is one example that it does not work i have another one which is below.
-(IBAction) btn:(id) sender {
ptc = [playerTurnController alloc];
if (ptc.player1name = MGplayerName.text) {
if (lblDiceResultP1.text > lblDiceResultP2.text) {
img.image = [UIImage imageNamed:#"yellow.png"];
}
else if (ptc.player2name = MGplayerName.text) {
img.image = [UIImage imageNamed:#"Blue.png"];
}
}
}
Thank you.
Your code contains quite a few errors. You're trying to compare NSString values with ">", you're using the comma operator and = operator incorrectly, and you're allocating new objects in (what look to be) the wrong places.
You really should work your way through the introductory documentation on Apple's developer website first:
Learning Objective-C: A Primer
and
Your First iPhone Application
In here you're comparing string (alphabetically) addresses:
lblDiceResultP1.text > lblDiceResultP2.text
You probably want to extract NSNumbers of out the strings and compare the numeric values.
This here is an assignment and not a comparison:
ptc.player2name = MGplayerName.text
You probably meant to use == which is also wrong.
NSStrings are compared with the isEqualToString e.g.
NSString * s1 = #"String One";
NSString * s2 = #"String Two";
if([s1 isEqualToString:s2])
// do something when strings are equal