Hello in Ios i am using Sttwitter Api
in view i call to twitter page and then user can login and after login call appDelegate page
and in appDelegate.m file call to this function
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
// NSLog(#"%#",url );
NSLog(#"%#",[url scheme]);
if ([[url scheme] isEqualToString:#"fb1428424747377519"])
{
return [FBAppCall handleOpenURL:url
sourceApplication:sourceApplication
withSession:self.session];
return YES;
}
else if ([[url scheme] isEqualToString:#"myapp"])
{
NSDictionary *d = [self parametersDictionaryFromQueryString:[url query]];
NSString *token = d[#"oauth_token"];
NSString *verifier = d[#"oauth_verifier"];
HomeViewController *vc = (HomeViewController *)[[self window] rootViewController];
NSLog(#"%#",token);
NSLog(#"%#",verifier);
[vc setOAuthToken:token oauthVerifier:verifier];
return YES;
}else
{
return NO;
}
in this code [vc setOAuthToken:token oauthVerifier:verifier]; my app is cash
in homeviewController.h i have add this method
- (void)setOAuthToken:(NSString *)token oauthVerifier:(NSString *)verfier;
and HomeViewController.m
- (void)setOAuthToken:(NSString *)token oauthVerifier:(NSString *)verifier {
[_twitter postAccessTokenRequestWithPIN:verifier successBlock:^(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName) {
NSLog(#"-- screenName: %#", screenName);
NSLog(#"-- userID: %#", userID);
//name=screenName;
// [self saveYourDescription];
// [self timeline];
// [self getimage];
} errorBlock:^(NSError *error) {
NSLog(#"-- %#", [error localizedDescription]);
}];
}
and Error is
2014-06-12 17:52:03.834[3302:90b] -[UINavigationController setOAuthToken:oauthVerifier:]: unrecognized selector sent to instance 0x10acc3af0
2014-06-12 17:52:03.864[3302:90b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController setOAuthToken:oauthVerifier:]: unrecognized selector sent to instance 0x10acc3af0'
please Help me
It seems that vc in not an instance of HomeViewController but UINavigationController instead.
You have to send the setOAuthToken:oauthVerifier: message to a HomeViewController instance.
Your HomeViewController not look like a "rootViewController", as #nst mentioned.
One possible solution is use delegate to call setOAuthToken method.
For this particular example you files will change as
Declare a delegate of HomeViewController in AppDelegate.h after #interface
AppDelegate.h
#property (assign) id home_delegate;
And in AppDelegate.m replace this line
HomeViewController *vc = (HomeViewController *)[[self window] rootViewController];
With
[home_delegate setOAuthToken:token oauthVerifier:verifier];
P.S: Don't forget to assign delegate to self in HomeViewController
Use this Simple code and Just Replace with your Code It's Working
for (UIViewController *vc in ((UINavigationController *)[[self window] rootViewController]).viewControllers) {
if ([vc isKindOfClass:[ViewController class]]) {
ViewController *viewController = (ViewController *)vc;
[viewController setOAuthToken:token oauthVerifier:verifier];
}
}
Related
I would like to show ChatViewController via a tab bar controller. The current initial view for the app is a NavigationController that loads the ChatViewController. When the ChatViewController is loaded, it checks to see if the ‘joinedchat’ method was called. If not, it presents LoginViewController to allow users to authenticate into the ChatViewController. When the user authenticates, LoginViewController is dismissed.
The LoginViewController and the ComposeViewController, are modal view controllers that are displayed on top of the ChatViewController.
I would like to access this ChatViewController at a much later point in the storyboard, while keeping it as the rootviewcontroller so it can still preserve the data model it uses for classes in anticipation of the $_POST method it uses.
Instead of presenting the LoginViewController if joinedchat hasn’t yet been called, I am showing a different view controller. About 4 view controllers later, after the user has gone on a different process, I use a tab bar controller to access the LoginViewController again. When I try to call the postUpdateRequest method to access the ChatViewController, the app crashes with the output in the debugger:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[1]'
I suspect it’s because the app uses a strict data model that sets snd stores default versions of the strings that will be posted by the user from LoginViewController via postJoinRequest. Does anyone know any ways to authenticate users using this data?
AppDelegate.m - didRegisterForRemoteNotificationsWithDeviceToken
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
UINavigationController *navigationController = (UINavigationController*)_window.rootViewController;
ChatViewController *chatViewController = (ChatViewController*)[navigationController.viewControllers objectAtIndex:0];
DataModel *dataModel = chatViewController.dataModel;
NSString* oldToken = [dataModel deviceToken];
NSString* newToken = [deviceToken description];
newToken = [newToken stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]];
newToken = [newToken stringByReplacingOccurrencesOfString:#" " withString:#""];
NSLog(#"My token is: %#", newToken);
[dataModel setDeviceToken:newToken];
if ([dataModel joinedChat] && ![newToken isEqualToString:oldToken])
{
[self postUpdateRequest];
}
}
AppDelegate.m - PostUpdateRequest
- (void)postUpdateRequest
{
UINavigationController *navigationController = (UINavigationController*)_window.rootViewController;
ChatViewController *chatViewController = (ChatViewController*)[navigationController.viewControllers objectAtIndex:0];
DataModel *dataModel = chatViewController.dataModel;
NSDictionary *params = #{#"cmd":#"update",
#"user_id":[dataModel userId],
#"token":[dataModel deviceToken]};
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:ServerApiURL]];
[client
postPath:#"/api.php"
parameters:params
success:nil failure:nil];
}
DataModel.m - initializer
+ (void)initialize
{
if (self == [DataModel class])
{
// Register default values for our settings
[[NSUserDefaults standardUserDefaults] registerDefaults:
#{NicknameKey: #"",
SecretCodeKey: #"",
JoinedChatKey: #0,
DeviceTokenKey: #"0",
UserId:#""}];
}
}
DataModel.m - userId
- (NSString*)userId
{
NSString *userId = [[NSUserDefaults standardUserDefaults] stringForKey:UserId];
if (userId == nil || userId.length == 0) {
userId = [[[NSUUID UUID] UUIDString] stringByReplacingOccurrencesOfString:#"-" withString:#""];
[[NSUserDefaults standardUserDefaults] setObject:userId forKey:UserId];
}
return userId;
}
LoginViewController.h (Update)
#class DataModel;
// The Login screen lets the user register a nickname and chat room
#interface LoginViewController : UIViewController
#property (nonatomic, assign) DataModel* dataModel;
#property (nonatomic, strong) AFHTTPClient *client;
#end
LoginViewController.m - postJoinRequest & loginAction
- (void)postJoinRequest
{
MBProgressHUD* hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = NSLocalizedString(#"Connecting", nil);
NSDictionary *params = #{#"cmd":#"join",
#"user_id":[_dataModel userId],
#"token":[_dataModel deviceToken],
#"name":[_dataModel nickname],
#"code":[_dataModel secretCode]};
[_client postPath:#"/api.php"
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
if ([self isViewLoaded]) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
if([operation.response statusCode] != 200) {
ShowErrorAlert(NSLocalizedString(#"There was an error communicating with the server", nil));
} else {
[self userDidJoin];
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
if ([self isViewLoaded]) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
ShowErrorAlert([error localizedDescription]);
}
}];
}
- (IBAction)loginAction
{
if (self.nicknameTextField.text.length == 0)
{
ShowErrorAlert(NSLocalizedString(#"Fill in your nickname", nil));
return;
}
if (self.secretCodeTextField.text.length == 0)
{
ShowErrorAlert(NSLocalizedString(#"Fill in a secret code", nil));
return;
}
[self.dataModel setNickname:self.nicknameTextField.text];
[self.dataModel setSecretCode:self.secretCodeTextField.text];
// Hide the keyboard
[self.nicknameTextField resignFirstResponder];
[self.secretCodeTextField resignFirstResponder];
[self postJoinRequest];
}
ChatViewController.h (Update)
#import "ComposeViewController.h"
#class DataModel;
// The main screen of the app. It shows the history of all messages that
// this user has sent and received. It also opens the Compose screen when
// the user wants to send a new message.
#interface ChatViewController : UITableViewController <ComposeDelegate>
#property (nonatomic, strong, readonly) DataModel* dataModel;
#end
Update Terminal Output
I'm having this exception that is not being caught, even is in a handling exception (#try{}#catch{}), it is probably something very easy but I can't see it at the moment. The exception says 'Tried to pop to a view controller that doesn't exist.' I believe a parameter is being pass nil but I don't see it:
-(void) theProblemMethod
{
dispatch_async(dispatch_get_main_queue(), ^{
#try {
[[self topViewController] dismissViewControllerAnimated:YES completion: ^{
UIViewController * rootViewControler = nil;
if ((rootViewControler = (UIViewController *) [UIApplication sharedApplication].keyWindow.rootViewController))
{
if([self topViewController])
[(UINavigationController *)[self topViewController].navigationController popToViewController:rootViewControler animated:YES];
if ((rootViewControler = (UIViewController *) [[[[UIApplication sharedApplication] delegate] window] rootViewController].presentedViewController)) {
[rootViewControler dismissViewControllerAnimated:YES completion:
^{
//do something here
}];
}
}
}];
} #catch (NSException *exception) {
NSLog(#"There is a problem at [myClass theProblemMethod] Exception: %#, reason: %#", [exception name], [exception reason]);
} #finally {}
});
}
Does anyone see the problem?
This error happens when the popped view controller is nil, or the popped view controller is not in the navigation view controller stack. Check both before popping.
UIViewController *poppedVC = ...
UINavigationController *nc = ...
if (poppedVC && [nc.viewControllers containsObject:poppedVC]) {
[nc popViewControllerAnimated:poppedVC];
}
I found the problem! I just found that the problem pointed to be at the line:
[(UINavigationController *)[self topViewController].navigationController popToViewController:rootViewControler animated:YES];
My code was trying to access the property navigationController after dismiss topViewController view (its parent).
The solution for this was store the navigationControllerstrong text in a temporal variable before dismiss topViewController after the #try:
UINavigationController * aNavigationController = (UINavigationController *)[[self topViewController] navigationController];
Finally :
-(void) theProblemMethod
{
dispatch_async(dispatch_get_main_queue(), ^{
#try {
UINavigationController * aNavigationController = (UINavigationController *)[[self topViewController] navigationController];
[[self topViewController] dismissViewControllerAnimated:YES completion: ^{
UIViewController * rootViewControler = nil;
if ((rootViewControler = (UIViewController *) [UIApplication sharedApplication].keyWindow.rootViewController))
{
[(UINavigationController *)[self topViewController].navigationController popToViewController:rootViewControler animated:YES];
if ((rootViewControler = (UIViewController *) [[[[UIApplication sharedApplication] delegate] window] rootViewController].presentedViewController)) {
[rootViewControler dismissViewControllerAnimated:YES completion:
^{
//do something here
}];
}
}
}];
} #catch (NSException *exception) {
NSLog(#"There is a problem at [myClass theProblemMethod] Exception: %#, reason: %#", [exception name], [exception reason]);
} #finally {}
});
}
Basically I was removing A and trying at the same time to invoke its child A.child inside A right after A was removed.
I am creating a iOS static library in which user will pass the name of the Viewontroller and some parameters inside the push and I am getting these details in didReceiveRemoteNotification and from here I got a string suppose NSString *vcName = #"ViewController2" and parameter suppose NSString *param1= #"UserName" NSString *param2= #"email" now I want to pass these parameters to the viewController Which name's string is received from push. But I don't want to write #import ViewController2.
I am able to redirect to ViewController2 without importing it but don't know how to pass these parameters to ViewController2
I can redirect to the viewController from the following code.
NSString *vcName = #"ViewController2";
NSString *param1= #"UserName";
NSString *param2= #"user_email";
UIStoryboard * storyboard = [[[UIApplication sharedApplication] keyWindow] rootViewController].storyboard;
UIViewController *vcToOpen = [storyboard instantiateViewControllerWithIdentifier:vcName]];
vcToOpen.modalPresentationStyle =UIModalPresentationFullScreen;
[[[[UIApplication sharedApplication]keyWindow] rootViewController] presentViewController:vcToOpen animated:YES completion:nil];
Now I want to get these two parameter's value in ViewController2. Can anybody help me how to do it. without writing #import ViewController2 because app can has many ViewControllers and vcName can be any of them.
AppDelegate.h
-(NSString *)getEmail;
-(NSString *)getName;
-(void)setEmail:(NSString *)email Name:(NSString *)name;
+(AppDelegate *)sharedAppDelegate;
AppDelegate.m
#interface AppDelegate ()
{
NSString *strEmail, *strName;
}
-(NSString *)getEmail
{
return strEmail;
}
-(NSString *)getName
{
return strName;
}
-(void)setEmail:(NSString *)email Name:(NSString *)name
{
strEmail = email;
strName = name;
}
+(AppDelegate *)sharedAppDelegate
{
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
ViewController1.m
#import "AppDelegate.h"
-(void)gotoViewController2
{
[[AppDelegate sharedAppDelegate] setEmail:#"email#gmail.com" Name:#"name1234"];
[self performSegueWithIdentifier:#"segueToViewController2" sender:nil];
}
ViewController2.m
#import "AppDelegate.h"
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *name = [[AppDelegate sharedAppDelegate]getName];
NSString *email = [[AppDelegate sharedAppDelegate]getEmail];
NSLog(#"name = %# and email = %#",name, email); //name = name1234 and email = email#gmail.com
}
Storing values in your app delegate is just messy.
Each one of your UIViewControllers that could be launched from a push notification could conform to a custom 'launch' protocol.
Each UIViewController e.g. 'UIViewController2' would conform to this protocol.
You could write the protocol like this:
#protocol LaunchProtocol <NSObject>
- (void) launchParams:(NSDictionary *)params;
#end
Each UIViewController could conform to this protocol, like so:
#interface ViewController2 : UIViewController <LaunchProtocol>
#end
#implementation ViewController2
- (void) launchParams:(NSDictionary *)params {
}
#end
Your app delegate only needs to know about the protocol, it doesn't care about your UIViewControllers.
When you get a push notification you check if the view controller conforms to the launch protocol.
...
vcToOpen.modalPresentationStyle =UIModalPresentationFullScreen;
if ([vcToOpen conformsToProtocol:#protocol(LaunchProtocol)]) {
UIViewController <LaunchProtocol> *launchController = (UIViewController <LaunchProtocol> *) vcToOpen;
NSDictionary* params = #{ /* create your param dict */ };
[launchController launchParams:params];
}
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:vcToOpen animated:YES completion:nil];
...
You would include the information from the push notification in the 'params' dict, and the UIViewController would extract what information it needs from it in launchParams:
- (void) launchParams:(NSDictionary *)params {
NSLog(#"Username: %#", params[#"username"]);
}
Actually you can use Singleton design pattern to achieve this. Create one shared instance class to store the values.
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
create properties inside the manager class which needs to be saved, then access the values from the manager class.
I don't seem to be able to mock NSUserDefaults. I want it to send back the data that I tell it too but instead, it sends back the data stored in it from when running the app.
I am using:
Specta
Expecta
OCMockito
I have the following test:
describe(#"AppDelegate", ^{
__block AppDelegate *appDelegate;
beforeEach(^{
appDelegate = [AppDelegate new];
});
afterEach(^{
appDelegate = nil;
});
describe(#"application did finish launching with options", ^{
beforeEach(^{
[appDelegate application:nil didFinishLaunchingWithOptions:nil];
NSUserDefaults *mockUserDefaults = mock([NSUserDefaults class]);
[given([mockUserDefaults objectForKey:#"currentUser"]) willReturn:nil];
});
it(#"should have a login view controller as root view controller", ^{
expect(appDelegate.window.rootViewController).to.beKindOf([ToALoginViewController class]);
});
});
});
So the above test fails because it actually returns some data for currentUser. What am I doing wrong?
Following on from what Ken Kuan said, I made the following changes:
AppDelegate.h
#property (strong, nonatomic) NSUserDefaults *userDefaults;
- (void)setUserDefaults:(NSUserDefaults *)userDefaults;
AppDelegate.m
- (void)setUserDefaults:(NSUserDefaults *)userDefaults {
_userDefaults = userDefaults;
}
AppDelegateSpec.m
beforeEach(^{
[appDelegate application:nil didFinishLaunchingWithOptions:nil];
NSUserDefaults *mockUserDefaults = mock([NSUserDefaults class]);
[appDelegate setUserDefaults:mockUserDefaults];
[given([mockUserDefaults objectForKey:#"currentUser"]) willReturn:nil];
});
However, I still get the same problem.
You can mock NSUserDefaults. I have this code which is working fine.
When you are mocking NSUserdefaults make sure you are stopping mock at the end of test case, otherwise it would cause errors in other places where you are using NSUserDefaults.
id userDefaultsMock = OCMClassMock([NSUserDefaults class]);
OCMStub([userDefaultsMock standardUserDefaults]).andReturn(userDefaultsMock);
OCMStub([[userDefaultsMock standardUserDefaults] boolForKey:SHOWMYNAME]).andReturn(#"N");
[self.myObject codewithuserdefaults];
OCMVerify( [[NSUserDefaults standardUserDefaults] setValue:[OCMArg any] forKey:FIRSTNAME]);
[userDefaultsMock stopMocking];
You must use [NSUserDefaults standardUserDefaults] in appDelegate which is actual user defaults, not your mock.
A solution is to make user default to a property of app delegate and set it with your mockUserDefaults in tests.
Another one is to swizzle [NSUserDefaults standardUserDefaults] to return your mockUserDefaults in your tests.
So with much thanks to Ken Kuan, I managed to solve the problem I was having. Heres how I achieved it.
I added a NSUserDefaults property to the AppDelegate and a setter function.
I set self.userDefaults to [NSUserDefaults standardUserDefaults] in application:willFinishLaunchingWithOptions:.
I then set the rootViewController in application:didFinishLaunchingWithOptions:
AppDelegate.h
#property (strong, nonatomic) NSUserDefaults *userDefaults;
- (void)setUserDefaults:(NSUserDefaults *)userDefaults;
AppDelegate.m
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.userDefaults = [NSUserDefaults standardUserDefaults];
return YES;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
NSData *encodedUserData = [self.userDefaults objectForKey:#"currentUser"];
if (encodedUserData) {
NSLog(#"Have current user");
self.window.rootViewController = [ThermostatViewController new];
} else {
NSLog(#"No current user");
self.window.rootViewController = [ToALoginViewController new];
}
[self.window makeKeyAndVisible];
return YES;
}
- (void)setUserDefaults:(NSUserDefaults *)userDefaults {
_userDefaults = userDefaults;
}
AppDelegateSpec.m
describe(#"application will finish launching with options", ^{
beforeEach(^{
[appDelegate application:nil willFinishLaunchingWithOptions:nil];
});
context(#"application did finish launching with options and with no current user", ^{
beforeEach(^{
NSUserDefaults *mockUserDefaults = mock([NSUserDefaults class]);
[appDelegate setUserDefaults:mockUserDefaults];
[given([mockUserDefaults objectForKey:#"currentUser"]) willReturn:nil];
[appDelegate application:nil didFinishLaunchingWithOptions:nil];
});
it(#"should have a login view controller as root view controller", ^{
expect(appDelegate.window.rootViewController).to.beKindOf([ToALoginViewController class]);
});
});
});
When I login to my app, my app does push the ViewController XYZMainViewController, XYZMainViewController viewWillAppear:animated call method that makes a request to my API to retrieve the authenticated user data, at this time I update the text of a label to show the user name. When I logout the app, it returns me to the login ViewController, when I do login again with another user, XYZMainViewController label text contains the name of the previous user, without updating the label text.
XYZMainViewController.m
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self homeProfile];
}
- (void)homeProfile
{
[NXOAuth2Request performMethod:#"GET"
onResource:[NSURL URLWithString:#"http://{url}/users/userinfo"]
usingParameters:nil
withAccount:[XYZCommonFunctions user]
sendProgressHandler:nil
responseHandler:^(NSURLResponse *response, NSData *responseData, NSError *error){
NSDictionary *parsedData = [[NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error] objectForKey:#"data"];
_user = [parsedData objectForKey:#"user"];
[self.label setText:[NSString stringWithFormat:#"Welcome %#!", [_user objectForKey:#"username"]]];
}];
}
- (IBAction)logout:(id)sender {
XYZAppDelegate* appDelegate = (XYZAppDelegate*)[[UIApplication sharedApplication] delegate];
[appDelegate logout];
}
XYZAppDelegate.m
- (void)login
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *identifier = [prefs stringForKey:#"accountidentifier"];
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
NSString *viewIdentifier = #"WelcomeView";
if(identifier != nil){
NXOAuth2Account *account = [[NXOAuth2AccountStore sharedStore] accountWithIdentifier:identifier];
if(account != nil) {
viewIdentifier = #"MainView";
}
UIViewController *controller = [mainStoryboard instantiateViewControllerWithIdentifier: viewIdentifier];
[navigationController pushViewController:controller animated:NO];
return;
}
}
- (void)logout
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs removeObjectForKey:#"accountidentifier"];
[prefs synchronize];
for (NXOAuth2Account *a in [[NXOAuth2AccountStore sharedStore] accounts] ){
[[NXOAuth2AccountStore sharedStore] removeAccount:a];
}
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
[navigationController popToRootViewControllerAnimated:YES];
}
I need to reinitialize all data in XYZMainViewController.
Thank you.
Look like problem is related to fetching JSON Object. It is possible that everytime you have send same user to fetch user data. You are not using NSUserdefault object to display name, you are using value, which is return by JSON Object. According to me cause of error is "withAccount:[XYZCommonFunctions user]" line.
I would like to suggest, instead of using
-(void)viewWillAppear:(BOOL)animated {
you can use
- (void)viewDidLoad
so that your login action performed only when your LoginController loads,instead when LoginController appear.
New viewwillAppear look as given below -
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self.label setText:#""];
}
and ViewDidLoad -
- (void)viewDidLoad
{
[super viewDidLoad];
[self homeProfile];
}
Also check your json response, whether you are getting response success or error.According to response need to handle.
Hope this helps.