i need fetch data from server in background mode ios. but when i run app in background, didReceiveData method never called. in foreground it work fine.
when app run in background then app request data from applicationDidEnterBackground method. i cannot why didReceiveData method not call.
please help me!
i use this way may help you..
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
[dic setObject:string forKey:#"data"];
[dic setObject:ADDD_DATA_UPGRADE forKey:#"cn"];
[dic setObject:#"true" forKey:#"mode"];
[self login:dic];
dispatch_async(dispatch_get_main_queue(), ^(void)
{
});
});
}
-(void)login:(NSDictionary*)dic
{
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:UPDATION_ON_SERVER_TIME];
SAAPIClient *manager = [SAAPIClient sharedClient];
[[manager responseSerializer] setAcceptableContentTypes:[NSSet setWithObject:#"text/html"]];
[manager postPath:#"" parameters:dic success:^(AFHTTPRequestOperation *operation, id responseObject)
{
if([[responseObject objectForKey:#"status"] boolValue])
{
NSLog(#"Success: %# ***** %#", operation.responseString, responseObject);
[self parseResponseFromJson:responseObject];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:UPDATION_ON_SERVER_TIME];
NSLog(#"Success: %# ***** %#", operation.responseString, error);
}];
Related
I am pretty new to iOS development, and entered a position where I need to maintain a large existing project in obj-c.
I have a sidebar-menu which is a webview. When program starts it makes a url request to check whether there is a newer version of the menu, and in that case retrieves the latest version.
Right now when the app runs for the first time it shows the old version, and from the second time and on it shows the current version.
When I tried debugging I've seen that the method that compares between local and remote version gets an empty value for the remote version. As far as I can understand it, the url request for the latest version is async, and therefore the code continues to execute before the request returns the current version.
Following an answer from StackOverflow, I've tried to call the getDataConfiguration method from within viewDidLoad instead of from AppDelegate, but that didn't work.
Would appreciate any help!
relevant code:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { .
...
[DataManager getDataConfiguration:^(DataConfiguration *dataConfiguration, NSError *error) {
[AppData sharedInstance].dataConfiguration=dataConfiguration;
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:dataConfiguration];
[standardDefaults setObject:encodedObject forKey:DATA_KEY];
[standardDefaults synchronize];
}];
[DataManager getProductMap:^(ProductsArray *products, NSError *error) {
[AppData sharedInstance].productsArray=products;
}];
DataManager.m
+(void)getDataConfiguration:(void (^)(DataConfiguration * dataConfiguration, NSError *error))completion
{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:[Configuration sharedInstance].infoJSONURL parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
DataConfiguration * dataConfiguration = [DataConfiguration modelObjectWithDictionary:responseObject];
completion(dataConfiguration,nil);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
+(void)updateHtmlFiles:(void (^)(NSError *error))completion{
float upToDateMenuVersion = [[AppData sharedInstance] dataConfiguration].general.menuVersion;
float localMenuVersion = [self getLocalMenuVersion];
if(upToDateMenuVersion != localMenuVersion){
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSString *url = [NSString stringWithFormat:#"%#?v=%f", [Configuration sharedInstance].menuHTMLFileURL, [[NSDate new] timeIntervalSince1970]];
[manager GET:url parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *htmlFiles = [userDefaults dictionaryForKey:#"HTML_FILES"];
NSMutableDictionary *mutableHtmlFiles = [NSMutableDictionary new];
NSString *myString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
[mutableHtmlFiles setValue:myString forKey:#"MENU"];
[userDefaults setObject:mutableHtmlFiles forKey:#"HTML_FILES"];
[self setLocalMenuVersion:upToDateMenuVersion];
completion(nil);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *htmlFiles = [userDefaults dictionaryForKey:#"HTML_FILES"];
if(htmlFiles == nil){
NSString *menuFile = [[NSBundle mainBundle] pathForResource:#"menu" ofType:#"html"];
htmlFiles = #{#"MENU":[NSString stringWithContentsOfFile:menuFile encoding:NSUTF8StringEncoding error:nil]};
[userDefaults setObject:htmlFiles forKey:#"HTML_FILES"];
}
NSLog(#"Error: %#", error);
}];
}
}
+(void) setLocalMenuVersion: (float) version{
[[NSUserDefaults standardUserDefaults] setFloat:version forKey:#"menuVersion"];
}
+(float) getLocalMenuVersion {
return [[NSUserDefaults standardUserDefaults] floatForKey:#"menuVersion"];
}
Menu.m
- (void)viewDidLoad {
[super viewDidLoad];
_firstLoad = YES;
...
[self initWebView];
}
-(void) initWebView {
if(_webView == nil){
_webView = [[WKWebView alloc] initWithFrame:_webViewPlaceholder.frame];
[_webView.scrollView setZoomScale:3 animated:YES];
_webView.navigationDelegate = self;
_webView.UIDelegate = self;
NSString *javaScriptText = #"document.body.style.zoom = 3;";
[_webView evaluateJavaScript:javaScriptText completionHandler:nil];
[self.view addSubview:_webView];
_webView.scrollView.bounces = NO;
[self updateHtml];
[AppData updateHeaderAndMenu:^(NSError *error){
[self updateHtml];
}];
}
}
- (void)viewDidAppear:(BOOL)animated{
_webView.frame = CGRectMake(_webViewPlaceholder.frame.origin.x,_webViewPlaceholder.frame.origin.y, _webViewPlaceholder.frame.size.width, _webViewPlaceholder.frame.size.height);
}
-(void)updateHtml{
NSDictionary *htmlFiles = [AppData getHeaderAndMenu];
NSString *menu = [htmlFiles objectForKey:#"MENU"];
[_webView loadHTMLString:menu baseURL: [[NSBundle mainBundle] bundleURL]];
}
AppData.m
+(void)updateHeaderAndMenu:(void (^)(NSError *error))completion{
[DataManager updateHtmlFiles:completion];
}
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
...
[AppData updateHeaderAndMenu:^(NSError *error){ [self loadHeader]; }];
_firstLoad = YES;
...
The updateHeaderAndMenu method has a completion block which is called after the async operation completes without an error.
I'm assuming ViewController.m holds a reference to Menu?
If that is the case, viewDidLoad calls the updateHeaderAndMenu method and will execute the completionBlock (if there is no error). In this block I can already see that a method is called loadHeader. You could call [self.menu updateHtml]; there and this would probably work.
...
[AppData updateHeaderAndMenu:^(NSError *error){
[self loadHeader];
// [self.menu updateHtml];
}];
_firstLoad = YES;
...
I'm doing some guess work here but I think this would update your webview after the DataManager completes the http request.
Edit:
As to the order of execution. Here is a breakdown:
This is the method definition in AppData
+(void)updateHeaderAndMenu:(void (^)(NSError *error))completion{
[DataManager updateHtmlFiles:completion];
}
You can see completion (which is a block parameter) is passed on to the updateHtmlFiles method in DataManager:
+(void)updateHtmlFiles:(void (^)(NSError *error))completion{
...
completion(nil);
...
}
Eventually the completion parameter (which is a block) is called when the async http request completes. You can look at blocks as kind of inline methods which can be passed as a parameter. Google working with blocks ios to see the official Apple documentation for this.
So the order of execution is:
Menu calls updateHeaderAndMenu in AppData
which calls updateHtmlFiles in DataManager and passes on completion
http request completes and calls completion.
the content of the block is executed all the way back in Menu which is:
{
[self loadHeader];
// [self.menu updateHtml];
}
loadHeader is executed ...
If you want to get a better overview of what is called when, you can use breakpoints inside your code.
Is there any way that I can print/NSLOG the string or the array that is being returned by the API in this following login code of mine:
-(void)loginToAPI:(NSString *)email password:(NSString *)password {
NSString *controller = #"login";
NSString *action = #"authenticate";
NSDictionary *params = #{#"username": email,
#"userpass": password,
#"controller": controller,
#"action": action,
#"app_id": APP_ID,
#"app_key": APP_KEY};
NSLog(#"params %#", params);
if ([self isNetworkAvailable]) {
AFHTTPRequestOperationManager *client = [AFHTTPRequestOperationManager manager];
client.responseSerializer.acceptableContentTypes = [client.responseSerializer.acceptableContentTypes setByAddingObject:#"text/html"];
[client POST:[[Config sharedInstance] getAPIURL]
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary *jsonObject= responseObject;
NSString *status = [jsonObject objectForKey:#"status"];
NSLog(#"Request Successful, response '%#'", jsonObject);
if ([status isEqualToString:#"success"]) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *data = [jsonObject objectForKey:#"data"];
NSDictionary *userDict = [data objectForKey:#"user"];
NSDictionary *djsaDict = [data objectForKey:#"djsa"];
User *currentUser = [[User alloc] initWithProperties:userDict];
[currentUser save];
DJSA_Cutomer *djsa = [[DJSA_Cutomer alloc] initWithProperties:djsaDict];
NSData *userData = [NSKeyedArchiver archivedDataWithRootObject:currentUser];
NSData *djsaData = [NSKeyedArchiver archivedDataWithRootObject:djsa];
[userDefaults setObject:userData forKey:#"currentUser"];
[userDefaults setObject:djsaData forKey:#"djsa_customer"];
[userDefaults synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName: #"LOGIN_SUCCESS" object:currentUser userInfo:nil];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName: #"LOGIN_FAILED" object:nil userInfo:nil];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Request Failed with Error - loginToAPI: %#, %#", error, error.userInfo);
[[NSNotificationCenter defaultCenter] postNotificationName: #"SERVER_ERROR" object:nil userInfo:nil];
}];
}
}
It is always failing and going to the part "Request Failed with Error - loginToAPI". Is it possible to see the actual values returned by the server so I can diagnose the problem?
Thanks!
I had the same problem. The solution to it in my case was
client.responseSerializer = [AFHTTPResponseSerializer serializer];
I did that but I was still getting same error. The problem was that I was setting client.requestSerializer instead of client.responseSerializer. Small mistakes but takes lot of time.
You can get the status code off the response property from the operation object which should give you some idea of what went wrong:
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Request Failed with Error - loginToAPI: %#, %#", error, error.userInfo);
NSLog(#"%#", [NSHTTPURLResponse localizedStringForStatusCode:operation.response.statusCode]);
[[NSNotificationCenter defaultCenter] postNotificationName: #"SERVER_ERROR" object:nil userInfo:nil];
}];
Otherwise, it's best to use a tool like Charles web proxy to inspect the actual request and response. It has a free trial which should be sufficient to do what you need, and is relatively easy to set up.
Replace this line:
client.responseSerializer.acceptableContentTypes = [client.responseSerializer.acceptableContentTypes setByAddingObject:#"text/html"];
with this:
[client.securityPolicy setValidatesDomainName:NO];
[client.securityPolicy setAllowInvalidCertificates:YES];
client.responseSerializer = [AFHTTPResponseSerializer serializer];
I'm building an app using the Drupal SDK - everything is great. I have my user log in and successfully upload a photo. However once it's uploaded, I want the image located in the field to display in a UIImageView. I'm using SDImageWeb to get this done, I'm just not sure how to write it? Incoming newb question...
Here is the code that successfully uploads the image to Drupal field called field_profile_photo:
viewcontroller.m
[DIOSFile fileSave:file success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"File uploaded!");
[file setObject:[responseObject objectForKey:#"fid"] forKey:#"fid"];
[file removeObjectForKey:#"file"];
fid = [responseObject objectForKey:#"fid"];
NSLog(#"%#",responseObject);
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject: [NSString stringWithFormat:#"%#", fid] forKey:#"fid"];
NSLog(#"%#", fid);
NSDictionary *fidLangDict = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:dict] forKey:#"und"];
[userData setObject:#"1" forKey:#"uid"];
[userData setObject:fidLangDict forKey:#"field_profile_photo"];
[DIOSUser
userUpdate:userData
success:^(AFHTTPRequestOperation *op, id response) { /* Handle successful operation here */
NSLog(#"User data updated!");
}
failure:^(AFHTTPRequestOperation *op, NSError *err) { /* Handle operation failire here */
NSLog(#"User data failed to update!");
}
];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failed to upload file!");
}];
}
When I refresh this view, I now want the photo uploaded displayed in an imageview. I assume it would look something like this? does anyone know how I can make secondLink the URL generated at field_profile_photo? I hope I'm asking this correctly. Help.
NSString *secondLink = [self.photoData setObject:imageView forKey:#"field_profile_photo"];
[self.imageView sd_setImageWithURL:[NSURL URLWithString:secondLink]];
I have it written like this in my table cell and it works (in another project), however I'm curious as to how I write it when my imageview is just in a view?
NSString *secondLink = [[self.descripData objectAtIndex:indexPath.row] objectForKey:#"field_profile_photo"];
[cell.itemPhoto sd_setImageWithURL:[NSURL URLWithString:secondLink]];
Can anyone help me with refreshing token on saleforce.
NSDictionary *fields = #{#"grant_type" : #"refresh_token",
#"refresh_token": oauth[#"refreshToken"],
#"client_id": coordinator.credentials.clientId,
#"format": #"json"
};
SFRestRequest *request = [SFRestRequest requestWithMethod:SFRestMethodPOST path:[NSString stringWithFormat:#"%#/services/oauth2/token",[SFAccountManager loginHost]] queryParams:fields];
request.endpoint = [NSString stringWithFormat:#"%#/services/oauth2/authorize", [SFAccountManager loginHost]];
[[SFRestAPI sharedInstance] sendRESTRequest:request failBlock:^(NSError *e) {
NSLog(#"Error %#", e);
} completeBlock:^(NSDictionary* dict) {
NSLog(#"dict");
}];
This is what i am trying to do, but i am getting the following error:
Error Domain=NSURLErrorDomain Code=404 "The operation couldn’t be completed. (NSURLErrorDomain error 404.)" UserInfo=0x7993c670 {Transfer-Encoding=Identity, Content-Type=text/html;charset=UTF-8, Date=Thu, 13 Nov 2014 15:23:36 GMT}
I am not sure what is going wrong.
FYI: Understanding the OAuth Refresh Token Process
This is the request:
endpoint: test.salesforce.com/services/oauth2/authorize
method: POST
path: test.salesforce.com/services/oauth2/token
queryParams: {"grant_type":"refresh_token","refresh_token":"5AsdfdfssdBuiV6yFNukUqDSMUH1tnk39L.x0GFcdqdMhpmQUTu.wv1BNndfglD3SxEZIFUev4i8T2KLkcCOK","format":"json","client_id":"3MVG9dPGzsddfssfdsOQG3p9KW.2hh3Bh5BeEsdfsRlDhxcybG.YRTQC0C0XayvzEw.5.umftLwJpYjYVDE6xgSCgXo"}
I stopped using Salesforce and NSURL Kit instead i use AFHTTPREQUEST
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:#"password" forKey:#"grant_type"];
[dict setObject:#"xxx" forKey:#"client_id"];
[dict setObject:#"xxx" forKey:#"client_secret"];
[dict setObject:#"your user" forKey:#"username"];
[dict setObject:#"password" forKey:#"password"];
[dict setObject:#"json" forKey:#"format"];
[manager POST:#"https://test.salesforce.com/services/oauth2/token" parameters:dict success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
}];
first I'd like to say that the issue I am having is in regards to how my own code is set, not IOS or AFNetworking.
I am going to show 1 example where a view is not refreshing immediately even though the updated JSON response object from the server is being recieved.
There are 2 views being used in the first view the button below takes the user to a second view where he can upload an image that will then be displayed in the first view.
- (IBAction)editImage:(id)sender {
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
TDUserProfileViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"TDUserProfileImageEditViewController"];
[self.navigationController pushViewController:vc animated:YES];
}
Below is the code that uploads the new data to the server and then gets new data regarding the images's address on the server.
- (IBAction)dataSubmitToServer{
// NSLog(#"photo: %#", self.photoData);
NSString * userID = [[NSUserDefaults standardUserDefaults] objectForKey:USERID];
//make the call to the web API
NSString* command = #"setProfilePhoto";
self.params =[NSMutableDictionary dictionaryWithObjectsAndKeys:command, #"command", userID, #"userid", nil];
//////////////////
NSLog(#"%#", self.params);
self.photoName = #"newname.png";
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager POST:BaseURLString parameters:self.params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:UIImageJPEGRepresentation(self.userPhoto.image, 1)
name:#"image"
fileName:self.photoName
mimeType:#"image/jpeg"];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error localizedDescription]);
[TDPublicFunctions showDefaultAlert:#"Error" body:[error description]];
}];
[self getNewProfileData];
[[NSUserDefaults standardUserDefaults] synchronize];
}
-(void)getNewProfileData{
/*start getting new data for profile pic and status message*/
NSString* command = #"displayProfileData";
NSString * userID = [[NSUserDefaults standardUserDefaults] objectForKey:USERID];
NSMutableDictionary* params =[NSMutableDictionary dictionaryWithObjectsAndKeys:command, #"command", userID,#"userid", nil];
NSLog( #"%#", params);
[SVProgressHUD showWithStatus:#"Loading..." maskType:SVProgressHUDMaskTypeBlack];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:BaseURLString parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
[SVProgressHUD dismiss];
if([responseObject objectForKey:#"error"])
{
[[[UIAlertView alloc] initWithTitle:#"Data Retrieval Error" message:[responseObject description] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
}
else
{
NSString * profilePhoto = [[[responseObject objectForKey:#"result"] objectAtIndex:0] objectForKey:#"profile_picture"];
NSString * status = [[[responseObject objectForKey:#"result"] objectAtIndex:0] objectForKey:#"status"];
NSString * statusMsg = [[[responseObject objectForKey:#"result"] objectAtIndex:0] objectForKey:#"status_message"];
if(profilePhoto && ![profilePhoto isKindOfClass:[NSNull class]])
[[NSUserDefaults standardUserDefaults] setObject:profilePhoto forKey:PROFILE_PHOTO];
if(status && ![status isKindOfClass:[NSNull class]])
[[NSUserDefaults standardUserDefaults] setObject:status forKey:STATUS];
if(statusMsg && ![statusMsg isKindOfClass:[NSNull class]])
[[NSUserDefaults standardUserDefaults] setObject:statusMsg forKey:STATUS_MESSAGE];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
//end of getting data for status message and profile pic
}
Now once this above process is over the HUD is supposed to stop and when the back button on the view is hit there should be a newly uploaded image in the first view. The code below is the last of many things that I have tried in order to get the view with the image to refresh:
-(void) viewWillAppear:(BOOL)animated
{
NSString * profPic = [[NSUserDefaults standardUserDefaults] objectForKey:PROFILE_PHOTO];
NSLog(#"profilepic: %#", profPic);
// NSString* status = [[NSUserDefaults standardUserDefaults] objectForKey:STATUS];
NSString * statusMsg = [[NSUserDefaults standardUserDefaults] objectForKey:STATUS_MESSAGE];
if(profPic)
[photo loadIconForProduct:profPic];
if(statusMsg)
statusMsgView.text = statusMsg;
//self.view=nil;
//[self viewDidLoad];
[self getFriendsList];
[photo setNeedsDisplay];
[self.view setNeedsDisplay];
}
After an image is uploaded it may work the first time an image is uploaded but it does not work if the user were to press the editImage button a second time and upload another image.
What is it that is being done wrong? Let me know if there is anything else that I can supply.
Thanks.
As I can see here, you are getting a URL back as the result of the profilePhoto. So you might as well use the AFNetworking function:
Im presuming photo is a UIImageView.
[photo setImageWithURL:[NSURL URLWithString:profPic] placeholderImage:[UIImage imageNamed:#"somePlaceholderImage"]];