I'm trying to implement background fetch as well as refresh in iOS 10.
I'm using XML parsing to parse the data and then storing it in a file in the document's directory. For parsing XML I'm using a custom class (XMLParser) that confirms the NSXMLParserDelegate protocol.
The background fetch works fine. But I'm having problems in displaying the refreshed data, both when I click on the refresh button as well as in viewDidLoad.
I'm calling the refreshData method in viewDidLoad.
Here's how far I've gotten.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//--Set background fetch--//
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
}
...
#pragma mark Background data fetch methods
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
NSDate *fetchStart = [NSDate date];
ArtsViewController *artsViewController = (ArtsViewController *)self.window.rootViewController;
[artsViewController fetchNewDataWithCompletionHandler:^(UIBackgroundFetchResult result) {
completionHandler(result);
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
NSLog(#"Background Fetch Duration: %f seconds", timeElapsed);
}];
}
ArtsViewController.h
#interface ArtsViewController : UIViewController <UIPageViewControllerDataSource>
#property BOOL newsAvailable;
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; // No problems here
#end
ArtsViewcontroller.m
#interface ArtsViewController ()
#property (nonatomic, strong) NSArray *arrNewsData;
-(void)refreshData;
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray;
#end
...
#implementation ArtsViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self refreshData];
//--Load the file that saves news--//
[self loadNews];
if (_newsAvailable == YES)
{
[self setupPageViewController];
}
else
{
[self showNoNewsMessage];
}
}
...
#pragma mark Data Fetch methods
-(void)refreshData{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
[self performNewFetchedDataActionsWithDataArray:dataArray];
}
else{
NSLog(#"%#", [error localizedDescription]);
}
}];
}
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray{
// 1. Initialize the arrNewsData array with the parsed data array.
if (self.arrNewsData != nil) {
self.arrNewsData = nil;
}
self.arrNewsData = [[NSArray alloc] initWithArray:dataArray];
// 2. Write the file and reload the view.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * docDirectory = [paths objectAtIndex:0];
NSString * newsFilePath = [NSString stringWithFormat:#"%#",[docDirectory stringByAppendingPathComponent:#"arts2"]]; // NewsFile
if (![self.arrNewsData writeToFile:newsFilePath atomically:YES]) {
_newsAvailable = NO;
NSLog(#"Couldn't save data.");
}
else
{
_newsAvailable = YES;
NSLog(#"Saved data.");
[self viewWillAppear:YES];
}
}
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
NSDictionary *latestDataDict = [dataArray objectAtIndex:0];
NSString *latestTitle = [latestDataDict objectForKey:#"title"];
NSDictionary *existingDataDict = [self.arrNewsData objectAtIndex:0];
NSString *existingTitle = [existingDataDict objectForKey:#"title"];
if ([latestTitle isEqualToString:existingTitle]) {
completionHandler(UIBackgroundFetchResultNoData);
NSLog(#"No new data found.");
}
else{
[self performNewFetchedDataActionsWithDataArray:dataArray];
completionHandler(UIBackgroundFetchResultNewData);
NSLog(#"New data was fetched.");
}
}
else{
completionHandler(UIBackgroundFetchResultFailed);
NSLog(#"Failed to fetch new data.");
}
}];
}
...
#pragma mark IBActions
- (IBAction)reloadNews:(UIBarButtonItem *)sender
{
[self viewDidLoad];
}
I've debugged the application and found that after viewDidLoad
completes execution, the data file is written but the view isn't
updated. I've also tried calling the refreshData method in the main
thread, but there's no change.
after viewDidLoad is complete the showNoNewNews method is called.
I'm suspecting that my logic isn't wrong but implementation is. Threads at play here..
Any help would be appreciated.
Update:
Hope this helps those with similar problems...
I moved the logic of viewDidLoad to a different method, called the method for the first time in viewDidLoad and again in refreshData, after
[self performNewFetchedDataActionsWithDataArray:dataArray];
I am new to iOS. I have a UITextfield and a Keyword Search Button. When ever I want to search a keyword from a service and press enter. Tt should display the related searched keyword from a service. Please help me to fix this issue? TIA!
- (IBAction)KeywordSearchClicked:(id)sender {
NSMutableDictionary *dict=[[NSMutableDictionary alloc] init];
[self KeywordcallSignupProfileService:dict];
}
-(void)KeywordcallSignupProfileService:(NSMutableDictionary *)dict
{
[SVProgressHUD showWithStatus:#"" maskType:SVProgressHUDMaskTypeBlack]; // Progress
NSString * post = [[NSString alloc]initWithFormat:#"userId=%#&key_word%#",UserId,[dict objectForKey:#"key_word"]];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://www.amarbiyashaadi.com/service/amarbiya-service.svc/userKeywordSearch/"]];
RBConnect = [[RBConnection alloc]init];
RBConnect.delegate = self;
[RBConnect postRequestForUrl:url postBody:post];
}
#pragma mark - MRConnection Delegate Methods
- (void)jsonData:(NSDictionary *)jsonDict
{
[SVProgressHUD dismiss];
NSMutableArray *jsonArr;
NSMutableDictionary *userDict,*dict;
NSArray *arr=[jsonDict allKeys];
jsonArr=[jsonDict objectForKey:#"DataTable"];
if (jsonArr.count>0) {
// Save credentials in user defaults
matchesProfileArr=[jsonArr mutableCopy];
DisplayTableViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"DisplayTableViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
else
{
NSString *error=#"Somthing Went Wrong";
[SVProgressHUD showErrorWithStatus:error];
}
}
I cannot resolve this issue and I have no idea why my UITableView duplicates rows whenever I click on the segment control. I want to refresh the table view with new data when segment control changes. I have tried many things and googled it but could not find any solution. I would appreciated if someone can help me please. I am still learning xcode and a lot to learn. here is my code -
#import "citsViewController.h"
#import "citsParseOperation.h"
#import "citsFuelFinder.h"
#import "citsTableViewCell.h"
#import "citsAboutViewController.h"
#import "MBProgressHUD.h"
#import <CoreLocation/CoreLocation.h>
// this framework is imported so we can use the kCFURLErrorNotConnectedToInternet error code
#import <CFNetwork/CFNetwork.h>
#import <MapKit/MapKit.h>
#interface citsViewController ()
{
CLLocationManager *locationManager;
CLGeocoder *geocoder;
CLPlacemark *placemark;
NSString *currentLoc;
int productName;
}
#property (nonatomic) NSMutableArray *earthquakeList;
#property (nonatomic) citsFuelFinder *currentEarthquakeObject;
#property (nonatomic, weak) IBOutlet UILabel *locationLabel;
// queue that manages our NSOperation for parsing earthquake data
#property (nonatomic) NSOperationQueue *parseQueue;
#end
#pragma mark -
#implementation citsViewController
#synthesize nomatchesView;
#synthesize footerLabel;
#synthesize headerLabel;
#synthesize fuelType;
#synthesize bannerIsVisible;
- (void)viewDidLoad {
[super viewDidLoad];
//refresh the tableview
UIRefreshControl *refreshControl=[[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
self.refreshControl=refreshControl;
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
[infoButton addTarget:self action:#selector(aboutUs:) forControlEvents:UIControlEventTouchUpInside];
//add info button in the navigation controller
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton] ;
//initialize location manager
locationManager=[[CLLocationManager alloc] init];
//to get location
geocoder=[[CLGeocoder alloc] init];
//show network activity
[UIApplication sharedApplication].networkActivityIndicatorVisible = TRUE;
locationManager.delegate=self;
locationManager.desiredAccuracy=kCLLocationAccuracyBest;
//call the location manager update function
[locationManager startUpdatingLocation];
}
-(void)refresh:(id)sender {
//update table data
[locationManager startUpdatingLocation];
[self.refreshControl endRefreshing];
[self.tableView reloadData];
}
- (void)dealloc {
// we are no longer interested in these notifications:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kAddEarthquakesNotificationName
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kEarthquakesErrorNotificationName
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSCurrentLocaleDidChangeNotification
object:nil];
}
/**
Handle errors in the download by showing an alert to the user. This is a very simple way of handling the error, partly because this application does not have any offline functionality for the user. Most real applications should handle the error in a less obtrusive way and provide offline functionality to the user.
*/
- (void)handleError:(NSError *)error {
NSString *errorMessage = [error localizedDescription];
NSString *alertTitle = NSLocalizedString(#"Error", #"Title for alert displayed when download or parse error occurs.");
NSString *okTitle = NSLocalizedString(#"OK ", #"OK Title for alert displayed when download or parse error occurs.");
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:alertTitle message:errorMessage delegate:nil cancelButtonTitle:okTitle otherButtonTitles:nil];
[alertView show];
}
/**
Our NSNotification callback from the running NSOperation to add the earthquakes
*/
- (void)addEarthquakes:(NSNotification *)notif {
assert([NSThread isMainThread]);
[self addEarthquakesToList:[[notif userInfo] valueForKey:kEarthquakeResultsKey]];
}
/**
Our NSNotification callback from the running NSOperation when a parsing error has occurred
*/
- (void)earthquakesError:(NSNotification *)notif {
assert([NSThread isMainThread]);
[self handleError:[[notif userInfo] valueForKey:kEarthquakesMessageErrorKey]];
}
/**
The NSOperation "ParseOperation" calls addEarthquakes: via NSNotification, on the main thread which in turn calls this method, with batches of parsed objects. The batch size is set via the kSizeOfFuelPumpBatch constant.
*/
- (void)addEarthquakesToList:(NSArray *)earthquakes {
NSInteger startingRow = [self.earthquakeList count];
NSInteger earthquakeCount = [earthquakes count];
NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:earthquakeCount];
for (NSInteger row = startingRow; row < (startingRow+earthquakeCount); row++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
[indexPaths addObject:indexPath];
}
NSLog(#"record count %d",earthquakeCount);
[self.earthquakeList addObjectsFromArray:earthquakes];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
//[self.tableView reloadData];
}
#pragma mark - UITableViewDelegate
// The number of rows is equal to the number of earthquakes in the array.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.earthquakeList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kEarthquakeCellID = #"EarthquakeCellID";
citsTableViewCell *cell = (citsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:kEarthquakeCellID];
if(cell==nil)
{
cell=[[citsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kEarthquakeCellID];
}
// Get the specific earthquake for this row.
citsFuelFinder *earthquake = (self.earthquakeList)[indexPath.row];
[cell configureWithEarthquake:earthquake];
return cell;
}
/**
* When the user taps a row in the table, display the USGS web page that displays details of the earthquake they selected.
*/
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *buttonTitle = NSLocalizedString(#"Cancel", #"Cancel");
//NSString *buttonTitle1 = NSLocalizedString(#"Show USGS Site in Safari", #"Show USGS Site in Safari");
NSString *buttonTitle2 = NSLocalizedString(#"Show Location in Maps", #"Show Location in Maps");
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:buttonTitle destructiveButtonTitle:nil
otherButtonTitles: buttonTitle2, nil];
[sheet showInView:self.view];
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, 500)];
headerLabel=[[UILabel alloc] initWithFrame:CGRectMake(10, 1.0, 300, 25)];
headerLabel.numberOfLines=0;
fuelType=[[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"ULP", #"PULP",#"Diesel",#"LPG", nil]];
fuelType.frame = CGRectMake(10, 30, 300,25);
//set first segment selected
[fuelType setSelectedSegmentIndex:0];
[fuelType addTarget:self action:#selector(segmentedControlHasChangedValue) forControlEvents:UIControlEventValueChanged];
headerLabel.font=[UIFont systemFontOfSize:10.0];
[headerView insertSubview:fuelType aboveSubview:headerLabel];
[headerView addSubview:headerLabel];
[headerView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"footer.gif"]]];
return headerView;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 60;
}
#pragma mark -
/**
* Called when the user selects an option in the sheet. The sheet will automatically be dismissed.
*/
- (void)actionSheet:(UIActionSheet *)actionSheet willDismissWithButtonIndex:(NSInteger)buttonIndex {
NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
citsFuelFinder *earthquake = (citsFuelFinder *)(self.earthquakeList)[selectedIndexPath.row];
switch (buttonIndex) {
case 0: {
// open the earthquake info in Maps
// create a map region pointing to the earthquake location
CLLocationCoordinate2D location = (CLLocationCoordinate2D) { earthquake.latitude, earthquake.longitude };
NSValue *locationValue = [NSValue valueWithMKCoordinate:location];
MKCoordinateSpan span = (MKCoordinateSpan) { 2.0, 2.0 };
NSValue *spanValue = [NSValue valueWithMKCoordinateSpan:span];
NSDictionary *launchOptions = #{ MKLaunchOptionsMapTypeKey : #(MKMapTypeStandard),
MKLaunchOptionsMapCenterKey : locationValue,
MKLaunchOptionsMapSpanKey : spanValue,
MKLaunchOptionsShowsTrafficKey : #(NO),
MKLaunchOptionsDirectionsModeDriving : #(NO) };
// make sure the map item has a pin placed on it with the title as the earthquake location
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:location addressDictionary:nil];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
[mapItem setName:earthquake.location];
[mapItem openInMapsWithLaunchOptions:launchOptions];
break;
}
}
[self.tableView deselectRowAtIndexPath:selectedIndexPath animated:YES];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError: %#", error);
UIAlertView *errorAlert = [[UIAlertView alloc]
initWithTitle:#"Error" message:#"Failed to Get Your Location" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
// Regiser for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
HUD.labelText = #"Loading";
HUD.detailsLabelText = #"updating data";
HUD.square = YES;
self.earthquakeList = [NSMutableArray array];
self.currentEarthquakeObject=nil;
if([self.earthquakeList count] >0)
{
[self.earthquakeList removeAllObjects];
[self.tableView reloadData];
}
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(addEarthquakesToList:) onTarget:self withObject:nil animated:YES];
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
NSString *latitude=[NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
NSString *longitude=[NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
citsTableViewCell *tcell = [[citsTableViewCell alloc] init];
//set the latitude and longitude
tcell.lon =[longitude doubleValue];
tcell.lat = [latitude doubleValue];
NSLog(#"Lat:%#, Lon:%#", latitude,latitude);
}
//stop updating location
[locationManager stopUpdatingLocation];
//reverse geocoding
NSLog(#"Resolving the address");
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
placemark = [placemarks lastObject];
currentLoc=[NSString stringWithFormat:#"%#",placemark.locality];
if(currentLoc == NULL)
{
currentLoc=#"N/A";
}
NSLog(#"%#",currentLoc);
//add text to headertext
NSDate *currDate=[NSDate date];
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/yy HH:mm:ss"];
NSString *dateString=[dateFormatter stringFromDate:currDate];
NSString *loc=[NSString stringWithFormat:#"Your location: %#, Updated on: %#", currentLoc, dateString ];
[dateFormatter setDateFormat:#"EEEE, dd/MM/yyyy"];
NSString *headerDate=[dateFormatter stringFromDate:currDate];
headerLabel.text=[NSString stringWithFormat:#"Prices for: %#\n%#", headerDate,loc];
//currentLoc=[NSString stringWithFormat:#"%#", placemark.locality];
currentLoc=#"Mirrabooka";
if(productName==0)
{
productName=1;
}
NSString *prdStr=[[NSString alloc] initWithFormat:#"%d", productName];
NSString *str =[currentLoc stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSString *feedURLString =[[NSString alloc] initWithFormat: #"http://www.fuelwatch.wa.gov.au/fuelwatch/fuelWatchRSS?Product=%#&Suburb=%#", prdStr,str ];
NSURLRequest *earthquakeURLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:feedURLString]];
NSLog(#"%#",feedURLString);
// send the async request (note that the completion block will be called on the main thread)
//
// note: using the block-based "sendAsynchronousRequest" is preferred, and useful for
// small data transfers that are likely to succeed. If you doing large data transfers,
// consider using the NSURLConnectionDelegate-based APIs.
//
[NSURLConnection sendAsynchronousRequest:earthquakeURLRequest queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// back on the main thread, check for errors, if no errors start the parsing
//
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// here we check for any returned NSError from the server, "and" we also check for any http response errors
if (error != nil) {
[self handleError:error];
}
else {
// check for any response errors
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if ((([httpResponse statusCode]/100) == 2) ) {
// Update the UI and start parsing the data,
// Spawn an NSOperation to parse the earthquake data so that the UI is not
// blocked while the application parses the XML data.
//
citsParseOperation *parseOperation = [[citsParseOperation alloc] initWithData:data];
[self.parseQueue addOperation:parseOperation];
}
else {
NSString *errorString =
NSLocalizedString(#"HTTP Error", #"Error message displayed when receving a connection error.");
NSDictionary *userInfo = #{NSLocalizedDescriptionKey : errorString};
NSError *reportError = [NSError errorWithDomain:#"HTTP"
code:[httpResponse statusCode]
userInfo:userInfo];
[self handleError:reportError];
}
}
}];
// Start the status bar network activity indicator.
// We'll turn it off when the connection finishes or experiences an error.
//
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
self.parseQueue = [NSOperationQueue new];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(addEarthquakes:)
name:kAddEarthquakesNotificationName object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(earthquakesError:)
name:kEarthquakesErrorNotificationName object:nil];
} else {
NSLog(#"%#", error.debugDescription);
}
} ];
NSLog(#"%d",[self.earthquakeList count]);
}
-(void)clearData{
[self.tableView beginUpdates];
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
for (int i = [self.tableView numberOfRowsInSection:0] - 1; i >= 0; i--)
{
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:0]];
NSLog(#"Deleted: %d",i);
}
[self.tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationFade];
[self.earthquakeList removeAllObjects];
[self.tableView endUpdates];
//[self.tableView reloadData] ;
}
-(void)segmentedControlHasChangedValue{
int product;
product=fuelType.selectedSegmentIndex;
switch (product) {
case 0:
productName=1;
[locationManager startUpdatingLocation];
[self clearData];
break;
case 1:
productName=2;
[self clearData];
[locationManager startUpdatingLocation];
break;
case 2:
productName=4;
[self clearData];
[locationManager startUpdatingLocation];
break;
case 3:
prod![enter image description here][1]uctName=5;
[self clearData];
[locationManager startUpdatingLocation];
break;
}
NSLog(#"%d",productName);
return;
}
#end
Try removing all previous object from the self.earthquakeList before adding new 1. use [self.earthquakeList removeAllObjects]
Remove this Line,
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
And uncomment,
[self.tableView reloadData];
You are Already updating the data set, So you just need to reload the tableView instead of explicitly insert row.
I am using GMTOAuth2 in an application with one viewController and it works fine, Then I add same code to another application that contains more viewControllers, It loads the webView with textfields for username and password but if I click on anything in this view it crashes with Thread 1: EXC_BAD_ACCESS (code=2, address=0xbf7ffffc) with void SendDelegateMessage(NSInvocation *): delegate (webView:decidePolicyForNavigationAction:request:frame:decisionListener:) failed to return after waiting 10 seconds. main run loop mode: kCFRunLoopDefaultMode in the console. My code for authorization part looks like this:
- (void)awakeFromNib {
[super awakeFromNib];
GTMOAuth2Authentication *auth = nil;
auth = [GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
clientID:kMyClientID
clientSecret:kMyClientSecret];
if (auth)
{
[auth authorizeRequest:nil
delegate:self
didFinishSelector:#selector(authentication:request:finishedWithError:)];
}
}
- (void)signInToGoogle {
[self signOut];
NSString *keychainItemName = kKeychainItemName;
NSString *scope = #"https://spreadsheets.google.com/feeds https://docs.google.com/feeds";
NSString *clientID = kMyClientID;
NSString *clientSecret = kMyClientSecret;
if ([clientID length] == 0 || [clientSecret length] == 0) {
NSString *msg = #"The sample code requires a valid client ID and client secret to sign in.";
[self displayAlertWithMessage:msg];
return;
}
SEL finishedSel = #selector(viewController:finishedWithAuth:error:);
GTMOAuth2ViewControllerTouch *viewController = [[GTMOAuth2ViewControllerTouch alloc] initWithScope:scope
clientID:clientID
clientSecret:clientSecret
keychainItemName:keychainItemName
finishedSelector:finishedSel];
delegate:self
[self presentViewController:viewController animated:YES completion:^{}];
[[self navigationController] pushViewController:viewController animated:YES];
}
- (void)authentication:(GTMOAuth2Authentication *)auth
request:(NSMutableURLRequest *)request
finishedWithError:(NSError *)error {
if (error != nil) {
NSLog(#"error!");
} else {
[self isAuthorizedWithAuthentication:auth];
self.auth = auth;
}
}
And copied the same class from first application to second one, and made exactly same nib file.
I'm trying to login to Tumblr via OAuth and a Mac App I begin to code.
I donwloaded gtm-oauth source code, everything is fine.
I just began with this code inside a view controller :
- (GTMOAuthAuthentication *)myCustomAuth {
GTMOAuthAuthentication *auth;
auth = [[[GTMOAuthAuthentication alloc] initWithSignatureMethod:kGTMOAuthSignatureMethodHMAC_SHA1
consumerKey:kConsumerKey
privateKey:kConsumerSecret] autorelease];
auth.serviceProvider = #"Custom Auth Service";
return auth;
}
- (void)viewController:(GTMOAuthViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuthAuthentication *)auth
error:(NSError *)error {
NSLog(#"finishedWithAuth");
if (error != nil) {
NSLog(#"failed");
} else {
NSLog(#"done");
}
}
- (void)signInToCustomService {
GTMOAuthAuthentication *auth = [self myCustomAuth];
if (auth == nil) {
NSLog(#"A valid consumer key and consumer secret are required for signing in to Tumblr");
}
else
{
NSLog(#"Ok auth");
}
NSURL *requestURL = [NSURL URLWithString:#"http://www.tumblr.com/oauth/request_token"];
NSURL *accessURL = [NSURL URLWithString:#"http://www.tumblr.com/oauth/access_token"];
NSURL *authorizeURL = [NSURL URLWithString:#"http://www.tumblr.com/oauth/authorize"];
NSString *scope = #"http://api.tumblr.com"; // HERE I DON'T KNOW WHAT TO WRITE
GTMOAuthViewControllerTouch *viewController;
viewController = [[[GTMOAuthViewControllerTouch alloc] initWithScope:scope
language:nil
requestTokenURL:requestURL
authorizeTokenURL:authorizeURL
accessTokenURL:accessURL
authentication:auth
appServiceName:#"My App: Custom Service"
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)] autorelease];
[self presentModalViewController:viewController animated:YES];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self signInToCustomService];
}
But nothing happens.
- (void)viewController:(GTMOAuthViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuthAuthentication *)auth
error:(NSError *)error;
This method is never called.
Perhaps this is my scope variable. I don't know which value I have to write for it.
Thanks for your help !