iOS 10 open app normal or with url - ios

I'm creating an iPad app with a SplitViewController, thats supposed to work in 2 ways.
You just open the app, and are presented with a master tableview
showing some assignments. These assignments can be 1 of three
different types. You select an assignment and are presented with a
detail ViewController using a show detail segue
You click a link
from another app, opening the app with an id, and should now only be
presented with the assignment corresponding to the id.
I have the following functions in AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let splitViewController = self.window!.rootViewController as! UISplitViewController
UIApplication.shared.statusBarStyle = .lightContent
splitViewController.delegate = self
splitViewController.preferredDisplayMode = .primaryOverlay
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if let itemid = getQueryStringParameter(url: url.absoluteString, param: "itemid"){
print(itemid)
NetworkService().getSpecificExercise(id: itemid) { response, error in
let exercise = response! as VoiceExercise
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
switch exercise.type {
case "STRENGTH":
print("Strength")
let initialViewController : StrengthViewController = storyBoard.instantiateViewController(withIdentifier: "StrengthViewController") as! StrengthViewController
initialViewController.exercise = exercise
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
case "RANGE":
print("Range")
let initialViewController : RangeViewController = storyBoard.instantiateViewController(withIdentifier: "RangeViewController") as! RangeViewController
initialViewController.exercise = exercise
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
case "COMBINED":
print("combined")
let initialViewController : CombinedViewController = storyBoard.instantiateViewController(withIdentifier: "CombinedViewController") as! CombinedViewController
initialViewController.exercise = exercise
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
default:
print(exercise.type)
}
}
}
return true
}
My problem is, that if the app has not been started yet (ie not running in background), and I open the app from another app (or a test link on the same ipad through safari), nothing happens from the open url function - it just shows the splitviewcontroller, with the tableview, as if I'd opened the app on its' own. If the app has already been opened, it behaves as expected - showing the assignment I expect.
Right now that is what I want. When you end an assignment, it opens the other app again, if the app has been opened by a link.
If I wanted to (when I click a link to open) show the assignment, but contained in a navigationcontroller, that was contained in the splitviewcontroller, where the splitviewcontroller had its' desiplaymode set to .primaryHidden, how would I go about this. All tutorials I've found only deals with Navigationcontroller, not contained in a splitviewcontroller. (this is still only for open url)

Related

Problems starting ViewController from a widget click

I have a widget and want to open a particular ViewController when clicking on it. I've read all the documentation and questions on SO regarding the topic, and can't figure out why it isn't working. When clicking the widget, it always opens the default ViewController.
Here's the code for the WidgetView.
struct WidgetAdapter : View {
let entry: TimeLine.Entry
#Environment(\.widgetFamily) var family
#ViewBuilder
var body: some View {
switch family {
case .systemSmall:
SmallView(...).widgetURL(URL(string: "fcv://open_activity"))
case .systemMedium:
MediumView(...).widgetURL(URL(string: "fcv://open_activity"))
default:
LargeView(...).widgetURL(URL(string: "fcv://open_activity"))
}
}
}
Here the AppDelegate method for managing URLs.
func application(_ application: UIApplication, open
url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool{
if url.scheme == "fcv"{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "WidgetActivity") as! WidgetActivityController
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
}
return true
}
I also tried implementing the respective method for the SceneDelegate, I added the url scheme to the URL Types in project info, I added the LSApplicationQueriesSchemes item to the info.plist, used Link instead of .widgetURL... And it didn't work even once. I also think that the method in the AppDelegate is not being called, however, I checked for the cases were that can happen and they don't come to case.
Any help would be appreciated.
I solved the same problem by finding current rootViewController and pushing the view I need:
let rootViewController = window!.rootViewController as! UINavigationController
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)let profileViewController = mainStoryboard.instantiateViewController(withIdentifier: "MainVC") as! CombinedMainVC
rootViewController.pushViewController(profileViewController, animated: false)
It is always UINavigationController in my case, so I push a new VC, in your case you could present it.
I use the same method in AppDelegate, so there could be a problem with your if statement. Where do you set a scheme for a widget URL? Maybe you could just check URL string:
if url.absoluteString.prefix(3) == "fcv" { }

Open the last opened view controller when closing and opening the app

