I'm using the RestKit for a simple request that returns JSON data.
I've implemented this code that works only the first time that the viewDidLoad is called, the second time the request is not performed. Why??
- (void)viewDidLoad
{
[super viewDidLoad];
[self planOtpTrip];
}
-(void) planOtpTrip{
NSLog(#"[INFO] planoOtpTrip");
client = [RKClient clientWithBaseURLString:#"http://myserver/opentripplanner-api-webapp/ws"];
[client setValue:#"application/json" forHTTPHeaderField:#"Accept"];
NSDictionary *queryParameters = [NSDictionary dictionaryWithObjectsAndKeys:#"45.028952,7.624598",#"fromPlace",#"45.06123,7.65981", #"toPlace",#"TRANSIT,WALK" ,#"mode", nil];
// Imposto il nome della API da richiamare
NSString *getResourcePath = RKPathAppendQueryParams(#"/plan", queryParameters);
[client get:getResourcePath delegate:self];
}
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response {
// where you handle response object
NSLog(#"Risposta ricevuta: %#", response.URL);
NSLog(#"Response Body: %#", response.bodyAsString);
id jsonParser = [[RKParserRegistry sharedRegistry] parserForMIMEType:RKMIMETypeJSON];
NSError *error = nil;
NSDictionary* parsedResponse = [jsonParser objectFromString:[response bodyAsString] error:&error];
if (error == nil)
{
NSLog(#"GET returned with HTTP Code %d and parsedContent: %#", [response statusCode], parsedResponse);
}
else
{
NSLog(#"Error: %#", error);
}
return [self renderResponse:parsedResponse];
}
I had this problem as well but i found a solution. Define the RKClient in your .h file like so:
#property (retain) RKClient *downloadClient;
Then in your .m file synthesize the downloadClient like so:
#synthesize downloadClient;
And then in your didLoadResponse method you release the downloadClient when your done with it, like so:
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response
{
//do something with your response
[downloadClient release];
}
Related
I am trying to monitor file changes in local and iCloud directories and have implemented the NSFilePresenter protocol methods but the only method that gets called is presentedItemAtURL.
Am I correct in assuming that I should be able to monitor a local or an iCloud directory and get notified any time any process adds, modifies or deletes a file in the directory.
Here is the basic code for the OS X App:
- (void)awakeFromNib {
_presentedItemURL = myDocumentsDirectoryURL;
_presentedItemOperationQueue = [[NSOperationQueue alloc] init];
[_presentedItemOperationQueue setMaxConcurrentOperationCount: 1];
_fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self];
}
- (NSURL*) presentedItemURL {
FLOG(#" called %#", _presentedItemURL);
return _presentedItemURL;
}
- (NSOperationQueue*) presentedItemOperationQueue {
FLOG(#" called");
return _presentedItemOperationQueue;
}
- (void)presentedItemDidChange {
FLOG(#" called");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadData];
});
}
-(void)accommodatePresentedItemDeletionWithCompletionHandler:(void (^)(NSError *errorOrNil))completionHandler
{ FLOG(#" called");
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self reloadData];
}];
completionHandler(nil);
}
-(void)presentedSubitemDidChangeAtURL:(NSURL *)url {
FLOG(#" called");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadData];
});
}
-(void)presentedSubitemDidAppearAtURL:(NSURL *)url {
FLOG(#" called");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadData];
});
}
Long time ago, I know, but perhaps this will still help. NSFilePresenter will only notify you about changes made by another process that makes changes to a directory or file USING AN NSFileCoordinator. If another process (eg: iTunes file sharing) makes changes without an NSFileCoordinator, you won't be notified.
This is in no way my final implementation and I will edit/update as I improve. But since there is nil examples on how to do this, i figured i'd share something that works!!! That's right, it works. I am able to read the file in my app, and at the same time make a change in textedit and the changes propagate to my app. Hope this helps bud.
PBDocument.h
#interface PBDocument : NSObject <NSFilePresenter>
#property (nonatomic, strong) NSTextView *textView;
#pragma mark - NSFilePresenter properties
#property (readonly) NSURL *presentedItemURL;
#property (readonly) NSOperationQueue *presentedItemOperationQueue;
- (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError *__autoreleasing *)outError textView:(NSTextView*)textView;
#end
PBDocument.m
#interface PBDocument ()
#property (readwrite) NSURL *presentedItemURL;
#property (readwrite) NSOperationQueue *presentedItemOperationQueue;
#property (readwrite) NSFileCoordinator *fileCoordinator;
#end
#implementation PBDocument
- (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError *__autoreleasing *)outError textView:(NSTextView*)textView {
self = [super init];
if (self) {
_textView = textView;
_presentedItemURL = url;
_presentedItemOperationQueue = [NSOperationQueue mainQueue];
[NSFileCoordinator addFilePresenter:self];
_fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self];
[self readWithCoordination];
}
return self;
}
- (void)readWithCoordination {
NSError *error = nil;
[self.fileCoordinator coordinateReadingItemAtURL:_presentedItemURL options:NSFileCoordinatorReadingWithoutChanges error:&error byAccessor:^(NSURL *newURL) {
NSLog(#"Coordinating Read");
NSError *error = nil;
NSFileWrapper *wrapper = [[NSFileWrapper alloc] initWithURL:newURL options:0 error:&error];
if (!error) {
[self readFromFileWrapper:wrapper ofType:[self.presentedItemURL pathExtension] error:&error];
}
if (error) #throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:#"%#", error] userInfo:nil];
}];
if (error) #throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:#"%#", error] userInfo:nil];
}
- (void)presentedItemDidChange {
[self readWithCoordination];
}
#end
If it's any help to anyone this is the approach (FSEvents) I ended up using recently for a file sync solution and it seems to work for any file system. I have not done any research recently on NSFileCoordinator to see whether this is better worse or what the use cases are as a comparison.
I also did not test every use case so your mileage may vary.
https://github.com/eonil/FSEvents
hi i want to integrate Via Me social site into my iphone app,i googled but didn't find any samples.
The basic process is as follows:
Create a custom URL scheme for your app. Via Me will use this after the user has been authenticated, to return to your app. In my example, I created one called "robviame://"
Register your app at http://via.me/developers. This will give you a client id and a client secret:
When you want to authenticate the user, you call:
NSString *redirectUri = [[self redirectURI] stringByAddingPercentEscapesForURLParameterUsingEncoding:NSUTF8StringEncoding];
NSString *urlString = [NSString stringWithFormat:#"https://api.via.me/oauth/authorize/?client_id=%#&redirect_uri=%#&response_type=code", kClientID, redirectUri];
NSURL *url = [NSURL URLWithString:urlString];
[[UIApplication sharedApplication] openURL:url];
What that will do is fire up your web browser and give the user a chance to log on and grant permissions to your app. When user finishes that process, because you've defined your custom URL scheme, it will call the following method in your app delegate:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
// do whatever you want here to parse the code provided back to the app
}
for example, I'll call a handler for my Via Me response:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
ViaMeManager *viaMeManager = [ViaMeManager sharedManager];
if ([[url host] isEqualToString:viaMeManager.host])
{
[viaMeManager handleViaMeResponse:[self parseQueryString:[url query]]];
return YES;
}
return NO;
}
// convert the query string into a dictionary
- (NSDictionary *)parseQueryString:(NSString *)query
{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
NSArray *queryParameters = [query componentsSeparatedByString:#"&"];
for (NSString *queryParameter in queryParameters) {
NSArray *elements = [queryParameter componentsSeparatedByString:#"="];
NSString *key = [elements[0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *value = [elements[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
value = [[value componentsSeparatedByString:#"+"] componentsJoinedByString:#" "];
[dictionary setObject:value forKey:key];
}
return dictionary;
}
That handler might, for example, save the code and then request the access token:
- (void)handleViaMeResponse:(NSDictionary *)parameters
{
self.code = parameters[#"code"];
if (self.code)
{
// save the code
[[NSUserDefaults standardUserDefaults] setValue:self.code forKey:kViaMeUserDefaultKeyCode];
[[NSUserDefaults standardUserDefaults] synchronize];
// now let's authenticate the user and get an access key
[self requestToken];
}
else
{
NSLog(#"%s: parameters = %#", __FUNCTION__, parameters);
NSString *errorCode = parameters[#"error"];
if ([errorCode isEqualToString:#"access_denied"])
{
[[[UIAlertView alloc] initWithTitle:nil
message:#"Via Me functions will not be enabled because you did not authorize this app"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
}
else
{
[[[UIAlertView alloc] initWithTitle:nil
message:#"Unknown Via Me authorization error"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
}
}
}
and the code to retrieve the token might look like:
- (void)requestToken
{
NSURL *url = [NSURL URLWithString:#"https://api.via.me/oauth/access_token"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
NSDictionary *paramsDictionary = #{#"client_id" : kClientID,
#"client_secret" : kClientSecret,
#"grant_type" : #"authorization_code",
#"redirect_uri" : [self redirectURI],
#"code" : self.code,
#"response_type" : #"token"
};
NSMutableArray *paramsArray = [NSMutableArray array];
[paramsDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
[paramsArray addObject:[NSString stringWithFormat:#"%#=%#", key, [obj stringByAddingPercentEscapesForURLParameterUsingEncoding:NSUTF8StringEncoding]]];
}];
NSData *paramsData = [[paramsArray componentsJoinedByString:#"&"] dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:paramsData];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error)
{
NSLog(#"%s: NSURLConnection error = %#", __FUNCTION__, error);
return;
}
NSError *parseError;
id results = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (parseError)
{
NSLog(#"%s: NSJSONSerialization error = %#", __FUNCTION__, parseError);
return;
}
self.accessToken = results[#"access_token"];
if (self.accessToken)
{
[[NSUserDefaults standardUserDefaults] setValue:self.accessToken forKey:kViaMeUserDefaultKeyAccessToken];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}];
}
Hopefully this will be enough to get you going. This is described in greater detail at the http://via.me/developers page.
I have a problem code with using AFNetworking:
#import "SyncProfile.h"
#import "AFNetworking.h"
#implementation SyncProfile: NSObject
#synthesize delegate = _delegate;
- (BOOL)syncProfile {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *token =[userDefaults objectForKey:#"token"];
int user_id = [userDefaults objectForKey:#"user_id"];
if([token length]) {
self.profileData = [[self sendRequest:#"method.get" token:token withUser:user_id andParameters:#"param1,param2"] valueForKeyPath:#"response"];
NSLog(#"%#", self.profileData);
return YES;
} else
return NO;
}
-(id)sendRequest:(NSString *)apiMethod token:(NSString *)token withUser:(int)user_id andParameters:(NSString *)param {
NSMutableString *apiLink = [NSMutableString stringWithFormat:#"https://domain.com/method/%#?uid=%#&fields=%#&access_token=%#", apiMethod, user_id, param, token];
NSURL *url = [[NSURL alloc] initWithString:apiLink];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"%#", JSON);
self.req = JSON;
[self myMethod:JSON];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
[operation start];
return self.req;
}
- (id)myMethod:(id)data {
NSLog(#"%#",data);
return 0;
}
#end
I need to return a variable with the result AFNetworking back method. But the result is given much later than the method returns. When I use a different method to handle the result, it does not. Tried to use the [operation waitUntilFinished] but nothing has changed.
Result in Xcode Output:
//Return variable from "sync" method
2013-02-26 23:57:29.793 walkwithme[13815:11303] (null)
//Return from AFN response
2013-02-26 23:57:31.063 walkwithme[13815:11303] {response = ({someJSON})}
//Return from MyMethod
2013-02-26 23:57:31.063 walkwithme[13815:11303] {response = ({someJSON})}
You definitely don't want to use any wait methods. What you need to do is have a call back in your success and failure blocks. You can do this the way I showed in this question you could also do something else like message passing. The key thing to realize is you won't be using the typical method return pattern. Reason being with asynchronous methods like this you have no idea when it will finish which is why it uses the block call backs. Like I said you definitely don't want to wait because that could entirely block your application.
EDIT:
I use this code in one of my projects:
Declare method
- (void)postText:(NSString *)text
forUserName:(NSString *)username
ADNDictionary:(NSDictionary *)dictionary
withBlock:(void(^)(NSDictionary *response, NSError *error))block;
Then inside this method I pass those parameters to the network request.
Return values in the block with:
if (block) {
block(responseObject, someError);
}
Then I call it with this:
[[KSADNAPIClient sharedAPI] postText:postText
forUserName:username
ADNDictionary:parameters
withBlock:^(NSDictionary *response, NSError *error)
{
if (error) {
// Deal with error
} else {
// Probably success!
}
}
This way the called method returns it's values to the caller method inside the block. I think about it that it defers the block to the caller.
I have a problem which I could really do with some help with please
I am using NSURLconnection to download a large file (27MB). the code work fine when there is no network interruption. In order to allow for network issues and only partially downloaded file I have added code to check to see how much of the file is downloaded and then using a server request to download the missing portion.
The code works as it should IF I download part of file, stop the program running and then run again - the download then commences where it left off and i have complete file.
However if I hit the download button a second time without stopping the program then didReceiveData only gets called once and adds just 200KB to the file and it tells me file has been succesfully downloaded.
Help please - I have spent ages trying to figure out what I'm doing wrong.
Code below:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"Response code = %d",httpResponse.statusCode );
file = [NSFileHandle fileHandleForUpdatingAtPath:filename] ;// file is in .h
if (file) {
[file seekToEndOfFile];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (file) {
[file seekToEndOfFile];
NSLog(#"file is %#",file);
}
[self.file writeData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if([[NSFileManager defaultManager] fileExistsAtPath:filename])
{
[file closeFile];
file = nil;
theConnection = nil;
filename = nil;
theRequest = nil;
}
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[file closeFile];
file = nil;
theConnection = nil;
filename = nil;
}
- (IBAction)downloadFile:(id)sender {
filename = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:movie1]; // filename is in .h file
theRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:movieDownload1] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSUInteger downloadedBytes = 0;
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:filename]) {
NSError *error = nil;
NSDictionary *fileDictionary = [fm attributesOfItemAtPath:filename error:&error];
if (!error && fileDictionary)
downloadedBytes = [fileDictionary fileSize];
} else {
[fm createFileAtPath:filename contents:nil attributes:nil];
}
if (downloadedBytes > 0) {
NSString *requestRange = [NSString stringWithFormat:#"bytes=%d-",downloadedBytes];
[theRequest setValue:requestRange forHTTPHeaderField:#"Range"];
}
theConnection = nil;
theConnection = [NSURLConnection connectionWithRequest:theRequest delegate:self];
}
Instead of using seekToEndOfFile in didReceiveResponse and didReceiveData, you can try the following code snippet. It worked well for me.
- (id)initWithURL:(NSString *)downloadString
{
if (![super init])
return nil;
// Construct the URL to be downloaded
self.downloadURL = [[NSURL alloc]initWithString:downloadString];
self.downloadData = [[NSMutableData alloc] init];
self.downloadedFilename = [[self.downloadURL path] lastPathComponent];
[self downloadFile];
return self;
}
-(void) downloadFile
{
// set the filePath
docFolderPath = [NSHomeDirectory() stringByAppendingPathComponent: [NSString stringWithFormat: #"Documents/%#", self.downloadedFilename]];
self.downloadStream = [NSOutputStream outputStreamToFileAtPath:docFolderPath append:NO];
if (!self.downloadStream)
{
self.error = [NSError errorWithDomain:[NSBundle mainBundle].bundleIdentifier
code:-1
userInfo:#{#"message": #"Unable to create NSOutputStream", #"function" : #(__FUNCTION__), #"path" : self.downloadedFilename}];
return;
}
[self.downloadStream open];
self.downloadConnection = [[NSURLConnection alloc] initWithRequest:downloadRequest delegate:self];
[self.downloadConnection start];
}
//code snippet for the Resume functionality after your downloading gets paused/cancel
-(void) resumeInterruptedDownload
{
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:docFolderPath])
{
NSError *error = nil;
NSDictionary *fileDictionary = [fm attributesOfItemAtPath:docFolderPath
error:&error];
if (!error && fileDictionary)
self.downloadedBytes = [fileDictionary fileSize];
} else
{
[fm createFileAtPath:docFolderPath contents:nil attributes:nil];
}
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.downloadURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
// Define the bytes we wish to download.
if(self.downloadedBytes != 0)
{
NSString *range = [NSString stringWithFormat:#"bytes=%i-", self.downloadedBytes];
[request setValue:range forHTTPHeaderField:#"Range"];
}
// Data should immediately start downloading after the connection is created.
self.downloadConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE];
}
It worked perfectly for me in case of the large files in Mbs but for the small files in Kbs it sometimes fails. You don't have to try much in all these delegate methods of NSURLConnection. One thing you can do is that set the macros for each state i.e, cancel, pause, downloading,downloaded so that you can come to know that when do you want to resume the downloading. Also you can try the following http://nachbaur.com/blog/resuming-large-downloads-with-nsurlconnection.
I know it is too late to reply, but I just got into IOS. If you try this, please let me know whether it worked or not. Thanks :) :)
RestKit doesn't send second request
I have a simple application with 1 view and 2 buttons
titles of buttons are hostnames (yandex.ru and localhost:3000)
when i click on any button at first time
request works
but if i click second time on any it doesn't send it....
what do i do wrong?
below is part of code of my ViewController
- (IBAction)testRestkit:(UIButton *)sender
{
RKClient *client =[RKClient clientWithBaseURLString:[NSString stringWithFormat:#"http://%#",sender.titleLabel.text]];
client.cachePolicy = RKRequestCachePolicyNone;
[client get:nil delegate:self];
}
- (void)requestWillPrepareForSend:(RKRequest *)request
{
NSLog(#"Preparing for request......");
}
- (void)request:(RKRequest *)request didFailLoadWithError:(NSError *)error
{
NSLog(#"%#",[error localizedDescription]);
}
- (void)request:(RKRequest *)request didLoadResponse:(RKResponse *)response
{
if ([response isHTML]) {
NSLog(#"Loaded html!");
} else{
NSLog(#"Loaded some response!");
}
}
if i send request like code below . they works!!!!
someone could explain difference between both methods???
- (IBAction)testRestkit:(UIButton *)sender
{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://%#",sender.titleLabel.text]];
RKRequest *theRequest = [[RKRequest alloc] initWithURL:url];
theRequest.queue = [RKClient sharedClient].requestQueue;
theRequest.delegate = self;
[theRequest setMethod:RKRequestMethodGET];
[theRequest send];
}
I think the problem is that the first time you call clientWithBaseURLString:, an [RKClient sharedClient] singleton is initialized (and properly retained), so the subsequent call does work. When you invoke the code for the second time, the convenience constructor returns an autoreleased instance of RKClient with the another base url.
The correct way to handle multiple base URLs in RestKit is to manage two, separate instances of RKClient, e.g.
#property (nonatomic, retain) RKClient *localhostClient;
#property (nonatomic, retain) RKClient *remoteClient;
and initialize them eg. in your init method (or lazily in your method).
static NSString *kRemoteServerUrl = #"http://yandex.ru";
- (IBAction)testRestkit:(UIButton *)sender
{
RKClient *client = nil;
if ([sender.titleLabel.text isEqualToString:kRemoteServerUrl]) {
if (!self.remoteClient) {
self.remoteClient = [RKClient clientWithBaseURLString:kRemoteServerUrl];
client.cachePolicy = RKRequestCachePolicyNone;
}
[self.remoteClient get:nil delegate:self];
} else if (.....)
....
}