Reputation: 3919
I'm trying to pull data from this url, but I am running into some problems. Why does this crash on the NSJSONSerialization
line? Is there a better way to download info from this website?
EDIT: I changed jsonArray from a NSArray to a NSDictionary, but it still crashes in the same spot. Is there a different way to download this data?
NSString *url=@"https://api.p04.simcity.com/simcity/rest/users/search/J3d1.json";
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSURLResponse *resp = nil;
NSError *err = nil;
NSData *response = [NSURLConnection sendSynchronousRequest: theRequest returningResponse: &resp error: &err];
NSDictionary *jsonArray = [NSJSONSerialization JSONObjectWithData: response options: NSJSONReadingMutableContainers error: &err];
NSLog(@"%@",jsonArray);
For reference, the JSON is:
{
"users": [
{
"uri": "/rest/user/20624",
"tutorialState": 0,
"nucleusId": 20624,
"id": 20624,
"screenName": "R3DEYEJ3D1",
"lastLogin": 1362666027000,
"isOnline": "true",
"avatarImage": "https://api.p04.simcity.com/simcity/rest/user/20624/avatar",
"cities_count": 0,
"canChat": "true"
},
{
"uri": "/rest/user/46326",
"tutorialState": 0,
"nucleusId": 46326,
"id": 46326,
"screenName": "J3D1_WARR10R",
"lastLogin": 1363336534000,
"isOnline": "false",
"avatarImage": "https://api.p04.simcity.com/simcity/rest/user/46326/avatar",
"cities_count": 0,
"canChat": "true"
}
]
}
Upvotes: 0
Views: 902
Reputation: 18363
Your server is presenting a certificate that isn't trusted by the OS - I had to use the -k
flag to curl
your JSON so should have thought of this earlier.
In order to get around this, you'll need to switch to using an asynchronous NSURLConnection
and implement its delegate methods. See this answer from this stack overflow question to implement a working solution to your parsing issue. I implemented it inside the app delegate, but you can for example put this in an asynchronous NSOperation
and use it there.
WARNING: This code will connect to any server, regardless of trust. This is asking for a man in the middle attack. Do not send sensitive information to a server when this type of trust is enabled. You MUST implement code to verify your server's identity, and replace the if (YES)
in - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
with a test against the result of that check.
My current understanding is that this should be done by including your certificate's public key in the app bundle as a DER file, and using SecCertificateCreateWithData
to create a SecCertficiateRef
that will be used as the input to SecTrustSetAnchorCertificates
. Then, SecTrustEvaluate
should be used to verify the identity. I base this understanding on my reading of the Certificate, Key, and Trust Services Reference and sample code found in this blog post on how to trust your own certificate.
@interface AppDelegate () <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
// This is needed to collect the response as it comes back from the server
@property (nonatomic, strong) NSMutableData *mutableResponseData;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Xcode template code for setting up window, ignore...
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil];
} else {
self.viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil];
}
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
// Instantiate our connection
NSString *urlString = @"https://api.p04.simcity.com/simcity/rest/users/search/J3d1.json";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
if(![NSURLConnection connectionWithRequest:request delegate:self]) {
NSLog(@"Handle an error case here.");
}
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// Prepare to recevie data from the connection
NSLog(@"did receive response: %@", response);
self.mutableResponseData = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Handle an error case here
NSLog(@"did fail with error: %@", error);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Build up the response data
[self.mutableResponseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *error = nil;
id JSONObject = [NSJSONSerialization JSONObjectWithData:self.mutableResponseData options: NSJSONReadingMutableContainers error:&error];
if (!JSONObject) {
// Handle error here
NSLog(@"error with JSON object");
}
else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
//we're in business
NSDictionary *dict = JSONObject;
NSLog(@"dict is %@", dict);
}
else {
// Handle case of other root JSON object class...
}
}
// The following two methods allow any credential to be used. THIS IS VULNERABLE TO MAN IN THE MIDDLE ATTACK IN ITS CURRENT FORM
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// WE ARE POTENTIALLY TRUSTING ANY CERTIFICATE HERE. Replace YES with verification of YOUR server's identity to avoid man in the middle attack.
// FOR ALL THAT'S GOOD DON'T SHIP THIS CODE
#warning Seriously, don't ship this.
if (YES) {
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
@end
Upvotes: 3
Reputation: 7113
Try put the url in the browser to make sure that gives you a valid JSON, also make sure the parsed data is Array or NSDictionary
Upvotes: 0
Reputation: 4558
NSJSONSerialization will always crash if the data is nil. You need to put a conditional in to see if that URL contains any data.
Upvotes: 0