Singleton Property causing App crash - objective-c

I have a singelton class which is used to pull and store all data from a Web Service. The class has an NSArray and an NSDictionary which stores the data. I then want to use this data to populate tableviews in other views.
In one of my views, within the View Did Load method, I tell the singleton to retrieve the data from the web service and store it (it successfully retrieves the data, I logged it). I then try to access the data like so:
[[ClubData initClubData] memberData];
If I try to use that to populate the table view, it crashes and keeps referencing some kind of view. One time it referenced CALayer, another time a WrapperView ??? Am I not doing something right
My Singleton:
#import "ClubData.h"
#import "JSON.h";
#implementation ClubData
#synthesize conn, response, url, api_calls, memberData, beerData, call;
static ClubData *globalClubData = nil;
#pragma mark -
#pragma mark Instance Methods
- (void)getBeerData {
}
- (void)getSingleBeer:(NSInteger *)beerID {
}
- (void)getClubData {
//check for existing data
if (memberData != nil)
return;
//init
memberData = [[NSArray alloc] init];
//create request
call = #"memberlist";
[self initRequest:#"memberlist"];
}
- (void)getMemberData:(NSInteger *)memberID {
}
- (void)parseData:(NSString *)json {
//parse based on call type
if (call == #"memberlist") {
memberData = [[NSDictionary alloc] init];
memberData = [json JSONValue];
}
//reset call
[call release];
call = nil;
}
#pragma mark -
#pragma mark Connection & Delegate
- (void)initRequest:(NSString *)type {
//build url
NSMutableString *rURL = [[NSMutableString alloc] initWithString:url];
[rURL appendString:[api_calls objectForKey:type]];
NSURL *tempURL = [[NSURL alloc] initWithString:rURL];
[rURL release];
//init request & create connection
NSURLRequest *request = [[[NSURLRequest alloc] initWithURL:tempURL] autorelease];
conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[tempURL release];
//init response
response = [[NSMutableData alloc] init];
}
//receive data
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[response appendData:data];
}
//connection complete
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//release conn
[conn release];
//parse JSON
NSString *json = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
[response release];
//parse
[self parseData:json];
[json release];
}
//connection failed
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Connection Error"
message:#"Unable to connect to network. Network required to load data."
delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
#pragma mark -
#pragma mark Singleton Control
+ (ClubData *)initClubData {
if (globalClubData == nil) {
#synchronized(self) {
globalClubData = [[super allocWithZone:NULL] init];
}
}
return globalClubData;
}
- (id)init {
if (self = [super init]) {
//set url
url = [[[NSString alloc] initWithString:#"https://myurl.com/path/to/script.php?action=get_app_data"] retain];
//possible calls
NSArray *keys = [[NSArray alloc] initWithObjects:#"beerlist", #"singlebeer", #"memberlist", nil];
NSArray *objs = [[NSArray alloc] initWithObjects:#"&subaction=beerlist", #"&subaction=singlebeer&beerID=", #"&subaction=memberlist", nil];
api_calls = [[[NSDictionary alloc] initWithObjects:objs forKeys:keys] retain];
[keys release];
[objs release];
}
return self;
}
#pragma mark -
#pragma mark Lifecycle
- (void)dealloc {
[conn release];
[response release];
[url release];
[call release];
[api_calls release];
[super dealloc];
}
#end

api_calls = [[[NSDictionary alloc] initWithObjects:objs forKeys:keys] retain];
do not retain it (you are already an owner of that object).
As for the crash - post the stack trace.
Edit: Probably found:
if (call == #"memberlist") {
memberData = [[NSDictionary alloc] init]; //remove this (it is redundant and causes leak)
memberData = [json JSONValue]; //retain this
}

Related

NSMutableArray is empty after ViewDidLoad

Data not loading in tableView here is my description.
I am trying to add objects from API which responding me in JSON form of data into NSMutableArray but it is not loading with the tableview.
I can see the array sets ok but it does not load into tableview until I try to scroll the tableview.
It is just so annoying and cannot figure out why kindly help me to figure out.
Here is my code -
- (void)viewDidLoad {
[SVProgressHUD showWithStatus:#"Please wait ..."];
//get the bookings
[self getBookings];
[super viewDidLoad];
[self.tableView reloadData];
}
-(void) getBookings
{
uid=#"41";
bookingsList=[[NSMutableArray alloc] init];
NSString *urlAsString = [NSString stringWithFormat:#"http://www.webservice.com/cleaning/api/booking/show/%#/%d",uid,0];
NSURL *url = [[NSURL alloc] initWithString:urlAsString];
NSLog(#"%#", urlAsString);
[NSURLConnection sendAsynchronousRequest:[[NSURLRequest alloc] initWithURL:url] queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"URL Session Task Failed: %#", [error localizedDescription]);
}
else {
NSArray *postsFromResponse = [NSJSONSerialization JSONObjectWithData: data options:NSJSONReadingMutableContainers error:nil];
NSLog(#"Count %d", postsFromResponse.count);
NSLog(#"JSON: %#", postsFromResponse);
//remove all objects from array
[self.bookingsList removeAllObjects];
for (NSDictionary *attributes in postsFromResponse) {
booking *bk = [[booking alloc] init];
[bk setAddress:[attributes objectForKey:#"Address"]];
[bk setBookingId:[attributes objectForKey:#"BookingID"]];
[bk setServiceDate:[attributes objectForKey:#"ServiceDate"]];
[bk setClientName:[attributes objectForKey:#"ClientName"]];
[bk setStatus:[attributes objectForKey:#"Status"]];
[bk setServiceTime:[attributes objectForKey:#"ServiceTime"]];
[bk setPrice:[attributes objectForKey:#"Price"]];
[bk setCleanType:[attributes objectForKey:#"CleanType"]];
[bk setNumOfHours:[attributes objectForKey:#"NumOfHours"]];
//add to array
[self.bookingsList addObject:bk];
}
NSLog(#"Records found -%lu",(unsigned long)[bookingsList count]);
[self.tableView reloadData];
if(bookingsList.count==0)
{
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"No Data Alert!"
message:#"No bookings found!" delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil];
//[alertView show];
[alertView performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
}
}
}];
[SVProgressHUD dismiss];
}
#pragma mark - TableView Delegate Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Total rows - %lu", (unsigned long)[self.bookingsList count]);
return [self.bookingsList count];
}
Use This It Will Help You
- (void)viewDidLoad {
[SVProgressHUD showWithStatus:#"Please wait ..."];
//get the bookings
[self getBookings];
[super viewDidLoad];
}
-(void) getBookings
{
uid=#"41";
bookingsList=[[NSMutableArray alloc] init];
NSString *urlAsString = [NSString stringWithFormat:#"http://www.webservice.com/cleaning/api/booking/show/%#/%d",uid,0];
NSURL *url = [[NSURL alloc] initWithString:urlAsString];
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:urlAsString];
[request setHTTPMethod:GET_REQUEST];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *err)
{
if (error) {
NSLog(#"URL Session Task Failed: %#", [error localizedDescription]);
}
else {
NSArray *postsFromResponse = [NSJSONSerialization JSONObjectWithData: data options:NSJSONReadingMutableContainers error:nil];
NSLog(#"Count %d", postsFromResponse.count);
NSLog(#"JSON: %#", postsFromResponse);
//remove all objects from array
[self.bookingsList removeAllObjects];
for (NSDictionary *attributes in postsFromResponse) {
booking *bk = [[booking alloc] init];
[bk setAddress:[attributes objectForKey:#"Address"]];
[bk setBookingId:[attributes objectForKey:#"BookingID"]];
[bk setServiceDate:[attributes objectForKey:#"ServiceDate"]];
[bk setClientName:[attributes objectForKey:#"ClientName"]];
[bk setStatus:[attributes objectForKey:#"Status"]];
[bk setServiceTime:[attributes objectForKey:#"ServiceTime"]];
[bk setPrice:[attributes objectForKey:#"Price"]];
[bk setCleanType:[attributes objectForKey:#"CleanType"]];
[bk setNumOfHours:[attributes objectForKey:#"NumOfHours"]];
//add to array
[self.bookingsList addObject:bk];
}
NSLog(#"Records found -%lu",(unsigned long)[bookingsList count]);
dispatch_async(dispatch_get_main_queue(),^{
[self.tableView reloadData];
});
if(bookingsList.count==0)
{
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"No Data Alert!"
message:#"No bookings found!" delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil];
//[alertView show];
[alertView performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
}
}
}];
[task resume];
[SVProgressHUD dismiss];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Total rows - %lu", (unsigned long)[self.bookingsList count]);
return [self.bookingsList count];
}
Coding conventions: The backing variable for any property should start with an underscore. Like _bookingsList. If you don't follow that connection, we cannot know without having your complete source code where an assignment to bookingsList will store the result.

Set UISwitches by JSON data?

I want to set some UISwitches to values pulled in by a HTTP GET request. The request is working. However, since I'm fairly new to iOS programming I don't know how to go from having some JSON data to pulling it apart like in PHP.
This is what I currently have:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"viewdidload");
self.responseData = [NSMutableData data];
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:#"http://www.test.api/setings"]];
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[mutableRequest addValue:_xAuthToken forHTTPHeaderField:#"X-Auth-Token"];
request = [mutableRequest copy];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
[switch1 setOn:1 animated:NO];
[switch2 setOn:1 animated:NO];
[switch3 setOn:1 animated:NO];
[switch4 setOn:1 animated:NO];
[switch5 setOn:1 animated:NO];
[switch6 setOn:1 animated:NO];
[switch7 setOn:0 animated:NO];
[switch8 setOn:1 animated:NO];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"didReceiveResponse");
[self.responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"didFailWithError");
NSLog([NSString stringWithFormat:#"Connection failed: %#", [error description]]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading");
NSLog(#"Succeeded! Received %d bytes of data",[self.responseData length]);
// convert to JSON
NSError *myError = nil;
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:&myError];
// show all values
for(id key in res) {
id value = [res objectForKey:key];
NSString *keyAsString = (NSString *)key;
NSString *valueAsString = (NSString *)value;
NSLog(#"key: %#", keyAsString);
NSLog(#"value: %#", valueAsString);
}
// extract specific value...
NSArray *results = [res objectForKey:#"results"];
for (NSDictionary *result in results) {
NSString *icon = [result objectForKey:#"icon"];
NSLog(#"icon: %#", icon);
}
}
- (void)viewDidUnload {
[super viewDidUnload];
}
And this is what it's getting back from the server:
{"data":
{"setting1":"1",
"setting2":"1",
"setting3":"0",
"setting4":"0",
"setting5":"0",
"setting6":"0",
"setting7":"0",
"setting8":"0"}
}
Just convert the 1 or 0 number string to bool value and then set the switch state using it
//Declare this array of switches in your class header file
property(strong,nonatomic)NSArray *switches;
and initialize it in ViewDidLoad method
switches=[NSArray arrayWithObjects:switch1,switch2,switch3,switch4,switch5,switch6,switch7,switch8,nil];
// and in your - (void)connectionDidFinishLoading:(NSURLConnection *)connection method
NSError *myError = nil;
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:&myError];
//get the array of settings and values from the data Object in your json
NSDictionary *switchValues=[res objectForKey:#"data"];
// show all values
NSUInteger *switchIndex=0;
//iterate and convert the value to bool and set the switch state using that value
for(id key in switchValues) {
id value = [switchValues objectForKey:key];
NSString *keyAsString = (NSString *)key;
NSString *valueAsString = (NSString *)value;
[[switches objectAtIndex:switchIndex] setOn:[valueAsString boolValue] animated:NO];
NSLog(#"key: %#", keyAsString);
NSLog(#"value: %#", valueAsString);
switchIndex++;
}

Button press doesn't work during parsing

I cannot make back button (previous page in navigation) work during html parsing process. What I am trying to do is parsing an html and get a pdf file on viewDidAppear. During that time I want back button to be enabled because parsing can take long time. So users can decide to leave parsing process.
here is my code:
-(void)viewDidAppear:(BOOL)animated
{
[self performSelector:#selector(getPDFUrl) withObject:nil afterDelay:0];
}
-(void) getPDFUrl
{
NSURL *programURL = [NSURL URLWithString:#"http://www.example.com/somepdf/"];
NSData *programHtmlData;
#try
{
programHtmlData = [NSData dataWithContentsOfURL:programURL];
}
#catch(NSException* ex)
{}
// 2
TFHpple *programHTMLParser = [TFHpple hppleWithHTMLData:programHtmlData];
NSString *studiosXpathQueryString =
#"//div[#class='ultra_wrapper']/div[#class='container columns extra_pad boxed_lay centered']/div[#id='prk_ajax_container']/div[#id='centered_block']/div[#id='main_block']/div[#id='content']/div[#id='main']/div[#class='twelve columns sidebarized']/div[#class='prk_no_composer']/p/a";
NSArray *programNodes = [programHTMLParser searchWithXPathQuery:studiosXpathQueryString];
NSMutableArray *activities = [[NSMutableArray alloc] init];
Tutorial *tutorial;
if (programNodes.count > 0) {
for (TFHppleElement *element in programNodes)
{
#try
{
tutorial = [[Tutorial alloc] init];
tutorial.url = [element objectForKey:#"href"];
}
#catch(NSException* ex)
{
}
}
NSURL *targetURL = [NSURL URLWithString:tutorial.url];
webView.scalesPageToFit=YES;
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
[webView loadRequest:request];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"warning"
message:#"warning!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
[self performSelector:#selector(getPDFUrl) withObject:nil afterDelay:0];
will execute on main thread. Therefore Blocking your UI.
You should use
[self performSelectorInBackground:#selector(getPDFUrl) withObject:nil];
Or you can use NSOperationQueue or just NSOperation or more simple way NSBlockOperation.
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"dasds");
}];
[op start];

NSURLConnection delegates not being called even when run on main thread

I know that this kind of question has been asked many times, but all of them point to saying that the connection must be on a different thread.
-(void)distanceMatrix{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:distanceMatrixURL]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
connection2 = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection2 scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
NSLog(#"Is%# main thread", ([NSThread isMainThread] ? #"" : #" NOT"));
[connection2 start];
if (connection2)
{
responseData2 = [NSMutableData data];
connectionIsActive = YES;
} else {
NSLog(#"connection failed");
}
}
- (void)connection2:(NSURLConnection *)connection2 didReceiveResponse:(NSURLResponse *)response
{NSLog(#"recieved response");
[responseData2 setLength:0];
}
- (void)connection2:(NSURLConnection *)connection2 didReceiveData:(NSData *)data
{
[responseData2 appendData:data];
}
- (void)connection2:(NSURLConnection *)connection2 didFailWithError:(NSError *)error
{
connectionIsActive = NO;
NSLog(#"failed!!");
}
- (void)connection2DidFinishLoading:(NSURLConnection *)conn
{
connectionIsActive = NO;
SBJsonParser *json = [[SBJsonParser alloc] init];
NSString *responseString = [[NSString alloc] initWithData:responseData2 encoding:NSUTF8StringEncoding];
NSError *jsonError = nil;
NSDictionary *parsedJSON = [json objectWithString:responseString error:&jsonError];
travelTime= [[[[parsedJSON valueForKey:#"rows"] valueForKey:#"elements"] valueForKey:#"duration"] valueForKey:#"text"];
NSLog(#"traveltime = %#", travelTime);
}
When I log it, it says that it runs on the main thread. Connection2 is active but none of the delegates are called.
Also, this is the way I am calling distanceMatrix method
-(id)initWithJsonResultDict:(NSDictionary *)jsonResultDict andUserCoordinates: (CLLocationCoordinate2D)userCoords andTimeURL:(NSString*)timeURL
{
self.distanceMatrixURL = timeURL;
[self distanceMatrix];
//more code here for other purposes
}
Because you have added a 2 into the names of all of the delegate methods. That changes the method signature so you aren't implementing the correct methods. Remove all of the 2 at the start of the methods - (void)connection2: and it should work.

What is asynchronous image downloading and how can I download too many images?

I have too many images to be download from net into iPhone? How can I build an application using asynchronous image downloading?.
Most common and simple way is to use NSURLConnection with asynchronous request. Create connection with request set delegate, and it starts load data in background calling delegate methods when receive next chunk of data, finish load or fail. when first object is loaded, start next and so on.
Here is slightly simplified working code:
- (id)init...{
//...
requestData = [[NSMutableData alloc] initWithCapacity:1000000];
myImages = [[NSMutableArray alloc] initWithCapacity:100];
myImagesAddresses = [[NSMutableArray alloc] initWithCapacity:100];
[myImagesAddresses addObject:#"http://mysite.com/image1"];
[myImagesAddresses addObject:#"http://mysite.com/image2"];
//...
[self loadNextImage];
//...
}
-(void)loadNextImage{
if ([myImagesAddresses count]){
NSURL * imageURL = [NSURL URLWithString:[myImagesAddresses lastObject]];
NSURLRequest * myRequest = [NSURLRequest requestWithURL:imageURL];
[[NSURLConnection alloc] initWithRequest:myRequest delegate:self];
NSLog(#"start load URL:%#", imageURL);
}
else{
NSLog(#"No more images to load");
// all images are ready to use!
}
}
// connection delegate methods
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data{
NSLog(#"more data...");
[requestData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)inConnection{
[myImages addObject:[UIImage imageWithData:[NSData dataWithData:requestData]]];
[inConnection release];
inConnection = nil;
NSLog(#"Image from:%# loaded",[myImagesAddresses lastObject]);
[myImagesAddresses removeLastObject];
[self loadNextImage];
}
- (void)connection:(NSURLConnection *) inConnection didFailWithError:(NSError *)error{
[inConnection release];
inConnection = nil;
NSLog(#"Image from:%# not loaded",[myImagesAddresses lastObject]);
[myImagesAddresses removeLastObject];
[self loadNextImage];
}