Using RSSParser for Today Extension Widget - ios

I am trying to get latest data from a RSS URL and show its content in Today Widget extension , with RSSParser but the problem is it returns null ! here is my codes :
- (void)viewDidLoad {
[super viewDidLoad];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://myBlog.net/?feed=rss2"]];
[RSSParser parseRSSFeedForRequest:req success:^(NSArray *feedItems) {
[self setDataSource:feedItems];
} failure:^(NSError *error) {
[self setTitle:#"Error"];
NSLog(#"Error: %#",error);
}];
item = [self.dataSource objectAtIndex:0];
//this returns null:
NSLog(#"Title is %#",[item title]);
NSLog(#"Widget Runs");
}

Not familiar with the library, but the call pattern is familiar enough to assume that the request runs asynchronously. That means that one's normal assumption that code executes top to bottom doesn't hold.
- (void)viewDidLoad {
[super viewDidLoad];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://myBlog.net/?feed=rss2"]];
[RSSParser parseRSSFeedForRequest:req success:^(NSArray *feedItems) {
// this runs after the request completes
NSLog(#"Got here #2");
// moving your test code here will allow it to work
item = [self.dataSource objectAtIndex:0];
NSLog(#"Title is %#",[item title]);
} failure:^(NSError *error) {
[self setTitle:#"Error"];
NSLog(#"Error: %#",error);
}];
// this runs before the request begins
NSLog(#"Got here #1");
}

Related

Retrieving latest version of component from within AppDelegate fails

I am pretty new to iOS development, and entered a position where I need to maintain a large existing project in obj-c.
I have a sidebar-menu which is a webview. When program starts it makes a url request to check whether there is a newer version of the menu, and in that case retrieves the latest version.
Right now when the app runs for the first time it shows the old version, and from the second time and on it shows the current version.
When I tried debugging I've seen that the method that compares between local and remote version gets an empty value for the remote version. As far as I can understand it, the url request for the latest version is async, and therefore the code continues to execute before the request returns the current version.
Following an answer from StackOverflow, I've tried to call the getDataConfiguration method from within viewDidLoad instead of from AppDelegate, but that didn't work.
Would appreciate any help!
relevant code:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { .
...
[DataManager getDataConfiguration:^(DataConfiguration *dataConfiguration, NSError *error) {
[AppData sharedInstance].dataConfiguration=dataConfiguration;
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:dataConfiguration];
[standardDefaults setObject:encodedObject forKey:DATA_KEY];
[standardDefaults synchronize];
}];
[DataManager getProductMap:^(ProductsArray *products, NSError *error) {
[AppData sharedInstance].productsArray=products;
}];
DataManager.m
+(void)getDataConfiguration:(void (^)(DataConfiguration * dataConfiguration, NSError *error))completion
{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:[Configuration sharedInstance].infoJSONURL parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
DataConfiguration * dataConfiguration = [DataConfiguration modelObjectWithDictionary:responseObject];
completion(dataConfiguration,nil);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
+(void)updateHtmlFiles:(void (^)(NSError *error))completion{
float upToDateMenuVersion = [[AppData sharedInstance] dataConfiguration].general.menuVersion;
float localMenuVersion = [self getLocalMenuVersion];
if(upToDateMenuVersion != localMenuVersion){
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSString *url = [NSString stringWithFormat:#"%#?v=%f", [Configuration sharedInstance].menuHTMLFileURL, [[NSDate new] timeIntervalSince1970]];
[manager GET:url parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *htmlFiles = [userDefaults dictionaryForKey:#"HTML_FILES"];
NSMutableDictionary *mutableHtmlFiles = [NSMutableDictionary new];
NSString *myString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
[mutableHtmlFiles setValue:myString forKey:#"MENU"];
[userDefaults setObject:mutableHtmlFiles forKey:#"HTML_FILES"];
[self setLocalMenuVersion:upToDateMenuVersion];
completion(nil);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *htmlFiles = [userDefaults dictionaryForKey:#"HTML_FILES"];
if(htmlFiles == nil){
NSString *menuFile = [[NSBundle mainBundle] pathForResource:#"menu" ofType:#"html"];
htmlFiles = #{#"MENU":[NSString stringWithContentsOfFile:menuFile encoding:NSUTF8StringEncoding error:nil]};
[userDefaults setObject:htmlFiles forKey:#"HTML_FILES"];
}
NSLog(#"Error: %#", error);
}];
}
}
+(void) setLocalMenuVersion: (float) version{
[[NSUserDefaults standardUserDefaults] setFloat:version forKey:#"menuVersion"];
}
+(float) getLocalMenuVersion {
return [[NSUserDefaults standardUserDefaults] floatForKey:#"menuVersion"];
}
Menu.m
- (void)viewDidLoad {
[super viewDidLoad];
_firstLoad = YES;
...
[self initWebView];
}
-(void) initWebView {
if(_webView == nil){
_webView = [[WKWebView alloc] initWithFrame:_webViewPlaceholder.frame];
[_webView.scrollView setZoomScale:3 animated:YES];
_webView.navigationDelegate = self;
_webView.UIDelegate = self;
NSString *javaScriptText = #"document.body.style.zoom = 3;";
[_webView evaluateJavaScript:javaScriptText completionHandler:nil];
[self.view addSubview:_webView];
_webView.scrollView.bounces = NO;
[self updateHtml];
[AppData updateHeaderAndMenu:^(NSError *error){
[self updateHtml];
}];
}
}
- (void)viewDidAppear:(BOOL)animated{
_webView.frame = CGRectMake(_webViewPlaceholder.frame.origin.x,_webViewPlaceholder.frame.origin.y, _webViewPlaceholder.frame.size.width, _webViewPlaceholder.frame.size.height);
}
-(void)updateHtml{
NSDictionary *htmlFiles = [AppData getHeaderAndMenu];
NSString *menu = [htmlFiles objectForKey:#"MENU"];
[_webView loadHTMLString:menu baseURL: [[NSBundle mainBundle] bundleURL]];
}
AppData.m
+(void)updateHeaderAndMenu:(void (^)(NSError *error))completion{
[DataManager updateHtmlFiles:completion];
}
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
...
[AppData updateHeaderAndMenu:^(NSError *error){ [self loadHeader]; }];
_firstLoad = YES;
...
The updateHeaderAndMenu method has a completion block which is called after the async operation completes without an error.
I'm assuming ViewController.m holds a reference to Menu?
If that is the case, viewDidLoad calls the updateHeaderAndMenu method and will execute the completionBlock (if there is no error). In this block I can already see that a method is called loadHeader. You could call [self.menu updateHtml]; there and this would probably work.
...
[AppData updateHeaderAndMenu:^(NSError *error){
[self loadHeader];
// [self.menu updateHtml];
}];
_firstLoad = YES;
...
I'm doing some guess work here but I think this would update your webview after the DataManager completes the http request.
Edit:
As to the order of execution. Here is a breakdown:
This is the method definition in AppData
+(void)updateHeaderAndMenu:(void (^)(NSError *error))completion{
[DataManager updateHtmlFiles:completion];
}
You can see completion (which is a block parameter) is passed on to the updateHtmlFiles method in DataManager:
+(void)updateHtmlFiles:(void (^)(NSError *error))completion{
...
completion(nil);
...
}
Eventually the completion parameter (which is a block) is called when the async http request completes. You can look at blocks as kind of inline methods which can be passed as a parameter. Google working with blocks ios to see the official Apple documentation for this.
So the order of execution is:
Menu calls updateHeaderAndMenu in AppData
which calls updateHtmlFiles in DataManager and passes on completion
http request completes and calls completion.
the content of the block is executed all the way back in Menu which is:
{
[self loadHeader];
// [self.menu updateHtml];
}
loadHeader is executed ...
If you want to get a better overview of what is called when, you can use breakpoints inside your code.

iOS: AVCaptureOutput stops working after navigation

I'm using the camera view as a barcode scanner that scans a barcode and launches a segue to another viewcontroller with a webview. This is working fine, and I can navigate back to the scanner from the webview and scan another barcode without issue. However, if I navigate away from the view controller with the camera and return to it, the camera view loads but no longer detects barcodes.
#implementation ProductScanViewController
NSString *loadUrl;
AVCaptureSession *_captureSession;
AVCaptureDevice *_videoDevice;
AVCaptureDeviceInput *_videoInput;
AVCaptureVideoPreviewLayer *_previewLayer;
BOOL _running;
AVCaptureMetadataOutput *_metadataOutput;
#synthesize mWebView;
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:loadUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{if([data length] > 0 && error == nil)[mWebView loadRequest:request]; else if (error != nil) NSLog(#"Error: %", error);}
];
[self setupCaptureSession];
}
- (void)setupCaptureSession {
// 1
if (_captureSession){
return;
}
// 2
_videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (!_videoDevice) {
return;
}
// 3
_captureSession = [[AVCaptureSession alloc] init];
// 4
_videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_videoDevice error:nil];
// 5
if ([_captureSession canAddInput:_videoInput]) {
[_captureSession addInput:_videoInput];
}
// 6
_previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
_previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
// capture and process the metadata
_metadataOutput = [[AVCaptureMetadataOutput alloc] init];
dispatch_queue_t metadataQueue =
dispatch_queue_create("com.1337labz.featurebuild.metadata", 0);
[_metadataOutput setMetadataObjectsDelegate:self
queue:metadataQueue];
if ([_captureSession canAddOutput:_metadataOutput]) {
[_captureSession addOutput:_metadataOutput];
}
}
#pragma mark - Delegate functions
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputMetadataObjects:(NSArray *)metadataObjects
fromConnection:(AVCaptureConnection *)connection {
[metadataObjects enumerateObjectsUsingBlock:^(AVMetadataObject *obj,
NSUInteger idx,
BOOL *stop) {
if ([obj isKindOfClass: [AVMetadataMachineReadableCodeObject class]]) {
//NSLog(#"Capture Output started");
// 3
AVMetadataMachineReadableCodeObject *code = (AVMetadataMachineReadableCodeObject*)
[_previewLayer transformedMetadataObjectForMetadataObject:obj];
// 4
Barcode * barcode = [Barcode processMetadataObject:code];
for (NSString * str in self.allowedBarcodeTypes) {
if([barcode.getBarcodeType isEqualToString:str]){
[self validBarcodeFound:(barcode)];
return;
}
}
}
}];
}
- (void) validBarcodeFound:(Barcode *)barcode{
NSLog(#"Found Barcode");
[self stopRunning];
[self.foundBarcodes addObject:barcode];
//[self showBarcodeAlert:barcode];
NSString *alertMessage = #"";
alertMessage = [alertMessage stringByAppendingString:[barcode getBarcodeType]];
NSLog([barcode getBarcodeData]);
NSLog(alertMessage);
NSLog([NSString stringWithFormat:#"%#", barcode.getBarcodeData]);
if ([barcode.getBarcodeType isEqualToString:#"org.iso.QRCode"])
{
if ([[NSString stringWithFormat:#"%lu",(unsigned long)[self.foundBarcodes count]-1] length] > 0){
NSString *input = [barcode getBarcodeData];
[NSString stringWithFormat:#"%lu",(unsigned long)[self.foundBarcodes count]-1];
NSLog(input);
if ([input length] >= 13)
{
input = [input substringToIndex:12];
}
loadUrl = [[#"http://www.mywebsite.co.uk/" stringByAppendingString:input] stringByAppendingString:#"?utm_source=iphone"];
NSLog(loadUrl);
dispatch_sync(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"toWebView" sender:self];
});
}
}
So I've managed to fix this but can't give any real understanding of why it works.
Instead of loading the URL asynchronously during viewDidLoad, I passed the URL with the segue and loaded it from within the ViewController containing the WebView.
Further to this, the variable declarations were encapsulated in curly brackets {} and the #synthesize mWebView was removed. I've no idea why that was causing issues so any possible explanation would be appreciated

Update the UI of the View Controller and then dismiss it

I have a view controller, that loads some an array. While everything is loading, I need to present another view controller (with the UIProgressView) and update it's UI (the progress property of a UIProgressView) and then dismiss and present first vc with downloaded data. I'm really struggling on it and I've tried delegation, but nothing worked for me.
- (void)viewDidLoad
{
[super viewDidLoad];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"downloaded"]) {
} else {
NSLog(#"First time Launched");
ProgressIndicatorViewController *progressVC = [ProgressIndicatorViewController new];
progressVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self syncContacts];
[self presentViewController:progressVC animated:YES completion:nil];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"downloaded"];
[progressVC release];
}
}
sync contacts method:
- (void)syncContacts
{
NSLog(#"Sync data");
NSMutableArray *allContacts = [ContactsOperations getAllContactsFromAddressBook];
NSInteger allContactsCount = [allContacts count];
if (allContactsCount > 0) {
for (ContactData *contact in allContacts) {
NSMutableArray *phoneNumbersArray = [[NSMutableArray alloc] init];
NSString *nospacestring = nil;
for (UserTelephone *tel in [contact.abonNumbers retain]) {
NSArray *words = [tel.phoneNumber componentsSeparatedByCharactersInSet :[NSCharacterSet whitespaceCharacterSet]];
NSString *nospacestring = [words componentsJoinedByString:#""];
[phoneNumbersArray addObject:nospacestring];
}
contact.abonNumbers = phoneNumbersArray;
if (phoneNumbersArray != nil) {
NSLog(#"NOT NULL PHONENUMBERS: %#", phoneNumbersArray);
}
NSDictionary *dataDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:contact.abonNumbers, #"phoneNumbers", contact.contactName, #"fullName", [NSNumber numberWithBool:contact.isBlackList], #"blacklist", [NSNumber numberWithBool:contact.isIgnore], #"ignore", contact.status, #"status", nil];
NSLog(#"dictionary: %#", dataDictionary);
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:dataDictionary options:0 error:&error];
NSLog(#"POST DATA IS : %#", postData);
NSMutableURLRequest *newRequest = [self generateRequest:[[NSString stringWithFormat:#"%#c/contacts%#%#", AVATATOR_ADDR, SESSION_PART, [[ServiceWorker sharedInstance] SessionID]] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding] withHTTPMethod:#"POST"];
[newRequest setHTTPBody:postData];
[newRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
//__block NSMutableData *newData;
[NSURLConnection sendAsynchronousRequest:newRequest queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
if (!connectionError) {
NSDictionary *allData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"alldata from contacts: %#", allData);
//NSInteger errorCode = [[allData objectForKey:#"CommandRes"] integerValue];
//if (errorCode == 0) {
NSInteger remoteId = [[allData objectForKey:#"contactId"] integerValue];
contact.remoteId = remoteId;
NSLog(#"remote id is from parse content : %d", remoteId);
[[AvatatorDBManager getSharedDBManager]createContactWithContactData:contact];
} else {
NSLog(#"error");
}
}];
//Somewhere here I need to update the UI in another VC
[phoneNumbersArray release];
[dataDictionary release];
}
} else {
}
}
generate request method:
- (NSMutableURLRequest *)generateRequest:(NSString *)urlString withHTTPMethod:(NSString *)httpMethod
{
NSLog(#"url is :%#", urlString);
NSURL *url = [NSURL URLWithString:urlString];
request = [NSMutableURLRequest requestWithURL:url];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[request setHTTPMethod:httpMethod];
return request;
}
ProgressViewController is just an empty VC with the progress bar. No code yet.
In the view controller that will display the progress view expose a method like this...
- (void)updateProgress:(float)progress;
Its implementation will look like this...
- (void)updateProgress:(float)progress {
[self.progressView setProgress:progress animated:YES];
}
On the main view controller you need to execute the long-running process on a background thread. Here's viewDidLoad for the main view controller. This example code uses a property for the progress view controller (you may not require this) and assumes your are in a navigation controller...
- (void)viewDidLoad {
[super viewDidLoad];
// Create and push the progress view controller...
self.pvc = [[ProgressViewController alloc] init];
[self.navigationController pushViewController:self.pvc animated:YES];
// Your long-running process executes on a background thread...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Your long-running process goes here. Wherever required you would
// call updateProgress but that needs to happen on the main queue...
dispatch_async(dispatch_get_main_queue(), ^{
[self.pvc updateProgress:progress];
});
// At the end pop the progress view controller...
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController popViewControllerAnimated:YES];
});
});
}

UI hanging on background rss parsing

I'm trying to create a simple rss reader. The code works okay, except the UI hangs when the feeds are being updated. I thought I cobbled together the code to get the feed and parse it on a background queue while updating the UI on the mainQueue, but the table hangs pretty badly. Code below:
-(void)refreshFeed2
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
for (NSString *feed in _feeds) {
// iterate over all feeds
NSLog(#"feed=%#", feed);
NSURL *url = [NSURL URLWithString:feed];
// Create url connection and fire request
NSURLConnection *conn = [[NSURLConnection alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
(void)[conn initWithRequest:request delegate:self];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if ([data length] == 0 && error == nil) {
// handle empty response
} else if (error != nil) {
// handle error
NSLog(#"Error %#", [error localizedDescription]);
} else if ([httpResponse statusCode] == 200) {
// data present and no errors
[queue addOperationWithBlock:^{
// parse feed on queue
RXMLElement *rss = [RXMLElement elementFromXMLData:data];
RXMLElement *rssChild = [rss child:#"channel"];
RXMLElement* title = [rssChild child:#"title"];
NSArray* items = [[rss child:#"channel"] children:#"item"];
NSMutableArray* result=[NSMutableArray array];
for (RXMLElement *e in items) {
// iterate over the articles
RSSArticle* article = [[RSSArticle alloc] init];
article.sourceTitle = [title text];
article.articleTitle = [[e child:#"title"] text];
article.articleDescription = [[e child:#"description"] text];
article.articleUrl = [NSURL URLWithString: [[e child:#"link"] text]];
NSString *articleDateString = [[e child:#"pubDate"] text];
article.articleDate = [NSDate dateFromInternetDateTimeString:articleDateString formatHint:DateFormatHintRFC822];
if (article.articleUrl != NULL) {
[result addObject:article];
}
}
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// update table on mainQueue
for (RSSArticle *article in result) {
// iterate over articles
int insertIdx = [_allEntries indexForInsertingObject:article sortedUsingBlock:^(id a, id b) {
RSSArticle *entry1 = (RSSArticle *) a;
RSSArticle *entry2 = (RSSArticle *) b;
return [entry1.articleDate compare:entry2.articleDate];
}];
[_allEntries insertObject:article atIndex:insertIdx];
[self.LeftTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:insertIdx inSection:0]]
withRowAnimation:UITableViewRowAnimationFade];
}
}];
}];
}
}];
// Stop refresh control
[refreshControl endRefreshing];
}
}
Code that calls refreshFeed2:
- (void)viewDidLoad {
[super viewDidLoad];
self.allEntries = [NSMutableArray array];
self.feeds = [NSArray arrayWithObjects:
#"http://feeds.washingtonpost.com/rss/politics",
#"http://rss.cnn.com/rss/cnn_allpolitics.rss",
#"http://www.npr.org/rss/rss.php?id=1012",
#"http://www.slatedigital.com/support/feeds/rss_kb.php?s=fd5aa35e773dc3177b85a2126583f002",
nil];
}
//add refresh control to the table view
refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self
action:#selector(refreshInvoked:forState:)
forControlEvents:UIControlEventValueChanged];
NSString* fetchMessage = [NSString stringWithFormat:#"Fetching Articles"];
refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:fetchMessage
attributes:#{NSFontAttributeName:[UIFont fontWithName:#"Helvetica" size:11.0]}];
[self.LeftTableView addSubview: refreshControl];
[self refreshInvoked:self forState:UIControlStateNormal];
}
-(void) refreshInvoked:(id)sender forState:(UIControlState)state {
NSOperationQueue *refreshQueue = [[NSOperationQueue alloc] init];
[refreshQueue addOperationWithBlock:^{
[self refreshFeed2];
}];
}
Any help?
Thanks!
Can you try this? replace
[self refreshInvoked:self forState:UIControlStateNormal];
by
[self performSelectorOnBackground:#selector(refreshFeed2) withObject:nil];
and replace the same instead of
-(void) refreshInvoked:(id)sender forState:(UIControlState)state {
[self performSelectorOnBackground:#selector(refreshFeed2) withObject:nil ];
}

iOS: EXC_BAD_ACCESS Error NSString length and setHTTPBody

When I start the simulator and the application starts and I click on the UI I get EXC_BAD_ACCESS for NSString *strLength = [NSString stringWithFormat:#"%d", [postStr length]]; and for [req setHTTPBody:[_postStr dataUsingEncoding:NSUTF8StringEncoding. I dont know why this happens. If I uninstall the app but keep the simulator open and run it again I get no errors. Any help would be great. Code is below.
#import "LocavoreRetroAPIAdapter.h"
//Class extention declares a method that is private to the class
#interface LocavoreRetroAPIAdapter ()
-(NSMutableURLRequest *)initRequest:(NSURL *)url method:(NSString *)method;
#end
#implementation LocavoreRetroAPIAdapter
//Called when this class is first initialized
-(id) initWithName:(NSString *)postStr webService:(NSString *)webService spinner: (UIActivityIndicatorView *)spinner{
if(self = [super init]){
_postStr = postStr;
_baseURL = #"http://base/api/";
_webService = webService;
_spinner = spinner;
_result = nil;
}
return self;
}
//Request to Locavore API restful web services
-(void) conn:(NSString *)method{
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
__block NSDictionary *resultBlock = nil;
dispatch_sync(concurrentQueue, ^{
/* Download the json here */
//Create webservice address
NSString *webService = [_baseURL stringByAppendingString:_webService];
//Create the url
NSURL *url = [NSURL URLWithString:webService];
//Create error object
NSError *downloadError = nil;
//Create the request
NSMutableURLRequest *req = [self initRequest:url method:method];
if(req != nil){
//Request the json data from the server
NSData *jsonData = [NSURLConnection
sendSynchronousRequest:req
returningResponse:nil
error:&downloadError];
NSError *error = nil;
id jsonObject = nil;
if(jsonData !=nil){
/* Now try to deserialize the JSON object into a dictionary */
jsonObject = [NSJSONSerialization
JSONObjectWithData:jsonData
options:NSJSONReadingAllowFragments
error:&error];
}
//Handel the deserialized object data
if (jsonObject != nil && error == nil){
NSLog(#"Successfully deserialized...");
if ([jsonObject isKindOfClass:[NSDictionary class]]){
resultBlock = (NSDictionary *)jsonObject;
//NSLog(#"Deserialized JSON Dictionary = %#", resultBlock);
}
else if ([jsonObject isKindOfClass:[NSArray class]]){
NSArray *deserializedArray = (NSArray *)jsonObject;
NSLog(#"Deserialized JSON Array = %#", deserializedArray);
} else {
/* Some other object was returned. We don't know how to deal
with this situation, as the deserializer returns only dictionaries
or arrays */
}
}
else if (error != nil){
NSLog(#"An error happened while deserializing the JSON data.");
}else{
NSLog(#"No data could get downloaded from the URL.");
[self conn:method];
}
}
});
dispatch_sync(dispatch_get_main_queue(), ^{
/* Check if the resultBlock is not nil*/
if(resultBlock != nil){
/*Set the value of result. This will notify the observer*/
[self setResult:resultBlock];
[_spinner stopAnimating];
}
});
});
}
//Configure the request for a post/get method
- (NSMutableURLRequest *)initRequest:(NSURL *)url method:(NSString *)method{
//Create the request
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
//Get the string length
NSString *strLength = [NSString stringWithFormat:#"%d", [_postStr length]];
//Specific to requests that use method post/get
//Configure the request
if([method isEqualToString:#"POST"]){
[req addValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content- Type"];
[req addValue:strLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"POST"];
}else if([method isEqualToString:#"GET"]){
[req addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[req addValue:strLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"GET"];
}else{
return nil;
}
//Set the HTTP Body
[req setHTTPBody:[_postStr dataUsingEncoding:NSUTF8StringEncoding]];
//Return the request
return req;
}
//Called when this object is destroyed
- (void)dealloc {
NSLog(#"DEALLOC LocavoreRetroAPIAdapter");
[super dealloc];
[_baseURL release];
[_result release];
}
#end
Familiarize yourself the with memory management and object lifetime rules. Your code is crashing because you do not retain (or copy) the arguments within your init... method, and they are being deallocated. Change your init... method to:
-(id) initWithName:(NSString *)postStr webService:(NSString *)webService spinner: (UIActivityIndicatorView *)spinner{
if(self = [super init]){
_postStr = [postStr copy];
_baseURL = #"http://base/api/";
_webService = [webService copy];
_spinner = [spinner retain];
}
return self;
}
Be sure to release the three instance variables you are now copying or retaining in your dealloc method. Also call [super dealloc] as the last step in that method but that's not the source of your problem right now.
//Called when this object is destroyed
- (void)dealloc {
NSLog(#"DEALLOC LocavoreRetroAPIAdapter");
[_postStr release];
[_webService release];
[_spinner release];
[_result release];
[super dealloc];
}
Notice I removed the call to [_baseURL release] from your dealloc as you did not retain it so you do not own the object. If you didn't create an object with alloc or new, and didn't call retain or copy on it, then you don't own the object, so you must not release it.

Resources