I am trying to read a plist from a server. I know for a fact that the plist is valid since when I go to the URL i get the plist downloaded and the plist editor shows me my array properly. Now when I use the following code I get now array populated. I see the data is in there. In debugger i see that plistData has value however the array petListArray has no data at all. What is wrong?
- (void)getPetListRequestFinished:(ASIHTTPRequest *)request
{
NSData *plistData = [request responseData];
NSError *error = nil;
NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0;
NSArray *petListArray = (NSArray *)[NSPropertyListSerialization propertyListWithData:plistData
options:(NSPropertyListReadOptions)NSPropertyListImmutable format:&format error:(NSError **)error];
if(error){
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"GetPetListError" message:[NSString stringWithFormat:#"getPettListRequestFinished_new deserialization error: error = %#", error]
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
----snip-------
You are passing the wrong type of argument for the error: slot of - NSPropertyListSerialization propertyListWithData:options:format:error:. Try:
[NSPropertyListSerialization propertyListWithData: plistData
options: (NSPropertyListReadOptions) NSPropertyListImmutable
format: &format
error: &error]
(edited after checking docs)
Related
Here's the scenario; I have a JSON response from a API. I obtain a response from the API as follows:
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://apitest.maranatha.org/api/SiteGroupStagings?countryId=%i",[country getCountryID]]]];
[request setHTTPMethod:#"GET"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setValue:token forHTTPHeaderField:#"Token"];
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil];
if (returnData) {
NSDictionary* jsonResponse = [NSJSONSerialization
JSONObjectWithData:returnData options:NSJSONReadingMutableContainers
error:&error];
}
The API will return a JSON array of objects when the API call is correct, the return looks like this:
[
{},
{},
...
]
If there is any problem processing the request on the server side (other than lack of internet connectivity on the client side), the response from the API is a as follows:
{
"message": "In lorem ipsum"
}
I want to check if that key/value pair is present, to be able to alert the user, and not attempt to process the response which would cause an exception to occur.
I've tried the following approach, but it doesn't seem to work, it seems it can always find a message key, even when the JSON response is an array of objects.
if ([jsonResponse valueForKey:#"message"]) {
NSLog(#"%#", [jsonResponse valueForKey:#"message"]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:[jsonResponse valueForKey:#"message"]
delegate:self
cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
}
else {
//consume the JSON response
}
How can I successfully check if the response from the API contains the message key/value pair?
It sounds like your server API returns JSON entities with a key "message" for successful requests as well, correct? If that's the case, maybe try this:
if (jsonResponse[#"message"] &&
[jsonResponse[#"message"] isKindOfClass:[NSString class]] &&
[jsonResponse[#"message"] isEqualToString:#"In lorem ipsum"])
{
// Alert
}
This should give you better (but not necessarily complete) protection against runtime variance in the content of the JSON entity as a whole.
Thanks to #fullofsquirrels I got an idea of how to solve the problem.
If a JSON is an JSON Array, [NSJSONSerialization] will make it a NSArray, so the easiest way was to check if my response was an array, or if it wasn't. Here's my solution.
if (![jsonResponse isKindOfClass:[NSArray class]]) {
NSLog(#"%#", [jsonResponse valueForKey:#"message"]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:[jsonResponse valueForKey:#"message"]
delegate:self
cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
}
else {
//consume API
}
I am making a call to twitters API to load some tweets for a specific section of my app.
A small chunk of users are reporting a crash when loading the tweets view, while the rest have no problem at all.
I have submitted the code to Apple Tech Support and they responded letting me know that NSJSONSerialization can sometimes return a NSArray or NSDictionary.
Obviously it will throw an error is objectAtIndex: is called on an NSDictionary object, which I believe is the culprit for all of my users.
The partial solution is to detect if it is an Array or NSDictionary.
Here is where I am at now:
id feedData = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonError];
if ([feedData isKindOfClass:[NSArray class]]) {
//Is array
} else if ([feedData isKindOfClass:[NSDictionary class]]) {
//is dictionary
}
I basically need an NSArray every single time. So in the is array block, I basically just use the feedData, but in NSDictionary, how can I convert it to an NSArray that will match the structure I need.
Honestly the biggest issue is that I cannot see what the NSDictionary structure looks like because none of my testing devices or simulator return the NSDictionary data, they all return an NSArray.
Here is what the entire getUserFeed method that sends the request to twitter looks like:
// Get the twitter feed
NSURL *requestURL = [NSURL URLWithString:TW_API_TIMELINE];
// Set up proper parameters
NSMutableDictionary *timelineParameters = [[NSMutableDictionary alloc] init];
[timelineParameters setObject:kNumTweets forKey:#"count"];
[timelineParameters setObject:#"1" forKey:#"include_entities"];
// Create the Social Request
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:requestURL parameters:timelineParameters];
postRequest.account = self.delegate.userAccount;
// Perform the request
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
// Check if we reached the reate limit
if ([urlResponse statusCode] == 429) {
// Rate limit reached
// Display an alert letting the user know we have hit the rate limit
UIAlertView *twitterAlert = [[UIAlertView alloc] initWithTitle:kRateLimitTitle
message:kRateLimitMessage
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[twitterAlert show];
// Stop animating the pull to refresh if it is animating
[self.feedTableView.pullToRefreshView stopAnimating];
return;
}
// Check if there was an error
if (error) {
NSLog(#"Error: %#", error.localizedDescription);
// Stop animating the pull to refresh if it is animating
[self.feedTableView.pullToRefreshView stopAnimating];
return;
}
// Check if there is some response data
if (responseData) {
NSError *jsonError = nil;
id feedData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&jsonError];
if ([feedData isKindOfClass:[NSArray class]]) {
//Is array
NSLog(#"It's an Array");
} else if ([feedData isKindOfClass:[NSDictionary class]]) {
//Is dictionary
NSLog(#"It's a Dictionary");
} else {
//is something else
}
if (!jsonError) {
[self gatherTweetsFromArray:feedData];
} else {
// Stop animating the pull to refresh if it is animating
[self.feedTableView.pullToRefreshView stopAnimating];
// Alert the user with the error
UIAlertView *twitterAlert = [[UIAlertView alloc] initWithTitle:kErrorTitle
message:kErrorMessage
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[twitterAlert show];
}
} else {
// Stop animating the pull to refresh if it is animating
[self.feedTableView.pullToRefreshView stopAnimating];
// Alert the user with the error
UIAlertView *twitterAlert = [[UIAlertView alloc] initWithTitle:kErrorTitle
message:kErrorMessage
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[twitterAlert show];
}
});
}];
This is a MAJOR bug and I need to squash it, so any ideas or information will be greatly appreciated! Thank you!
There are some questions I have found but the answers don't work with my code, therefore I ask a question of my own.
My objective is reloading a table view from a detailviewcontroller. I tap a cell, go to the detail and when I return to the table I want it updated. The thing is it doesn't update. So I decided it would be better to go back to the rootviewcontroller when certain thing happens on the detailviewcontroller but it still didn't work.
I am open to suggestions and advice feel free to comment!!
I download a video and when the video is downloaded I update the tableView.
Here is the code I am using:
I use MKNetworkKit btw.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *cachesDirectory = [paths objectAtIndex:0];
NSString *downloadPath = [cachesDirectory stringByAppendingPathComponent:videoName];
self.downloadOperation = [ApplicationDelegate.mainDownloader downloadVideoFrom:finalAddress
toFile:downloadPath];
[self.downloadOperation onDownloadProgressChanged:^(double progress) {
//DLog(#"%.2f", progress*100.0);
//self.downloadProgressBar.progress = progress;
}];
[self.downloadOperation onCompletion:^(MKNetworkOperation* completedRequest) {
//THIS DOES NOT WORK, dismissModalViewControllerAnimated.
[[ApplicationDelegate.window rootViewController] dismissModalViewControllerAnimated:YES];
DLog(#"COMPLETED REQUEST: %#", completedRequest);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Download completed"
message:#"The file is in your photo and video Library"
delegate:nil
cancelButtonTitle:NSLocalizedString(#"Thank you WebSurg!!", #"")
otherButtonTitles:nil];
[alert show];
// THIS IS WHAT I PREVIOUSLY TRIED.
// NSDictionary *tableSecData = ApplicationDelegate.videoLibraryController.tableSectionData;
// NSMutableArray *tempValuesDownloaded = [tableSecData objectForKey:#"Downloaded videos"];
// NSMutableArray *tempValuesUndownloaded = [tableSecData objectForKey:#"Undownloaded videos"];
// for (NSArray *videoArray in tempValuesUndownloaded) {
// if ([[videoArray objectAtIndex:0] isEqualToString:self.videoDetailTitle.text]) {
// [tempValuesUndownloaded removeObject:videoArray];
// [tempValuesDownloaded addObject:videoArray];
// }
// }
// [ApplicationDelegate.videoLibraryController.tableSectionData removeAllObjects];
// ApplicationDelegate.videoLibraryController.tableSectionData = [NSMutableDictionary dictionaryWithObjectsAndKeys:tempValuesDownloaded, #"Downloaded videos", tempValuesUndownloaded, #"Undownloaded videos", nil];
// [ApplicationDelegate.videoLibraryController.mainTableView reloadData];
}
onError:^(NSError* error) {
DLog(#"%#", error);
[[[UIAlertView alloc] initWithTitle:#"Download failed" message:#"The download failed because of a connection error please try again" delegate:nil cancelButtonTitle:NSLocalizedString(#"Dismiss", #"") otherButtonTitles:nil] show];
}];
} else {
UIAlertView *failureAlert=[[UIAlertView alloc] initWithTitle:#"Download status" message:#"Download failed, not enough free space." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil, nil];
[failureAlert show];
}
I would like to transfer (send and receive) the file using XMPP Framework in Objective C (from iPhone to iPhone).Is there any tutorial which explains the detail steps to do this.
File Transfer using XMPPFramework
This application is merely a brief demo of how to use the file transfer extension of the XMPPFramework.
A detailed blog post can be found here.
My changes have been merged into the master branch of the XMPPFramework, so you'll want to include the latest version in your project.
Both incoming file transfers and outgoing file transfers are functional within this demo, but I've left a significant amount of error-handling out, so you'll want to include that in your app.
Server Settings
In order for SOCKS5 to work properly, your server must be configured to handle proxy connections. I've only tested this using ejabberd, but these are the mod_proxy65 settings I used:
{mod_proxy65, [
{ip, {0,0,0,0}},
{hostname, "myhostnamehere"},
{port, 7777},
{access, all},
{shaper, c2s_shaper}
]},
If you're unable to get the proxy functioning, you always have the option to set disableSOCKS5 = YES, which will force an IBB transfer instead. This is slower, but it's very widely supported.
Usage
Incoming File Transfers
Instantiate a new XMPPIncomingFileTransfer, activate it, add a delegate, and wait for a file transfer request.
_xmppIncomingFileTransfer = [XMPPIncomingFileTransfer new];
[_xmppIncomingFileTransfer activate:_xmppStream];
[_xmppIncomingFileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];
Responding to disco#info queries and the like are handled for you. You'll get a delegate call when an SI offer is received, at which point you can decide whether or not you wish to accept. You can also set autoAcceptFileTransfers = YES and you won't need to call acceptSIOffer: yourself.
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender
didFailWithError:(NSError *)error
{
DDLogVerbose(#"%#: Incoming file transfer failed with error: %#", THIS_FILE, error);
}
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender
didReceiveSIOffer:(XMPPIQ *)offer
{
DDLogVerbose(#"%#: Incoming file transfer did receive SI offer. Accepting...", THIS_FILE);
[sender acceptSIOffer:offer];
}
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender
didSucceedWithData:(NSData *)data
named:(NSString *)name
{
DDLogVerbose(#"%#: Incoming file transfer did succeed.", THIS_FILE);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *fullPath = [[paths lastObject] stringByAppendingPathComponent:name];
[data writeToFile:fullPath options:0 error:nil];
DDLogVerbose(#"%#: Data was written to the path: %#", THIS_FILE, fullPath);
}
Outgoing File Transfers
To start a new outgoing file transfer, simply create an instance of XMPPOutgoingFileTransfer, activate it, add a delegate, and send your data:
_fileTransfer = [[XMPPOutgoingFileTransfer alloc] initWithDispatchQueue:dispatch_get_main_queue()];
[_fileTransfer activate:[self appDelegate].xmppStream];
[_fileTransfer addDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *err;
if (![_fileTransfer sendData:data
named:filename
toRecipient:[XMPPJID jidWithString:recipient]
description:#"Baal's Soulstone, obviously."
error:&err]) {
DDLogInfo(#"You messed something up: %#", err);
}
The following delegate calls when get invoked when appropriate:
- (void)xmppOutgoingFileTransfer:(XMPPOutgoingFileTransfer *)sender
didFailWithError:(NSError *)error
{
DDLogInfo(#"Outgoing file transfer failed with error: %#", error);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"There was an error sending your file. See the logs."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
- (void)xmppOutgoingFileTransferDidSucceed:(XMPPOutgoingFileTransfer *)sender
{
DDLogVerbose(#"File transfer successful.");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Success!"
message:#"Your file was sent successfully."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"the response string is %#",responseString);
if ([responseString isEqualToString:#"No Data Available"] )
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Message"
message:#"No data Avilable in server"delegate:self cancelButtonTitle:nil otherButtonTitles:
#"OK", nil];
[alert show];
[alert release];
}
in my connectiondidfinishloadin delegate method i have the above code. when the server replies there is no data , it does not go into the if loop for some reason. not sure why. any hint will be greatly appreciated.
Thank You.
ResponseString is probably null/nil and runtime knows that that if statement will b false because of this, so skips it. You should see this with your NSLog(#"the response string is %#",responseString);
On a side note, Charles is a good debugging tool for this- you can see the http info as it goes out and comes in.