I have an url request method, because each page will be used, I want to make it public.
(Poor standard of English, forgive me)
here is my class method:
- (void)httpRequest :(NSString *)url :(NSMutableArray *)array;
{
NSURL *myUrl = [NSURL URLWithString:url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:myUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"请求完成");
NSArray *arr;
arr = [NSJSerialization JSONObjectWithData:operation.responseData options:NSJSONReadingAllowFragments error:NULL];
[array addObjectsFromArray:arr];
???????
}failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"请求失败: %#", error);
??????
}];
[operation start];
}
I know how to pass simple parameters,like array and url.But when I use this method,code in the question mark will be different everytime.What can I do to make it can be passed,like parameters.
Like: [myApple httpRequest :myurl :myarray : (susucess .....) : (failure......)];
the question mark can be filled in: mytableView reloadData or nslog...
I'm not sure if i explained my question clearly,i don't know if block can solve this,thanks,waiting for your help.
Firstly, note that all method parameters in Objective-C should be named. So instead of - (void)httpRequest :(NSString *)url :(NSMutableArray *)array;, you should have something like:
- (void)performHttpRequestWithURL:(NSString *)url resultsArray:(NSMutableArray *)array;
To answer your question, you could pass your own completion block into your method. You can then call this block once AFNetworking has completed its HTTP request.
- (void)performHTTPRequestWithURL:(NSString *)urlString completion:(void (^)(NSArray *results, NSError *error))completion
{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"请求完成");
NSArray *results = [NSJSerialization JSONObjectWithData:operation.responseData options:NSJSONReadingAllowFragments error:nil];
if (completion)
completion(results, nil);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"请求失败: %#", error);
if (completion)
completion(nil, error);
}];
[operation start];
}
You could then call your method and pass in a block:
[self performHTTPRequestWithURL:#"http://www.example.com" completion:^(NSArray *results, NSError *error) {
if (error)
{
// handle error
}
else
{
// reload tableview or similar
}
}];
Take a look at Apple's Blocks Programming Guide for more information on blocks.
Related
I have this method here:
-(BOOL)User:(NSString *)user andPassWordExists:(NSString *)password
{
__block BOOL isLoggedIn = YES;
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
isLoggedIn = YES;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
isLoggedIn = NO;
}];
[operation start];
return isLoggedIn;
}
I get no errors, but when I put brake points on success and failure and neither one of them are getting hit. I know the URL is correct and I can see the creds being passed in, I dont know what the issue is ?
UPDATE
I have changed my code to this:
-(void)User:(NSString *)user andPassWordExists:(NSString *)password completionHandler:(void (^)(NSArray *resultsObject, NSError *error))completionHandler
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[[NSOperationQueue mainQueue] addOperation:operation];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[operation start];
}
Now I need to add this is my code:
[self fetchDataWithCompletionHandler:^(id responseObject, NSError *error) {
if (responseObject) {
}
}];
but when I do, I get this error:
Use of undeclared identifier 'self'
I guess this where I am confused because this how I would call this method from another method:
- (void)Login
{
NSString *rawString = [self.idTextField text];
NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
[self.idTextField setText:[rawString stringByTrimmingCharactersInSet:whitespace]];
BOOL *isAuthenticated = [userName User:self.idTextField.text andPassWordExists:self.passwordTextField.text];
if(isAuthenticated != 0){
[self.idTextField removeFromSuperview];
[self.passwordTextField removeFromSuperview];
[self.loginButton removeFromSuperview];
self.idTextField = nil;
self.passwordTextField = nil;
self.loginButton = nil;
[self CreateMenu];
}else{
[self CustomAlert:#"Sorry Login Failed, User and/or Passsword Incorrect"];
}
[indicatorView stopAnimating];
[indicatorView removeFromSuperview];
indicatorView = nil;
[loadingView removeFromSuperview];
loadingView = nil;
}
So inside this:
[self fetchDataWithCompletionHandler:^(id responseObject, NSError *error) {
if (responseObject) {
}
}];
would I want to either return true or false and my Login method would still work?
UPDATE
I have updated my method to this:
-(void)User:(NSString *)user andPassWordExists:(NSString *)password completionHandler:(void (^)(NSArray *resultsObject, NSError *error))completionHandler
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[[NSOperationQueue mainQueue] addOperation:operation];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completionHandler) {
completionHandler(nil, error);
}
}];
[operation start];
[self User:^(id responseObject, NSError *error) {
if (responseObject) {
return true;
}
}];
}
Two things wrong here....
I get an error at the end of my fetchDataWithCompletionHandler code:
/Users/jsuske/Documents/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:237:5: Control may reach end of non-void block
and I get this warning:
/Users/jsuske/Documents/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:233:11: Instance method '-fetchDataWithCompletionHandler:' not found (return type defaults to 'id')
Am I getting close?
This is an excellent start. I might suggest
changing the closure to simply be BOOL;
eliminate starting the operation until you're done configuring everything (and either call start or add it to a queue, but not both); and
eliminate the call to user at the end.
Thus
- (void)verifyUserid:(NSString *)user password:(NSString *)password completionHandler:(void (^)(BOOL success, NSError *error))completionHandler
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completionHandler) {
completionHandler(TRUE, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completionHandler) {
completionHandler(FALSE, error);
}
}];
[operation start];
}
I've also changed the method name to conform to Cocoa conventions (start with verb, method names always begin with lowercase letter), but that's more a stylistic matter than anything else.
Now you can call this method with something like:
[self verifyUserid:user password:password completionHandler:^(BOOL success, NSError* error) {
if (success) {
// userid/password ok; do anything related to successful login here
} else {
// not ok
}
}];
// note, we're not logged in yet (the above completion block is called later),
// so don't assume we're logged in or not here
I am new to AFNetworking and I am having a nightmare of a time with it.
So far I have created this method:
-(void)UserLogin:(NSString *)user andPassWordExists:(NSString *)password completionHandler:(void (^)(NSArray *resultsObject, NSError *error))completionHandler
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceForSession]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[[NSOperationQueue mainQueue] addOperation:operation];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completionHandler) {
completionHandler(nil, error);
}
}];
[operation start];
}
I am just super confused on what the next step would be. I have this other method inside another file and I would like to call the UserLogin method and pass in a username and password, would I want to do this like this:
- (void)Login
{
NSString *rawString = [self.idTextField text];
NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
[self.idTextField setText:[rawString stringByTrimmingCharactersInSet:whitespace]];
[userName User:self.idTextField.text andPassWordExists:self.passwordTextField.text:^(id responseObject, NSError *error) {
if (responseObject) {
//We have a user.
}
}];
}
The idea of your second snippit is correct but it's unclear what the "userName" object is. That should be an instance of the class where the first snippit's method can be located, and the name of the method you created was "UserLogin" (you put "User").
I am very new to AFNetworking. I was using ASIHTTPRequest which I found a lot easier, but heard its no good.
Now I am trying to redo everything I did with ASIHTTPRequest with AFNetworking. I created a method to login to an API:
-(BOOL)User:(NSString *)user andPassWordExists:(NSString *)password
{
__block BOOL isLoggedIn;
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"losanihomes" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceNone]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
isLoggedIn = YES;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
isLoggedIn = NO;
}];
[operation start];
return isLoggedIn;
}
Now my question is how do I store these credentials and then reuse them? in ASIHTTPRequest it would store the credentials automatically.
I am using AFNetworking to access a URL with Windows Authentication. I was using ASIHTTPRequest to login like so:
-(BOOL)User:(NSString *)user andPassWordExists:(NSString *)password
{
NSURL *url = [NSURL URLWithString:kIP];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUseSessionPersistence:YES];
[request setUseKeychainPersistence:NO];
[request setUsername:user];
[request setPassword:password];
[request setDomain:#"domain"];
[request startSynchronous];
NSError *loginError = [request error];
if(loginError == nil){
return true;
}else{
return false;
}
}
and now I am trying to do the same thing with AFNetworking and this what I came up with from this example: http://www.raywenderlich.com/forums/viewtopic.php?f=2&t=18385
-(BOOL)User:(NSString *)user andPassWordExists:(NSString *)password
{
NSURL *url = [NSURL URLWithString:kIP];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:90.0];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
[operation setCredential:[NSURLCredential credentialWithUser:[#"domain" stringByAppendingString:user]
password:password persistence:NSURLCredentialPersistenceNone]];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
return true;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
return false;
}];
[operation start];
}
but this gives me two errors:
/Users/jsuske/Documents/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:148:46: Incompatible block pointer types sending 'int (^)(AFHTTPRequestOperation *__strong, __strong id)' to parameter of type 'void (^)(AFHTTPRequestOperation *__strong, __strong id)'
/Users/jsuske/Documents/SSiPad(Device Only)ios7/SchedulingiPadApplication/Classes/LHJSonData.m:152:15: Incompatible block pointer types sending 'int (^)(AFHTTPRequestOperation *__strong, NSError *__strong)' to parameter of type 'void (^)(AFHTTPRequestOperation *__strong, NSError *__strong)'
How Can I create a method that will use user and password to login and store those credentials...this is possible with AFNetworking, it sure was with ASIHTTPRequest
I think you are misunderstanding blocks. You are returning TRUE/FALSE from the callback blocks, not your method. Those blocks will be executed at some point in the future, and their return type is void. Try running this code to see the order:
-(void)User:(NSString *)user andPassWordExists:(NSString *)password
{
// construct your request
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"In success block");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"In Fail block");
}];
[operation start];
NSLog(#"Network op started");
}
As to your design, I would not return true or false, you have to find another way to manage the success and fail scenarios, which could look something like this (I don't know enough about your overall design to give a definitive answer):
-(void)User:(NSString *)user andPassWordExists:(NSString *)password
{
// construct your request
__weak typeof(self) weakSelf = self;
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// called at somepoint in the future if the request is success full
[weakSelf handleSuccess:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// called at somepoint in the future if the request fails
[weakSelf handleFail:error];
}];
[operation start];
}
- (void)handleSuccess(id)response {
// process the response
}
- (void) handleFail:(NSError*)error {
// evaluate error
}
You see the bock declaration here: https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFHTTPRequestOperation.h
I'm trying to compile application on my iPad. I'm using AFNetworking to get list of files on my FTP. Application works on simulator, but when i start it on iPad i get (null) content of file with list. Here's code:
- (void) getListOfFiles {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"ftp://anonymous#ftphost/"]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"list"];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:YES];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response) {
NSLog(#"Success %#",operation.response);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error localizedDescription]);
}];
[operation start];
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSLog (#"%#",content);
}
So variable content = (null) only on iPad, and on Simulator everything is fine. Please help, i have lost any hope )
the AFHTTP*Operations are all asynchronous by default.
this is proper, since synchronous(blocking) calls block the main thread
you are starting the operation and take file content directly afterwards. This can't work reliably because the start call is ASYNC and only STARTS the op but doesnt wait for it
either wait for it ... which is BAD as it blocks the thread:
[operation start];
[operation waitUntilDone];
or MUCH BETTER modify getFiles to work asynchronously:
- (void) getListOfFiles {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"URL"]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"list"];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:YES];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, NSHTTPURLResponse *response) {
NSLog(#"Success %#",operation.response);
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSLog (#"%#",content);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error localizedDescription]);
}];
[operation start];
}
Ok, problem solved. I can't write to bundle directory, so i nee to use next code instead:
NSArray *paths = NSSearchPathForDirectoiesDomain(NSDocumentDirectory,NSCachesDirectory,YES);
NSString *path = [[paths objectAtIndex:0] stingByAppendingPathComponent:#"list"];