I want to be able to close and reopen the same view controller on my app. The reason I want to do this is because I have a "tutorial" scene that I only want to be brought up the first time you open the app. Every other time I want the app to instantly open up the second view controller. Here is my code:
for GameViewController:
UserDefaults.standard.set(0, forKey: "View")
for GameViewController2:
UserDefaults.standard.set(1, forKey: "View")
for AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let viewCount = UserDefaults.standard.integer(forKey: "View")
var VC = UIViewController()
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
print(viewCount)
if viewCount == 0 {
//FirstView should initiate
VC = storyboard.instantiateViewController(withIdentifier: "First") as! GameViewController
} else {
//SecondView should inititate
VC = storyboard.instantiateViewController(withIdentifier: "Second") as! GameViewController2
}
self.window?.makeKeyAndVisible()
self.window?.rootViewController = VC
return true
}
The problem is that every single time I open the app, it always starts on my first view controller instead of my second view controller. Any help would be appreciated.
Based on the code above, you are not calling UserDefaults.synchronize
var userDefaults = UserDefaults.standard
userDefaults.set(1, forKey:"View")
userDefaults.synchronize()
In both cases it must be synchronised as this saves changes to disk
Update: As noted by #cs4alhaider synchronize() is not needed in Swift 4, so this problem would not occur anymore.

AddingViewcontroller manually into the stack swift 3

I am coding the sign out button in my app.This is the flow of my app.
SplashvwController -> secretCodeViewController ->
LoginviewController -> DashboardViewController
in this DashboardViewController I have the signOut button.
My app has single sign in facility once user logged, next time when he opens the app,
SplashvwController -> DashboardViewController
I want to prompt the user to Loginviewcontroller whenever he clicks the sign out button.
Question
When user going through path 1 I can simply do popviewcontroller to go back to the previous viewcontroller. But when user go though the 2nd path,
how can I add the Loginviewcontroller manually into my
viewcontrollers stack to perform the same operation?
How can I check whether the LoginviewController exists in my current Viewcontrollers stack?
Please help me
I think, the below code helps you,
for (var i = 0; i < self.navigationController?.viewControllers.count; i++) {
if(self.navigationController?.viewControllers[i].isKindOfClass(Loginviewcontroller) == true) {
self.navigationController?.popToViewController(self.navigationController!.viewControllers[i], animated: true)
break;
}
}
To add a ViewController Manually , check the code below..
let navigation = mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
let nav = UINavigationController(rootViewController: navigation)
appdelegate.window!.rootViewController = nav
Set the storyboard reference identifier for a LoginViewController on the Main.Storyboard file.
Whenever you want to show the LoginViewController just call the function
func launchLoginView() {
if let loginVC: LoginViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginStoryboardID") as? LoginViewController {
// .instantiatViewControllerWithIdentifier() returns AnyObject! this must be downcast to utilize it
// LoginStoryboardID is the reference id for login view controller.
self.presentViewController(loginVC, animated: true, completion: nil).
// OR
//UIApplication.shared.keyWindow?.rootViewController = loginVC
}
}
Landing screen based on the User login status.
In AppDelegte.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let isLoggedAlready = //Get the login status
if isLoggedAlready == true {
if let dashBoardVC: DashboardViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("DashboardStoryboardID") as? DashboardViewController {
//Already logged in then directly launch dashboard vc.
//change the code based on your needs
window?.rootViewController = dasbBoardVC
}
}
// Otherwise let it go as flow 1
return true
}
}

iOS: NSUserDefault not loading fast enough in AppDelegate? Conditional fails intermittently

