I have been all over stackoverflow and all over Google and I cannot seem to figure this one out. Here's my scenario:
I have my app's "main screen" where the user first makes decisions about what they're going to do. The app works off of a CoreData database which is created by "importing" XML files. The user can choose to open an XML file attached to an email in my application, which automatically triggers my main screen to show up and run the import of the file.
I can get this far without any issues. In my storyboard, I have a segue called ParseSegue from my main screen to a view controller which will handle the parsing and give the user some status information.
When the main screen is called via the email app, the main screen automatically calls
[self performSegueWithIdentifier:#"ParseSegue" sender:self];
I then check for this segue name in prepareForSegue and it's a valid name. This is where I assign the file URL to the parser controller so that it can parse the correct file.
The problem is that the segue never actually happens. The prepareForSegue method gets called, the name "ParseSegue" can be checked against and is valid, but the segue itself simply does not happen. If I add a button to the main screen and tell it to perform the segue within the storyboard, it works fine. But calling it programmatically seems to do nothing.
It turns out that I was looking in the wrong place entirely. My problem was that in my appDelegate, where the app reacts to the incoming URL, I was inadvertently creating a new instance of my storyboard and my main view controller. This was different than the one which was already active and may or may not have been on the screen.
The controller I was creating was never actually shown. I only noticed this because the log:
NSLog(#"Source: %#", [segue.sourceViewController description]);
would show different memory addresses for my test (the button push) and the import test. This led me to believe that I was, in fact, working with two different instances of the storyboard and the app's main view controller. Thanks to Paul for the suggestion of logging the destination and the source controllers.
Related
I have been searching the net, and i have found many very good examples on how to create a login page for your iPhone app. However... none of then meets my demands, an since i am new to iPhone development( i know java / c / C++ and objective-c), so the programming itself is not the issue.. The issue is where to put the code and what design patterns to use..
My app is a basic tabbar controller with 3 tabs that could contain navigation controllers, but that is not important.. it is the part before the tabbar controller i am interrested in, namely the need to authenticate the user towards a Lotus Domino Server. I have the authenification code working, so that is not the issue either..
By the way, i am concentrating on IOS5 and using storyboards..
Where do i put the "check if user is still authenticated" code ? The domino server will log the user out after 1 hour, so if the user has the app open(in background) then the code that downloads data will die if the user is not told that the session has expired..
So here is what i would like..
When the app launches, show the login page. This is working for now with the Login View Controller as initial controller, and a modal segue to the tabbar controller..
Next time the App is launched(either from background or from new is user has closed it completely), check if username and password is stored in userdefaults, and then just login in background, and if that fails(password has changed or another failure) then show the login view controller again..
So to sum up, where do i put the "part 2" code ? I have a seperate authenticator class that is using delegates, and i can use this class to perform the authentification, and the it will answer back if all is good.
Do i put this in the AppDelegate code ? If i put it in one of the tabbar viewcontrollers, then there might be an issue with the user having tab2 open when launching after 2 hours, and if the check is in tab1, then tab 2 will fail.. Should i put the code in ALL the tabbar viewcontrollers ? Naaa, that is ugly..
I am leaning towards the AppDelegate(appdidbecomeactive), but can that be used as delegate in my authenticator class ?
That was a bit long, sorry for that, but i needed to explain my problem fully so people would understand what i need..
Thank you for your help.. This is my first post, but this forum is fantastic :)
I'd definitely put both part one and part two code in the AppDelegate.
This allows you to create the appropriate viewcontroller at app start and remove the tabbarcontroller from within the AppDelegate if the authentication times out etc.
I'm not sure about your modal segue to a tabbarcontroller though (I'm not familiar with storyboards yet though). Modal implies you wouldn't be able to present any other view modally on top of the tabbar controller. I think it sounds like it would be much more appropriate to create your logincontroller and tabbarcontroller programmatically and add/remove them directly in your application’s main window as needed from your AppDelegate.
I would tell the AppDelegate to check the login status very time the tab is changed or data loaded.
In my app there is authentication required, so when you launch one of the tabs on tab bar, "class A" checks are there credentials saved if not, "class B" modal view controller with fields to login launches.
So my question is : in which method in class A (loadView, viewWillAppear or maybe in another one) should be implemented checking if there are credentials saved and other stuff described above.
And my additional second question is:
is pushing modalviewcontroller correct way to show login screen, or i should do that differently?
Thank you for reply guys.
OH ! One More Thinh
And one more thing. I've done implementing LoginView by adding delegate and presenting ModalVC (Harkonian the Piquant's method). But in my tab bar app i have got very confusing problem. I mean when user taps login button (assume that everything was correct and he's able to secured data) how PROPERLY switch to tab where is secured info. I mean previously selected tab.
i did it by adding in
-(IBAction) login {
//some code
self.tabBarController.selectedIndex =1;
And it seem to work good but is it correct ?
I have a very similar use case in my app -- it requires a passcode to authenticate. After a lot of testing and tweaking I found the following design to be the best approach:
Don't use class A to launch your credentials VC -- use the app delegate instead.
For security purposes, typically you'll want the credentials VC to show before the user can view the underlying view. It's much easier to handle this in the app delegate than in a VC. In addition, you need to consider what happens when your app is backgrounded -- a screen shot is taken of the current state of the app. If you are using viewController A to show the credentials view, when the app relaunches the user will be able to see whatever sensitive information was visible on app close until the app finishes launching and VC A presents the credentials VC.
Don't insert your credentials view into an existing ViewController -- use a new UIWindow instead.
You don't ever want any other view to be able to sit on top of your credentials view. Ever. Even views that would normally always be on top, like UIAlertView. The easiest way to achieve this is to have a special UIWindow just for your credentials view. Show this window and hide the primary app window whenever you need to display the credentials view.
How does this approach look in practice?
If you are at all interested in how well this design works, you can check out the passcode feature in Audiotorium Notes for iPad. I spent a lot of time with this design to make sure it was as secure as possible.
If you have any specific implementation quests feel free to ask and I'll try to answer them.
I have a problem with a navigation controller.
First of all, there is a navigationviewcontroller.
Also, there is A webviewcontroller-containing view controller, meaning that webview controller is loaded inside WEBcontroller.m
I made that when the WEBcontroller is loaded, it automatically loads google.com. The function is in the -viewDidLoad()
First, when the app is launched, navigationview loads WEBcontroller.m, then WEBcontroller loads google.com as intended.
Then, when I click any link in the google.com, navigationview pushes a new view with
[self.navController pushViewController:newWebController animated:YES];
[newWebController gotoUrl:[request.URL absoluteString]];
It, of course, works. The newly loaded(and alloc) WEBController.m loads gmail.com by calling "gotoUrl" function.
And, I click another links to go "gmail.com/help"
So,
google.com -> gmail.com -> gmail.com/help
Then, I close the app, and play some games... it makes iPhone free memory.
Launching the app again, the "gmail.com/help" webpage is shown. Then, I click the [Back] button which is at the navigationBar which calls [popViewController].
Then, the navigation controller properly go back to preceding WEBController.m which was showing "gmail.com" page.
BUT!! there is a problem. Because the memory was 'dealloc' by iPhone, the WEBController is loaded AGAIN with "google.com" page, not "gmail.com" page.
I've searched this problem but I couldn't get any.
Really thank you for reading and giving some interests to my problem.
I'm confused. You are using a UIWebView? If so, why don't you just let it handle the links/navigation? Why are you creating (and pushing) a new UIWebView for every link click? Technically speaking, a view controller needs to be able to handle being freed and restored from memory by IOS. This is done in viewDidLoad and viewDidUnload. But I don't think that's what you want/need here.
I am building a login onto an completed app. The app have already used the appdelegate but in order to do the login, it needs to use the appdelegate. I am stuck at a point where I cannot "make a new reference outlet for your Navigation Controller to your App Delegate." since the appdelegate in not in my .xib.
How can I make the appdelegate appear on the .xib file so I can link it with the navigation controller?
Thanks.
We need to first understand the architecture of your app. But even without that, one solution that I could think of is this.
Create a viewController named ValidateViewController by right clicking on your project-> add new file -> UIViewControllerSubClass and check the xib option as well.
This will generate the following
ValidateViewController.h
ValidateViewController.m
ValidateViewController.xib
Write all your validations functions in this class. Write a function in this class that would return true on validation success and false on validation failure. Now let's use this class in your appdelegate.
Now in your appdelegate.h, import this ValidateViewController.h. All the validation functions that you defined in the ValidateViewController will now be exposed to be used just by creating and allocating an object of ValidateViewController in your appdelegate. I hope this is pretty straight forward for you. If not, we can look at it again.
Now in the applicationDidFinishLaunching method of your appdelegate, the first thing you do is you load this ValidateViewController as a modalView controller programmatically. Once loaded, call the functions and get the return values from your validate functions, if validation succeeds, dismiss this modalViewController otherwise, you pop up an alertView in the modalViewController saying, validation was unsuccessful and the user stays on the ValidationViewController. I think that should solve your problem. If you need more help please come back. If you find the answer satisfactory, please accept it.
Resolution: While trying to recreate this bug in a fresh project to submit to Apple, I discovered that it is specific to iPhone OS 2.1, and compiling for 2.2 fixes the problem. Stephen, thanks for your help; I'll be accepting your answer since it would have worked if the bug still existed or I wasn't willing to compile for 2.2.
I have an app which is radically changing its database schema in a way that requires me to transform old-style records to new-style ones in code. Since users may store a lot of data in this app, I'm trying to display a modal view controller with a progress bar while it ports the data over (i.e. as the very first thing the user sees). This view controller's viewDidAppear: begins a database transaction and then starts a background thread to do the actual porting, which occasionally uses performSelectorInMainThread:withObject:waitUntilDone: to tell the foreground thread to update the progress bar.
The problem is, viewDidAppear: is being called twice. I noticed this because that "start a transaction" step fails with a "database busy" message, but setting a breakpoint reveals that it is indeed called two times—once by -[UIViewController viewDidMoveToWindow:shouldAppearOrDisappear:], and again by -[UIViewController modalPresentTransitionDidComplete]. Those names appear to be private UIViewController methods, so I'm guessing this is either a framework bug, or I'm doing something UIKit isn't expecting me to do.
Two relevant code excerpts (some irrelevant code has been summarized):
- (void)applicationDidFinishLaunching:(UIApplication *)application {
(register some default settings in NSUserDefaults)
// doing this early because trying to present a modal view controller
// before the view controller is visible seems to break it
[window addSubview:[self.navigationController view]];
// this is the method that may present the modal view
[self.databaseController loadDatabaseWithViewController:self.navigationController];
if(!self.databaseController.willUpgrade) {
[self restoreNavigationControllerState];
}
}
And from my DatabaseController class:
- (void)loadDatabaseWithViewController:(UIViewController*)viewController {
(open the new database)
(compute the path the old database would live at if it existed)
if([[NSFileManager defaultManager] fileExistsAtPath:oldDBPath]) {
(open the old database)
[viewController presentModalViewController:self animated:NO];
}
}
So, is there something I'm screwing up here, or should I file a bug report with Apple?
I saw this in my app too. I never got it entirely confirmed, but I think this is what's happening:
Load root view
Load modal view
OS sends view did appear notification for the view in step 1
The current view controller, which in this instance happens to be your DatabaseController class, picks it up
OS sends the view did appear notification for the modal view
The current view controller gets the notification. In this case it's the exact same controller as last time
In my case I just reset what happened in the first call to viewDidAppear:.
In your case two options spring to mind: a static variable to track whether you've started the upgrade already; or look at the UIView* parameter passed in before starting.