I'm trying to add a user for the first time app is opened. That adding response back to me with a some sort of password, what we call Access Token. The token is using to reaching the API services.
Anyway, beside that story, what my problem is for the first time app open, I can add the user and get the token and I saved it as NSUserDefault, but I can't reach to API Services since it tries to reach services with null. After refreshing or reopening the app or switching another view and back that view will solve the issue. But just for the first time, it can't reach the service.
The problem is here, obviously, when the app opens for the first time, before the adding operation is finished, I try to reach to service what cause it to stay null. So here is the code I have:
- (void)viewDidLoad {
[super viewDidLoad];
//Adding user.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // User informations.
NSString *accessToken = [defaults objectForKey:#"accessToken"];
if(accessToken == nil)
[APIClient AddUser];
//Adding the inital viewController.
UIViewController *vc = [self viewControllerForSegmentIndex:self.typeSegmentedControl.selectedSegmentIndex];
[self addChildViewController:vc];
vc.view.frame = self.contentView.bounds;
[self.contentView addSubview:vc.view];
self.currentViewController = vc;
}
Don't hang up with what I'm doing while adding the initial viewController, rather than that please just see what happens there as I add the user and then initialize the viewController.
Being asynchronous is the problem, so I'm looking a way to adding the user before the app actually started or launched.
I'm not sure in this case if I have to add the user in application:didFinishLauncingWithOptions, but I don't think so that is the problem.
Okay, so the first thing I would do is extract token related code to the function in view controller. Let's call it tokenReceived:
-(void) tokenReceived{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // User informations.
NSString *accessToken = [defaults objectForKey:#"accessToken"];
if(accessToken == nil)
[APIClient AddUser];
//Adding the inital viewController.
UIViewController *vc = [self viewControllerForSegmentIndex:self.typeSegmentedControl.selectedSegmentIndex];
[self addChildViewController:vc];
vc.view.frame = self.contentView.bounds;
[self.contentView addSubview:vc.view];
self.currentViewController = vc;
}
In the view did load add the following:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(onGetToken)
name:#"getToken"
object:nil];
then add the new method:
- (void) onGetToken
{
[self tokenReceived]
}
and, ofcourse, when you get the token ( in app delegate or wherever you retrieve token ) send notification to observer:
// your code, saving to nsuserdefualts etc.
[[NSNotificationCenter defaultCenter]
postNotificationName:#"getToken"
object:self];
Explanation: It is a bad practice to block main thread with network - your app will not respond to any of the events and if you don't have 3g or wifi - you are stuck. User experience is bad, and that will drive users away.
On the other hand, this will open user screen, and you can put loader if you want till you notify the view controller that you actually got something. This way you will keep async nature of network and still refresh UI when you get one. If you are concerned that you will get token before opening view controller, you can add tokenReceived to view did load as well.
One more thing, you can send NSNotification with the object, and get one from NSNotification object, but in this case you just need to be notified that something happened, you don't need that data.
Related
I'm using the NSNotification centre to detect changes of currency so I can update all the other classes. When a currency change occurs, all the other classes and views get updated, however when there is no currency change, and if you press the back button to go back to the home page the view loads on top of the already existing view.
Code for NSNotification center
if ([overviewModel.currency isEqual:#"GBP"]){
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataUpdated"
object:self];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:#"DataUpdated"
object:self];
}
Code for handling updated data in homepage:
for (UIView *b in self.view.subviews) {
[b removeFromSuperview];
}
self.build = [[ApiRequestBuild alloc]initWithVersionKey:kAPI_VERSION_KEY requestType:kAPI_REQUEST_TYPE data:#""];
[self.build setQueryWithSection:#"homepage" value:#"" parameter:#[]];
self.request = [[ApiRequest alloc]init];
self.request.delegate = self;
[self.request sendRequestWithParams:[self.build buildConfig] toUrl:kAPI_URL_STRING];
I know why this is happening, the request gets sent again so the page loads on top of the already existing page, what I don't understand is why doesn't the remove from subview code get rid of the of the view and how would I be able to fix this? thanks
The removeFromSuperview won't work if it's being called from another thread (than main thread). Your notification will be received on the same thread it was fired from. I'll wager that you're listening to a model change event (regarding your currency state) on another thread.
Try dispatching to main queue before walking your copy of subviews to remove them all.
I created my iMessage extension, when I try to open it, the first screen appears but it is totally frozen, and it does not react in any way.
I've put logs in the viewDidLoad of that first view and nothing appears there, after a few seconds I can already see those logs.
To make the application freezing lose that status, user has to slide screen left or right and back again.
I've tried looking all over the web for someone who happens to be the same, but I could not find anything.
It does not come to mind more screenshots or portions of code add, if you think I should provide some additional information, just let me know
Any help would be appreciated.
Thank you.
UPDATE:
This is my Project Structure.
This is my viewDidLoad code.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"here viewDidLoad iMessage ext~~~!");
[self applyCornerRadiusToBtn];
[self registerPresentationAction];
NSDictionary *user = [self getUserInfoFromHostApp];
if (user) {
NSLog(#"Here != null user info");
//It is assumed that when you enter this point and run this log, the app should navigate to the next screen, but it does not.
[self performSegueWithIdentifier:#"goToYoutubeListIm" sender:nil];
} else {
NSLog(#"Here userInfo null");
}
}
- (NSDictionary *)getUserInfoFromHostApp
{
NSUserDefaults *myDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.com.xxxxx"];
NSDictionary *userNameSaved = [myDefaults objectForKey:#"userInfoExt"];;
NSLog(#"userNameSaved in xxxx Ext ==> %#",userNameSaved);
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.xxxx"];
NSLog(#"groupURL ==> %#",groupURL);
return userNameSaved;
}
For all concerned I have found the problem or problems to be accurate.
1) I was creating my controllers type MSMessagesAppViewController. Apparently there should only be one controller of this type.
2) I had logic in the viewDidAppear in my MSMessagesAppViewController. For some strange reason this also caused the problem, I had to get the logic out there and force the user to interact with a button to execute the logic that was in the didAppear
So one of the users in here managed to show me how to pass data from a child view controller to a parent view controller via a string.
So now the string is passed, BUT, i want that value to stay displayed on the firstViewController after the app is closed and re-opened.
The value is saved in with NSUserDefaults by the way and with an NSLog i am seeing on the conosole it is saved in the apps folder but that value isnt saved onto the UILabel display.
It only displays it when i put save but then i close and reopen, it dissappears but in an NsLog it is still inside the app but not on display UILabel.
How can i address this ?
On my appDelegate.h i have a
#property (strong, nonatomic) NSString *sharedString;
To pass the secondViewController data to the firstViewController.
In the save method on my secondViewController i have a function related to the
AppDelegate.h declaration which is:
AppDelegate *apiDelegate = [[UIApplication sharedApplication] delegate]
apiDelegate.sharedString = self.textFieldData.text;
And in my firstViewController i have a method which display the data from the second
viewController:
-(void) viewDidAppear:(BOOL)animated {
AppDelegate *apiDelegate = [[UIApplication sharedApplication] delegate]
self.DisplayData.text = appDelegate.sharedString;
[super viewDidAppear: NO];
Is there something wrong which isnt keeping the data intact after app closes or am
I missing something here ?
So one of the users in here managed to show me how to pass data from a
child view controller to a parent view controller via a string.
First you need to establish some hierarchy as to how you get a childViewController from a parentViewController. One way to pass data from childViewController to parentViewController is using a delegate. The other could be using the KVC/KVO protocol. https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html
In this you can simply register an observer for the property defined in the childViewController and observe it's changes wherever you want (well, given the hierarchy is satisfied).
To save the value. You can simply save it using NSUserDefaults. I don't see any code in your post but you can simply define a key and save the value with NSUserDefaults using:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:sharedString forKey:#"sharedString"];
NSString *sharedStringFromDefaults = [defaults objectForKey:#"sharedString"];
Also,
AppDelegate *apiDelegate = [[UIApplication sharedApplication]
delegate]
Apple requires you to avoid such references in the application. It only constrains the app. Further, the sharedString is not required to be in the AppDelegate. Otherwise the AppDelegate will be filled with almost every other data structure you have shared in the app.
//add this code when you want to store string
[[NSUserDefaults standardUserDefaults] setObject:self.textFieldData.text forKey:#"sharedString"];
[[NSUserDefaults standardUserDefaults] synchronize];
//and when you want string than
self.DisplayData.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"sharedString"];
I have setup a custom url scheme for my app, this is how it works when app is not running in background:
custom url link arrives in email, upon clicking brings the login view from storyboard
Upon clicking the login button will take to the you requested tab in tab controller
So far so good.
Here is the problem I am having:
If I click on the custom url link again from email When the app has already been loaded once and its running in the background it does not bring the login view again
My question is what do I need to do load the login view again when I click the custom url link more than once.
Custom url scheme works perfectly the first time but not when the app has already been running. I tried to debug this ... when I click custom url scheme link "handleOpenURL:(NSURL *)url" method is called in my AppDelegate.m file so what do I need to do to load the login screen again from storyboard and how do I check if the login screen is already loaded in memory ... Login screen is my start view in storyboard, below is how handleOpenUrl function looks like in my app delegate.
-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{
if(!url){
return NO;
}
// check if email link was clicked
if ([[url scheme] isEqualToString:#"docova"]) {
NSString *urlString = [url absoluteString];
NSLog(#"URL Parameter string: %#", [url query]);
NSLog(#"incoming url => %#", urlString);
// NSArray *arrayQStrings=[self getDataOfQueryString:urlString];
NSDictionary *dict = [self parseQueryString:[url query]];
NSLog(#"query dict: %#", dict);
NSLog(#"query dict: %#", [dict valueForKey:#"action"]);
NSLog(#"query dict: %#", [dict valueForKey:#"docpath"]);
//[self.tabBarController setSelectedIndex:1]; // tab 3
//[self.parentViewController.tabBarController setSelectedIndex:1]; // tab 3
/* save user data ***/
NSUserDefaults *appPrefs = [NSUserDefaults standardUserDefaults];
[appPrefs setObject:[dict valueForKey:#"action"] forKey:#"action"]; // reset these after using it
[appPrefs setObject:[dict valueForKey:#"docpath"] forKey:#"docpath"]; // reset these after using it
// save data to application preference
[appPrefs synchronize];
return YES;
}
return NO;
}
Can someone suggest on how to bring a view from storyboard upfront when the app has already been running. I know there is activity stack in android, is there something like that in ios as well ...
I was able to solve this by using the commnets from mialkan and also have to change the handleOpenUrl method ( this is deprecated) to " application:openURL:sourceApplication:annotation", so in my openUrl method, I check for specific action and invoked the view accordingly, see below:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: nil];
loginView = [storyboard instantiateViewControllerWithIdentifier:#"SIDLoginViewController"];
self.window.rootViewController = loginView;
Thanks mialkan :)
As I understand, your interface builder structure like this
Navigation Controller -> Login View Controller -> Tabbar Controller
When your app already run in background, if the user open the app via mail link you want show login view first?
you can use NSNotificationCenter to invoke a method to pop to Login view.
Here is a ex. code to register your notification and to call it.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(popToLoginView:) name:#"popToLoginViewNotification" object:nil];
To notifiy methods.
[[NSNotificationCenter defaultCenter] postNotificationName:#"popToLoginViewNotification" object:nil];
if you have TabbarController classes put popToLoginView method. Or each view controller of tabbar put popToLoginView method. in view check if its current view, with this code
if (viewController.isViewLoaded && viewController.view.window) {
// viewController is visible
}
then pop to login view.
I hope this helps.
I have a NavigationController and one of the tabs is supposed to load a ViewController.
This ViewController (1), when loaded on "viewDidLoad" does some stuff and then pushes a new ViewController (2). The thing is that after ViewController (1) has already passed through viewDidLoad, it won't pass through it again, unless the app is restarted.
Could you guys please refer a clever way to to this?
Here's what I am really doing:
- (void)viewDidLoad
{
// Keep track of cash using NSUserDefaults
BOOL dreceived[63];
int rightData;
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
//Load cash switches
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSData *data = [prefs objectForKey:#"dreceived"];
memcpy(&dreceived, data.bytes, data.length);
for(int n = 72; n >= 1; n = n - 1)
{
if(dreceived[n-1]==1)
{
rightData = n;
}
}
NSLog(#"Right Data %d", rightData);
CashItem *c = [cashflow objectAtIndex:rightData];
// Go for details
CashDetailedViewController *cdetail = [[[CashDetailedViewController alloc] init] autorelease];
cdetail.cash = c;
cdetail.navigationItem.hidesBackButton = YES;
[self.navigationController pushViewController:cdetail animated:YES];
}
The thing is, this code is never called again. And if I touch the tab twice, a blank view is displayed (th original xib view).
Thanks!
It sounds like you would want to use viewWillAppear. It is called every time that your view controller is about to be onscreen.
Although, based on what you've posted, you may want to rethink what your doing. Having a view controller that immediately presents another view controller should like it would lead to a confusing user experience.
Put your code in - (void)viewWillAppear instead
Try calling
[yourViewController.view setNeedsDisplay];
Or you could spin the code out to a seperate method and call it in viewDidLoad and viewDidAppear:animated