Im still trying to get on to the ios development but im hoping you can help me.
Currently I have a WCF that returns some json data in the format of
"Address": "3453453",
"Category": "CONCRETE",
"Closest_Property_Number": 2,
"ID": 42,
"Image1": 324,
"Image2": 0,
"Image3": 0,
"Latitude": 2,
"Longitude": "-6.541902",
"Notes": "GHTFHRG",
"User_ID": 2
I then created a class called Location here is the Location.m
#import "Location.h"
#implementation Location {
NSString* _address;
NSString* _category;
NSString* _closest_Property_Number;
NSString* _iD;
NSString* _image1;
NSString* _latitude;
NSString* _longitude;
NSString* _notes;
NSString* _user_ID;
}
#synthesize address = _address;
#synthesize category = _category;
#synthesize closest_Property_Number = _closest_Property_Number;
#synthesize iD = _iD;
#synthesize image1 = _image1;
#synthesize latitude = _latitude;
#synthesize longitude = _longitude;
#synthesize notes = _notes;
#synthesize user_ID = _user_ID;
#end
I think this is right so far? Here is my class where all the importing happens
#import "Location.h"
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSString *urlAsString = #"http://crm.fpmccann.co.uk/TemperatureWebService/iphonewebservice.svc/retrievelocations";
NSURL *url = [NSURL URLWithString:urlAsString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error)
{
if ([data length] >0 && error == nil)
{
NSMutableArray* tmpLocations = [[NSMutableArray alloc] init];
for (NSDictionary* loc in locations) {
Location* location = [[Location alloc] initWithParameters:loc];
[tmpLocations addObject:location];
}
NSMutableArray* tmpAnnotations;
for (NSDictionary* location in tmpLocations)
{
// retrieve latitude and longitude from location
MKPointAnnotation* annotation = [[MKPointAnnotation alloc] init];
annotation.title = location.address;
newAnnotation.coordinate = location;
[tmpAnnotations addObject:annotation];
}
dispatch_async(dispatch_get_main_queue(), ^{
self.locations = tmpLocations;
self.annotations = tmpAnnotations;
[self.mapView reloadInputViews];
});
}
else if ([data length] == 0 && error == nil)
{
NSLog(#"Nothing was downloaded.");
}
else if (error != nil){
NSLog(#"Error = %#", error);
}
}];
}
Here is where i am having problems, I want to show an annotation on a UImapview using the information from the json data. Please see the errors i am having in this part of the code below, commented on the line that they are happening
if ([data length] >0 && error == nil)
{
NSMutableArray* tmpLocations = [[NSMutableArray alloc] init];
for (NSDictionary* loc in locations) { //receiving error use of undeclared identifier 'locations', did you mean 'Location'
Location* location = [[Location alloc] initWithParameters:loc];
[tmpLocations addObject:location];
}
NSMutableArray* tmpAnnotations;
for (NSDictionary* location in tmpLocations)
{
// retrieve latitude and longitude from location
MKPointAnnotation* annotation = [[MKPointAnnotation alloc] init];
annotation.title = location.address; // receiving error Property 'address' not found on object of type 'NSDictionary'
newAnnotation.coordinate = location; // receiving error use of undeclared identifier 'newAnnotation'
[tmpAnnotations addObject:annotation];
}
dispatch_async(dispatch_get_main_queue(), ^{
self.locations = tmpLocations; /// receiving error Property 'locations' not found on object of type 'MapViewController'
self.annotations = tmpAnnotations; /// receiving error Property 'annotations' not found on object of type 'MapViewController'
[self.mapView reloadInputViews];
});
}
And here is my MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface MapViewController : UIViewController <MKMapViewDelegate>
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
- (IBAction)refreshTapped:(id)sender;
#end
You should make a few improvements to your code:
First, it's crucial to conform to the "naming conventions" in Objective-C. Properties, should start with a lowercase letter. For example:
#property (nonatomic, copy) NSString* address;
Properties of type NSString should have a "copy" attribute (the exception is managed objects).
Almost always the name of a class should be in singular form, that is instead of
#class LocationResults;
I would suggest to name it
#class Location;
The preferred way to declare ivars is in the implementation. So, instead of declaring ivars in the interface
In file Location.h
#interface Location : NSObject{
NSString* address;
}
declare them as shown below:
#interface Location : NSObject
... // public properties and methods
#end
In file Location.m:
#implementation Location {
NSString* _address;
}
#synthesize address = _address;
Note:
clang supports "auto-synthesized" properties, which let you omit the ivar declaration and the #synthesize directive.
Now, regarding your code in viewDidLoad:
You seem to load a resource from a remote server:
NSData *data = [NSData dataWithContentsOfURL:url];
This is not an appropriate way to load resources from a remote server: it's an synchronous method which merely uses the thread to wait for something happen in the future (a response from the underlying network code).
The underlying network code internally dispatches its work onto internal private threads.
The effect is, you are wasting system resources when you just use a thread which gets blocked anyway for doing nothing. And - even more importantly - since you are calling this method in your main thread you are blocking the main thread and thus blocking UIKit display updates and other UIKit tasks.
Furthermore, networks request may fail in countless ways. The method dataWithContentsOfURL: cannot return reasonable error information.
These are just the most obvious caveats - but rest assured, there are more!
So, when accessing remote resources, generally use NSURLConnection or NSURLSession (or a third party library which utilizes these under the hood). In a first viable approach use the asynchronous class method:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
queue:(NSOperationQueue *)queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler;
While this method is better, it sill has a number of caveats: no way to cancel, no way to tailor authentication, no way to customize anything.
There a bunch of questions and answers how to use sendAsynchronousRequest:queue:completionHandler: on SO. Here are a few related questions and answers:
How to return an UIImage type from a GCD
NSData dataWithContentsOfURL: not returning data for URL that shows in browser
ERROR happened while deserializing the JSON data
As a rule of thumb, always check return values and if an error output parameter is given, provide an NSError object.
In case of sendAsynchronousRequest:queue:completionHandler: you should also check for the status code of the HTTP response and the Content-Type and confirm that you actually get what you requested and what you expect.
Having that said, you would populate your array of Locations as follows:
In the completion handler of sendAsynchronousRequest:queue:completionHandler: you would, first check the error and status code if that matches your expectations. IFF this is true, you have obtained a NSData object containing your JSON, then within the completion handler you implement this code:
NSError* error;
NSArray* locations = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&error];
if (locations != nil)
{
NSMutableArray* tmpLocations = [[NSMutableArray alloc] init];
for (NSDictionary* loc in locations) {
Location* location = [[Location alloc] initWithParameters:loc];
[tmpLocations addObject:location];
}
NSMutableArray* tmpAnnotations;
for (NSDictionary* location in tmpLocations)
{
// retrieve latitude and longitude from location
MKPointAnnotation* annotation = [[MKPointAnnotation alloc] init];
annotation.title = location.address;
annotation.coordinate = ...
[tmpAnnotations addObject:annotation];
}
dispatch_async(dispatch_get_main_queue(), ^{
self.locations = tmpLocations;
self.annotations = tmpAnnotations;
[self.tableView reloadData];
});
}
else {
// handle error
....
}
Note: The actual implementation depends on you more specific requirements. This implementation is merely an example how to solve a such a problem.
The method initWithParameters: should be straight forward.
Related
i have to show the user details from NSUserDefaults in more than 5 view controllers. So i have created a NSObject subclass, which will load the user details from server when the first view controllers viewDidLoad is called.
Here is my First view controller viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// Getting the Current User Details
CurrentUserDetails *userDetails = [[CurrentUserDetails alloc]init];
[userDetails initializeTheCurrentUserData];
//CurrentUserDetails is my NSObject class
}
And
#import <Foundation/Foundation.h>
#interface CurrentUserDetails : NSObject
#property(strong,nonatomic) NSString *memberName;
#property(strong,nonatomic) NSString *designation;
#property(strong,nonatomic) NSString *memberType;
#property(strong,nonatomic) NSString *entreprenuer;
#property(strong,nonatomic) NSDate *expiryDate;
#property(strong,nonatomic) NSData *imageData;
- (void) initializeTheCurrentUserData;
#end
and implementation
#implementation CurrentUserDetails
- (void) initializeTheCurrentUserData{
NSData *data = [[NSUserDefaults standardUserDefaults] valueForKey:#"userDictionary"];
NSDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
self.memberName = [retrievedDictionary valueForKey:#"Name"];
self.designation = [retrievedDictionary valueForKey:#"Designation"];
self.memberType = [[retrievedDictionary valueForKey:#"Member_type"] stringValue];
self.expiryDate = [retrievedDictionary valueForKey:#"Expiry"];
self.kanaraEntreprenuer = [retrievedDictionary valueForKey:#"CityName"];
NSString *imageUrl = [retrievedDictionary valueForKey:#"Member_image"];
self.imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#",[GlobalVariables getBaseURLForMemberImage],imageUrl]]];
}
And when iam trying to take the details from other class like this..
CurrentUserDetails *userDetails = [[CurrentUserDetails alloc]init];
memberName = userDetails.memberName;
designation = userDetails.designation;
memberType = userDetails.memberType;
dateFromServer = userDetails.expiryDate;
entreprenuer = userDetails.entreprenuer;
imageDataFromServer = userDetails.imageData;
I am getting nil values.
But if call initializeTheCurrentUserData method each time, i am getting the exact values. I though once a property is assigned with a value , we can use the property for entire program. I'm getting confusion.. Can anyone please tell me about this????. Do i need to call initializeTheCurrentUserData everytime when i want to use the values?
Once you set a property of an instance, that property remains for that instance. You, however, are creating new instances with [[CurrentUserDetails alloc] init]. Each new instance will be initialized with default values (nil for NSString).
Call -initializeTheCurrentUserData in -init so each instance will be initialized with the values from user defaults.
#implementation CurrentUserDetails
- (instancetype)init {
self = [super init];
if (self != nil) {
[self initializeTheCurrentUserData];
}
return self;
}
- (void)initializeTheCurrentUserData {
…
}
Crashlytics reported this crash in one of my apps and I am not able to reproduce it at all, no matter what I do.
This happens to about 5% of the users, so it's a pretty big deal.
I'm posting screenshots with the crash report and also the methods that are mentioned in the crash report.
Any idea how to solve this?
This is where the app crashed:
#pragma mark - custom transformations
-(BOOL)__customSetValue:(id<NSObject>)value forProperty:(JSONModelClassProperty*)property
{
if (!property.customSetters)
property.customSetters = [NSMutableDictionary new];
NSString *className = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:[value class]]);
if (!property.customSetters[className]) {
//check for a custom property setter method
NSString* ucfirstName = [property.name stringByReplacingCharactersInRange:NSMakeRange(0,1)
withString:[[property.name substringToIndex:1] uppercaseString]];
NSString* selectorName = [NSString stringWithFormat:#"set%#With%#:", ucfirstName, className];
SEL customPropertySetter = NSSelectorFromString(selectorName);
//check if there's a custom selector like this
if (![self respondsToSelector: customPropertySetter]) {
property.customSetters[className] = [NSNull null]; // this is line 855
return NO;
}
//cache the custom setter selector
property.customSetters[className] = selectorName;
}
if (property.customSetters[className] != [NSNull null]) {
//call the custom setter
//https://github.com/steipete
SEL selector = NSSelectorFromString(property.customSetters[className]);
((void (*) (id, SEL, id))objc_msgSend)(self, selector, value);
return YES;
}
return NO;
}
This is the originating method:
-(void)reloadUserInfoWithCompletion:(void (^) (LoginObject *response))handler andFailure:(void (^)(NSError *err))failureHandler {
NSString *lat;
NSString *lon;
lat = [NSString stringWithFormat:#"%.6f",[[LocationManager sharedInstance] getPosition].coordinate.latitude];
lon = [NSString stringWithFormat:#"%.6f",[[LocationManager sharedInstance] getPosition].coordinate.longitude];
NSMutableDictionary *params = [NSMutableDictionary new];
[params setObject:lat forKey:#"latitude"];
[params setObject:lon forKey:#"longitude"];
[[LoginHandler sharedInstance] getLoginToken:^(NSString *response) {
NSDictionary *headers;
if (response) {
headers = #{#"Login-Token":response};
}
GETRequest *req = [GETRequest new];
[req setCompletionHandler:^(NSString *response) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"response: %#",response);
NSError *err = nil;
self.loginObject.userDetails = [[User alloc] initWithString:response error:&err]; // <- this is the line reported in the crash
[self storeLoginObject];
NSLog(#"%#",self.loginObject.userDetails);
// [Utils updateFiltersFullAccessIfAll];
dispatch_async(dispatch_get_main_queue(), ^{
if (handler) {
handler(self.loginObject);
}
});
});
}];
[req setFailedHandler:^(NSError *err) {
if (failureHandler) {
failureHandler(err);
}
}];
NSLog(#"%#",params);
[req requestWithLinkString:USER_DETAILS parameters:nil andHeaders:headers];
}];
}
So setObject:forKey: can cause problems in two ways. 1. If object is nil or 2. the key is nil. Both could cause the crash you are seeing. Given that you are setting the object to [NSNull null] it is probably safe to assume that it is the key giving you problems (on line 855).
Walking back from there that would reveal that className is nil. If you look, your code does not protect against this. You make an assumption here that NSStringFromClass (a couple lines before) is giving you back a valid string, which assumes that the value originally passed into the method is non-nil. If it is nil it would make it past all of your checks, including !property.customSetters[className], since this would be !nil allowing it to enter the if.
If I am reading your code right (a bit hard since I cannot test any of my assumptions) NSLog(#"response: %#",response); would print out a nil response.
Try seeing how your code handles these unexpected nils and let me know in the comments how things go.
If you don't use model custom setters you can replace JSONModel __customSetValue:forProperty: with swizzling or Aspects library
#import "JSONModel+Aspects.h"
#import "JSONModel.h"
#import "Aspects.h"
#implementation JSONModel (Aspects)
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[JSONModel aspect_hookSelector:#selector(__customSetValue:forProperty:) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> aspectInfo) {
return NO;
} error:NULL];
});
}
#end
I need to implement the map view in my app to locate the required place. I had tried with the SVGeocoder concept.
[SVGeocoder geocode:searchfield.text
completion:^(NSArray *placemarks, NSHTTPURLResponse *urlResponse, NSError *error) {
}
But suppose I am trying to search any restaurent then the result is nil.
I was looking on Google map sdk but don't know how to do search functionality on GMSCameraPosition class.
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:latitude
longitude:longitude
zoom:5];
how to search with the address using google sdk.
Thanks in advance.
If I understood it correctly, you need the location co-ordinates from a address string. Its Forward geo-coding. You can take a look at Google's free api for this: Link1
You will need a API key from your google account to access this api and there is way to select a free or business plan depending on your number of requests.
You need to use a CLLocation object for getting co-ordinates from your address. I wrote a similar function. CLLocation* temp_location=[[CLLocation alloc]init];
temp_location=[GeoCoding findAddressCordinates:sourceAddressTxtField.text];
// Class GeoCoding to find Co-ordinates
#import <Foundation/Foundation.h>
#interface GeoCoding : NSObject {
}
+(CLLocation*)findAddressCordinates:(NSString*)addressString;
#end
#import "GeoCoding.h"
#import <CoreLocation/CLAvailability.h>
#implementation GeoCoding
+(CLLocation*)findAddressCordinates:(NSString*)addressString {
CLLocation *location;
NSString *url = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/geocode/json?address=%#&sensor=true", addressString];
url = [url stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSURL *wurl = [NSURL URLWithString:url];
NSData *data = [NSData dataWithContentsOfURL: wurl];
// Fail to get data from server
if (nil == data) {
NSLog(#"Error: Fail to get data");
}
else{
// Parse the json data
NSError *error;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
// Check status of result
NSString *resultStatus = [json valueForKey:#"status"];
// If responce is valid
if ( (nil == error) && [resultStatus isEqualToString:#"OK"] ) {
NSDictionary *locationDict=[json objectForKey:#"results"] ;
NSArray *temp_array=[locationDict valueForKey:#"geometry"];
NSArray *temp_array2=[temp_array valueForKey:#"location"];
NSEnumerator *enumerator = [temp_array2 objectEnumerator];
id object;
while ((object = [enumerator nextObject])) {
double latitude=[[object valueForKey:#"lat"] doubleValue];
double longitude=[[object valueForKey:#"lng"] doubleValue];
location=[[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
NSLog(#"CLLocation lat is %f -------------& long %f",location.coordinate.latitude, location.coordinate.longitude);
}
}
}
return location;
}
#end
You can then use this co-ordinates in your Google Map to focus your camera position.
hi I’m trying load data from my online server to view controller but I m getting few issues in that. Using the nsobject and json I’m trying to load in my view controller. I’m stuck with connecting the nsobject to the labels which i have in my storybord.
this is the nsboject code h file and m file
#interface vote : NSObject
#property (nonatomic,strong) NSString * question;
#property (nonatomic,strong) NSString * choose1;
#property (nonatomic,strong) NSString * choose2;
-(id) initWithquestion: (NSString *) qut andchoose1: (NSString *) ch andchoose2: (NSString *) cho;
this is code which i have used in my viewcontorller m file to get the datas.
#define getDataURL #"http://localhost/poll/view.php"
#implementation pollingpoliticalViewController
#synthesize que,cho1,cho2;
#synthesize json,pollarray;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//vote *cnnt = [pollarray ];
// Do any additional setup after loading the view.
}
-(void) retrieveData
{
NSURL * url =[NSURL URLWithString:getDataURL];
NSData * data=[NSData dataWithContentsOfURL:url];
json =[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
pollarray = [[NSMutableArray alloc]init];
for (int i=0; i<json.count; i++) {
NSString * dd =[[json objectAtIndex:i]objectForKey:#"ques"];
NSString * plae= [[json objectAtIndex:i]objectForKey:#"choss1"];
NSString * ti =[[json objectAtIndex:i]objectForKey:#"choss2"];
vote *myarray =[[vote alloc]initWithquestion:dd andchoose1:plae andchoose2:ti];
[pollarray addObject:myarray];
}
}
im stuck with the connecting with mutable array to the label which in my view controller so pls somebody suggest weather this is right way to do or what i have to do connect the datas to my view controller..
in tableview we use like we use like this:
fieldpolitical * cunt=[eventarray objectAtIndex:indexPath.row];
detailvc.detail = cunt.title;
detailvc.pla = cunt.place;
detailvc.tim = cunt.time;
detailvc.dat = cunt.date;
// detailvc.stott
i want to do same thing for my controller view
thanks
You should have to use NSURLConnection class for loading data from server. See example from following link:- http://codewithchris.com/tutorial-how-to-use-ios-nsurlconnection-by-example/#asynchronous
Also there is an external library for networking called AFNetworking, which is one of the best networking library for iOS and Mac OS X. You can visit this link:- http://afnetworking.com/ .
You can find tutorial on AFNetworking from following link:- http://www.raywenderlich.com/30445/afnetworking-crash-course
If you want to go with synchronous method then you should have to use dispatch_async like as
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
// ... perform a blocking, synchronous URL retrieval ...
NSURL * url =[NSURL URLWithString:getDataURL];
NSData * data=[NSData dataWithContentsOfURL:url];
// ... and hop back onto the main queue to handle the result
dispatch_async(dispatch_get_main_queue(),
^{
json =[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
pollarray = [[NSMutableArray alloc]init];
for (int i=0; i<json.count; i++) {
NSString * dd =[[json objectAtIndex:i]objectForKey:#"ques"];
NSString * plae= [[json objectAtIndex:i]objectForKey:#"choss1"];
NSString * ti =[[json objectAtIndex:i]objectForKey:#"choss2"];
vote *myarray =[[vote alloc]initWithquestion:dd andchoose1:plae andchoose2:ti];
[pollarray addObject:myarray];
});
});
Hope It will help you.
I've a NSMutableDictionary , that i add to some custom NSObjects which i receive asynchronously. Each time i receive new data i clear the dictionary then add the new objects. Then push data to another class "UIView" to render the content in the dictionary. I have been getting SIGABRT exception which as far as i know means the object trying to access has been released. I have tried synchronization blocks , creating a mututablecopy and getting allvalues but nothing worked now My question how i can achieve synchronization
here is a code snippet ,
#interface MyAltViewController : UIViewController
{
__block NSMutableDictionary *currentDataList;
TestUIVIEW *myUIVIEW
}
#implementation MyAltViewController
......
- (void)viewDidLoad
{
currentDataList = [[NSMutableDictionary alloc] initWithCapacity:10];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(processDataMessag:)
name:view_Data object:nil];
}
.....
-(void)processDataMessag:(NSNotification *) notification
{
[currentDataList removeAllObjects];
NSArray tmpAr= (NSArray*) [notification object]
dispatch_async(dbQueue, ^(void)
{
/// Loop through the array and process the data then add to NSDictionary
[self pushtoMUViewLayer:currentDataList];
});
}
.........
-(void)pushtoMUViewLayer:(NSMutableDictionary *)ina
{
/// Even here if i try to access and object within
/// currentDataList and just a specific NSString i get the SIGABRT
[myUIVIEW updateWithData:ina];
}
//////////////////////////////////////////////////
#interface TestUIVIEW : UIView
{
NSMutableDictionary *mdata;
UIImage *outputImage ;
}
#property (assign) NSMutableDictionary *mapdata;
#property (retain) UIImage *outputImage ;
#implementation TestUIVIEW
.......
-(void)updateWithData:(NSMutableDictionary *)data
{
mdata = [data retain]; // ??? not sure how to set it correctly
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self CreateImage];
[self setNeedsDisplay];
});
}
-(void) CreateImage
{
NSString *key;
for(key in mapdata)
{
DataMessage *tmpData= (DataMessage*)[mapdata objectForKey:key];
NSString *aID = [ tmpData alt] ;
double aLat = [ tmpData lat] ;
double aLong = [ tmpData lon] ;
/*
Drawing code I can Get aLat & A Long
but it fails with SiABRT when trying to render
aID
/*
}
}
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGPoint imagePoint = CGPointMake(0, 0);
[outputImage drawAtPoint:imagePoint];
}
/// if that synchronization didnt work . i can use NSNotification to see my currentDataList
// to my currentDataList
Adding #synchronised block around all the places where i access the NSDictionary did solove the problem . Thanks all for your input