i'm working on an iPhone application which retrieve sqlite database from server through json/rest api. And user can add rows to its tables locally and can update it locally. Now, as i added some rows to tables in local database, i want to sync/insert only those new rows to server database from my local updated db.
Please help if somebody knows about that api method(json/rest) or If there is any tutorial related to it please help.
When you say you are retrieving the "sqlite" database, do you mean a "json" representation of all the tables and their rows? I'm assuming you're not actually sending the "sqlite" db file.
For sending and retrieving json via http you can use NSURLConnection and NSURLRequest for simplicity, because they are built in. If you want to enforce a mapping to core data, you can use the RestKit framework for both the connection and data handling.
Here is an example implementation of the former solution - it assumes you are ARC, you will need to add the appropriate retain and release statements otherwise.
1) declare the class you're using as the appropriate delegate
#interface ClassName : NSObject <NSURLConnectionDelegate>
2) declare a responseData object that will be used to receive data
//interface
#property (nonatomic, strong) NSMutableData *responseData;
//implementation
#synthesize responseData;
3) create the function that sends the json request
- (void)sendRequest
{
responseData = [NSMutableData data];
//whatever your server address is
NSURL *url = [NSURL URLWithString:#"http://www.resturl.com/whatever"];
//just sample data - create this dictionary with what you want to send
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"SomeValue" forKey:#"SomeKey"];
NSError *jsonError;
//NSJSONSerialization is Apple's new json serialization class so we can use it to convert to and from json and foundation objects
NSData *requestdata = [NSJSONSerialization dataWithJSONObject:params options:0 error:&jsonError];
NSMutableURLRequest *request;
request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", [requestdata length]] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:requestdata];
//this kicks off the request asynchronously
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
//if you'd rather send a synchronous request, you can use the static NSURLConnection function
//sendSynchronousRequest:returningResponse:error:
}
4)implement the delegate functions to receive our data
//any time a piece of data is received we will append it to the responseData object
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.responseData appendData:data];
}
//some sort of error, you can print the error or put in some other handling here, possibly even try again but you will risk an infinite loop then unless you impose some sort of limit
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Clear the activeDownload property to allow later attempts
self.responseData = nil;
}
//connection has finished, thse requestData object should contain the entirety of the response at this point
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *jsonError;
NSDictionary *responseDict =
[NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONWritingPrettyPrinted
error:&jsonError];
if(responseDict)
{
NSLog(#"%#", responseDict);
}
else
{
NSLog(#"%#", [jsonError description]);
}
//clear out our response buffer for future requests
self.responseData = nil;
}
If you want to update the remote database with some new information, just keep track of the new rows locally (rather than just merging them with the full dataset) and send a new request containing only those rows to an endpoint that will add them. That is the simplest way to do this without enforcing an actual mapping.
Related
I am trying to use the code below to 'trigger' the web address. The web server doesn't return any data. But the NSURLConnection is being established.
NSString *serverAddressTest = #"http://domain.com";
NSString *fullWebAddress = [NSString stringWithFormat:#"%#?CustomerName=%#&ContactNo=%#&Products=%#",serverAddressTest,customer,contactnumber,allProductsInString];
NSURL *url = [NSURL URLWithString:fullWebAddress];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:url];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
NSMutableData *webData = [NSMutableData data];
NSLog(#"%#",webData);
}
else {
NSMutableData *webData = [NSMutableData data];
NSLog(#"%#",webData);
}
When you write:
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest
delegate:self];
you are starting an asynchronous url connection.
Then immediately after that you are testing whether the connection was successful or not and creating an instance of NSMutableData with local scope. Your NSURLConnectionDelegate methods (which you haven't posted) will not have access to this locally-scoped NSMutableData variable.
Have you indeed implemented the methods of the NSURLConnectionDelegate protocol?
Try to send synchronous request to localise the problem:
NSError *error;
NSData *returnData = [NSURLConnection sendSynchronousRequest: theRequest
returningResponse: nil
error: &error];
NSLog(#"error = %#, \ndata = %#", error, returnData);
You also need to also implement the delegate Protocols. ( As NSBum says)
Using Apples example
shown here is the data is returned when the parts are put together.:
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [NSMutableData dataWithCapacity: 0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!theConnection) {
// Release the receivedData object.
receivedData = nil;
NSLog(#"FAIL " );
// Inform the user that the connection failed.
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#" response %#", response);
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse object.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
NSLog(#" receivedData %#", receivedData);
}
receivedData is not local but declared elsewhere. (NSMutableData* receivedData;)
I do not use this much so cannot expand further without reading the docs fully myself; which is what you need to do. :-)
I am trying to call a class method that takes a string and posts it to a site to receive a JSON response(among some other variables I have stored in the DataClass). I am stuck trying to return the data in the form of a response and can not at this point even NSLog the returned data. The question is, now that I have called my class method, how can the class method wait to return a response from an HTTP POST to return data? Once I return my JSON, I can expand it to a dictionary and process from there. Help is appreciated :)
Class Method:
//
// APISample.m
//
// Created by Sam on 1/6/13.
// Copyright (c) 2013 Sam. All rights reserved.
//
#import "APISample.h"
#import "DataClass.h"
#implementation APISample
#synthesize first_name = _first_name;
#synthesize last_name = _last_name;
#synthesize profile_pic_url = _profile_pic_url;
#synthesize responseData;
-(id)init
{
self = [super init];
return self;
NSLog(#"Loaded APISample and fetching");
}
+(id)getDataAboutUser:(NSString *)user_request_id;
{
DataClass *userdata=[DataClass getInstance];
NSLog(#"Loaded APISample and fetching %#", user_request_id);
NSMutableURLRequest *user_fetch_details = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://10.0.23.161/users/user_fetch_details.php"]];
[user_fetch_details setHTTPMethod:#"POST"];
NSMutableString *postString = [NSMutableString stringWithString:#"id=123"];
[postString appendString:#"&userrequest_id="];
[postString appendString:[userdata.str_userid copy]];
[postString appendString:#"&user_id="];
[postString appendString:[userdata.str_userid copy]];
[postString appendString:#"&identifier="];
[postString appendString:[userdata.str_identifier copy]];
[user_fetch_details setValue:[NSString stringWithFormat:#"%d", [postString length]] forHTTPHeaderField:#"Content-length"];
[user_fetch_details setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:user_fetch_details delegate:self];
NSMutableData *responseData=[NSMutableData data];
[responseData appendData:[NSURLConnection connection:didReceiveData];
if (connection) {
// Create the NSMutableData that will hold
// the received data
// receivedData is declared as a method instance elsewhere
NSMutableData *responseData=[NSMutableData data];
} else {
// inform the user that the download could not be made
}
NSLog(#"Received Data %#", [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding]);
return [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
NSString *receivedDataString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
if ([receivedDataString isEqualToString: #"error"]) {
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"An error has occured. The application will now exit. Unexpected Response!"
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles:nil];
[errorAlert show];
exit(0);
}else{
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSString *firstnameResponse = [json objectForKey:#"first_name"];
NSString *lastnameResponse = [json objectForKey:#"last_name"];
NSString *profile_pic_urlResponse = [json objectForKey:#"profile_pic_url"];
NSLog(#"didReceiveData %# analysed " , firstnameResponse);
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading");
NSLog(#"Succeeded! Received %d bytes of data",[self.responseData length]);
}
#end
I receive no data in the log after "Received Data" and do not see my error messages. Thanks you
The design pattern you are describing is called a CallBack. You need to be notified of an event occurring at some point in the future. In objective-c there are 4 main forms of callbacks.
Target Action Pairing (this is what is used with buttons, and things of the like. "When this button is pressed notify my target, and tell them to execute this action")
Delegation (you are using a form of delegation in the code above with NSURLConnection. When you see the word 'delegate' i want you to think 'helper object'. You are saying, "hey NSURLConnection, when important events happen, i would like you to tell this delegate (helper object) about these events)
Notifications (these are used a lot when dealing with model objects changing)
and finally... the one i would recommend for your purposes...
Blocks.
A block is a very cool variable. Most variables hold data. A block is a variable which holds code to be executed at some point in the future. So in your situation you could pass a completion block along with your method getDataAboutUser:(NSString *)user_request_id. So it would look like this.
getDataAboutUser:(NSString*)string withCompletion:(void(^)(NSData *finishedData))cBlock
Store that cBlock as an instanceVar. Then when your NSURLConnection finishes downloading all its data, you will execute the cBlock, passing in the finished data as an argument.
Blocks are a fairly complicated things if you have not used them before, so i would reccomend taking 20 minutes and reading this.
Since you need your method to wait for a response before returning, you can use NSURLConnection's convenience class method sendSynchronousRequest to carry out a synchronous request instead of creating and managing an NSURLConnection instance asynchronously.
So instead of your [[NSURLConnection alloc] init...] line you can do this:
NSURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:user_fetch_details returningResponse:&response error:&error];
Following which you can immediately parse the JSON from responseData instead of doing that in the connection:didReceiveData delegate.
Edit: Just saw user698846's suggestion to modify your method signature to take a completion block. That's also a good and possibly cleaner way to approach your problem if you are at liberty to change your method signature (i.e. nobody is requiring your function to return synchronously). Either way, sendSynchronousRequest is possibly the easiest way out and there's no shame in it especially if there's nothing your app nor your user can do while waiting for the request to complete.
This is some code:
NSURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:user_fetch_details returningResponse:&response error:&error];
I have a little Mac application which should be able to post Data to my web server which saves the data in a database. Now that's the Code I have now:
NSData *postData = [urlString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"http://..."]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection) NSLog(#"Done");
And it works fine. But now I want to check whether the data was correct (and stored in the database) or something (like eMail) is wrong. The PHP file prints e.g. "email incorrect" out if the E-Mail is not correct.
But how can I fetch this data (which PHP prints out) in Xcode that the App knows whether it was successful or not?
Thanks for answers!
You need to implement the NSURLConnectionDelegate methods connection:didReceiveData: and connectionDidFinishLoading:
According to the docs didReceiveData: may be called multiple times per NSURLRequest (i.e. the response will not always arrive all at once) so the recommended method is to append the incoming data to buffer during connection:didReceiveData: and then do any processing on the data in connectionDidFinishLoading:.
You could create a property on your class like this:
#property (nonatomic, strong) NSMutableData *dataBuffer;
And instantiate your buffer during viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataBuffer = [[NSMutableData alloc] init];
// do any other setup your class requires...
}
And then implement the delegate methods:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// clear the buffer in case it has been used previously
[self.dataBuffer setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.dataBuffer appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSString *response = [NSString stringWithUTF8String:[self.dataBuffer bytes]]);
NSLog(#"response from HTTP request=>%#", response);
}
This can all also be done using a third-party networking library like ASIHTTPRequest (which is no longer under active develoment) or AFNetworking, but sometimes those can be overkill depending upon what you are trying to accomplish
Implement the delegate method for the NSURLConnection,
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response.
This method is called by the app when the request finishes. You can access response data using the 'response' parameter.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
Then just convert the 'data' parameter into a string using:
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
Then you can search the response for whatever string you want, e.g., "email incorrect".
PS: I generally don't use NSURLConnection/NSURLRequest for HTTP requests, I'd recommend you check out ASIHTTPRequest for really simple HTTP requests/connections.
Hope this helps.
I'm a novice in iOS developing, and have some problems with understanding web service organization. I want to send a Get query to the URL. And I do it so:
-(BOOL) sendLoginRequest{
NSString *getAction = [NSString stringWithFormat:#"action=%#&username=%password=%#",#"login",self.log.text, self.pass.text];
NSString *getUserName = [NSString stringWithFormat:#"username=%#",self.log.text];
NSString *getPassword = [NSString stringWithFormat:#"username=%#",self.pass.text];
NSData *getDataAction = [getAction dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *getLengthAction = [NSString stringWithFormat:#"%d", [getDataAction length]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:#"http:http://www.site.fi/api/"]];
[request setHTTPMethod:#"GET"];
[request setValue:getLengthAction forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:getLengthAction];
self.urlConnection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
NSAssert(self.urlConnection != nil, #"Failure to create URL connection.");
// show in the status bar that network activity is starting
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
the answer may be "true" or "false"
but how can I take this answer?
You should define next methods to get answer:
Start connection: [self.urlConnection start];
Check server response:
- (void)connection:(NSURLConnection *)theConnection didReceiveResponse:(NSURLResponse *)response
Collect data that servers sends you:
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)data
Be sure to manage errors:
- (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error
Check received data:
- (void)connectionDidFinishLoading:(NSURLConnection *)theConnection
To be more sure that you correctly understood me check NSURLConnection Class Reference
Send [self.urlConnection start]; and implement the NSURLConnectionDelegate methods to receive the response. Alternatively use ASIHTTPRequest and the block handlers, which to my way of thinking are much easier to write for beginners, provided you don't need to run on iOS pre-4.1.
You will gather the data returned as NSData; just convert that to a string, and either call boolValue on the string (check the docs for its rather strange tests), or use a specific set of your own tests.
I am new to Objective C, so I'm not really even sure what this message means:
EXC_BAD_ACCESS
When executing the following code:
-(void)HelloWorld
{
NSURL *url = [NSURL URLWithString:#"http://example.com/service.asmx/HelloWorld"];
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL: url];
//do post request for parameter passing
[request setHTTPMethod:#"POST"];
//set the content type to JSON
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection release];
[request release];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Store incoming data into a string
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
I'm attempting to integrate with the Json Framework.
I call HelloWorld, which executes an async request to my service. What's strange, is that it worked one time, and now I get this EXC_BAD_ACCESS message every subsequent time. Any ideas what would be causing this?
You shouldn't be releasing the request. It's already autoreleased.
EXC_BAD_ACCESS means you have a bad pointer. In your case, it's because you are releasing the request when it's already autoreleased.
There's also another problem with your code (unrelated to the crash). You create an NSURLConnection and immediately release the connection after creation. When creating an asynchronous connection, you should release the connection in the delegate methods (if connection fails or if connection did finish loading).