I'm using NSUserDefault in AppDelegate to decide which VC to show to a user first after they load my app.
If user hasn't set a specific setting, they go to VC 1 where they set that.
If user has the setting, they go to VC 2 and on.
My problem is that sometimes I'm being sent to VC1 when I know I have the setting.
This leads me to believe that NSUserDefaults are not being fully loaded before my conditional checks for the value.
Could this be possible and if so is there a way to "block" the load process until the variable is fully loaded?
Here's the relevant code from AppDelegate that I'm using:
class AppDelegate: UIResponder, UIApplicationDelegate {
let prefs = NSUserDefaults.standardUserDefaults()
var window: UIWindow?
func updateNavBar(navBar: UINavigationBar) {
navBar.barTintColor = UIColor(netHex:0x1d8696)
navBar.tintColor = UIColor(netHex:0xAEFFDD)
navBar.titleTextAttributes = [NSForegroundColorAttributeName : UIColor(netHex:0xAEFFDD),NSFontAttributeName : UIFont(name: "Lato-Bold", size: 16)!]
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
self.updateNavBar(UINavigationBar.appearance())
if let team = self.prefs.stringForKey("team")
{
print("Team prefs found. (" + team + ") User sent to IVList")
// show IVList VC
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) // this assumes your storyboard is titled "Main.storyboard"
let yourVC = mainStoryboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController // inside "YOUR_VC_IDENTIFIER" substitute the Storyboard ID you created in step 2 for the view controller you want to open here. And substitute YourViewController with the name of your view controller, like, for example, ViewController2.
appDelegate.window?.rootViewController = yourVC
appDelegate.window?.makeKeyAndVisible()
}else{
// show editProfile VC
print("No team prefs found. User sent to profile Edit")
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) // this assumes your storyboard is titled "Main.storyboard"
let yourVC = mainStoryboard.instantiateViewControllerWithIdentifier("editProfile") as! EditProfileViewController // inside "YOUR_VC_IDENTIFIER" substitute the Storyboard ID you created in step 2 for the view controller you want to open here. And substitute YourViewController with the name of your view controller, like, for example, ViewController2.
appDelegate.window?.rootViewController = yourVC
appDelegate.window?.makeKeyAndVisible()
}
return true
}
I think it's a load timing thing because the issue is intermittent and inconsistent. It happens about 20% of the time.
I recently had exactly this issue.
The simple answer is that didFinishLaunchingWithOptions() in the appDelegate is only called AFTER the nib has been loaded for your initial view controller.
See the discussion at iOS - viewDidLoad is being called BEFORE the didFinishLaunchingWithOptions delegate?
Hope that helps!

Segue from LaunchScreen to a viewController in Main.Storyboard?

I got some logic I want to do in my LaunchScreen, and if the check is alright, I want to segue to a viewController and if not I want to segue to another, is that possible?
I got some logic I want to do in my LaunchScreen
Now that I understand the question, I can answer it: don't. Do your time-consuming logic later. Your job is to launch fast. You need to get out of applicationDidFinishLaunchingWithOptions, get out of viewDidLoad, and launch.
What you show at that point is up to you; if you have time-consuming stuff (in your case, it sounds like you're networking or doing something else that takes time while you load up the data source for a table) and you want to show a special view controller that covers the time with a spinning activity view or something, fine. But during actual launch is not the time to do that.
No you cant code for launchScreen.Storyboard, The reason why :- when your launchScreen.storyboard shows the app is still loading.
Simply put: You cant access your app when it is displaying launchScreen.storyboard, all you can do is make a UI/UX for that not execute any code for it.
Alternative:- Make a viewController that appears as a first viewController check your logic there and do things from there accordingly!
Reference : - https://stackoverflow.com/a/27642160/6297658
Your LaunchScreen is shown while your app is loading. Go to your AppDelgate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
window.rootViewController = //your root view controller that you have figured out with logic
return true
}
Run your check in didFinishLaunchingWithOptions() and use that to "jump" directly to a specific vc. Here's an example using userDefaults, but of course you can replace that with whatever check you're running.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Do some logic
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let welcomeVC = storyboard.instantiateViewControllerWithIdentifier("WelcomeNavController")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = welcomeVC
self.window?.makeKeyAndVisible()
}
}
Add this function into the AppDelegate:
func initialVC(storyboardID: String) {
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("\(storyboardID)") as UIViewController
self.window?.makeKeyAndVisible()
if storyboardID == "tabBarVC" {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = initialViewController
} else {
let navController = UINavigationController(rootViewController: initialViewController)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = navController
}
}
In the didFinishLaunchingWithOptions method inside of the AppDelegate, you can add this:
if currentUser != nil {
initialVC("tabBarVC")
} else {
initialVC("loginVC")
}
You can see in my example, I am either loading the main app VC or the Login VC depending on if the user is logged in. In your case, You can use an if - else statement and do the logic within the initialVC function.
Note: When I call for the loginVC to be loaded, I have to load the navigationController because the loginVC is embedded in a navigationController. For the tabBarVC, I don't embed the navController because it isn't needed.

Resources