Best Way to Implement an Intro Sequence? Swift - ios

I'm trying to add an intro sequence to my code so that if it's the first time the app is opened, the user can enter some basic information (which I can then store in UserDefaults).
The way that I was thinking of doing this is by having a variable called isFirstTime which is initially set to true. Every time the app is opened, it'll check if there is a value for isFirstTime in UserDefaults. If it isn't there, it'll trigger the View Controller that has my intro sequence to appear. Once the intro sequence is finished, isFirstTime will be set to false and then stored in UserDefaults.
Is this a correct implementation, or is there a more efficient way?
EDIT: If anyone is interested, this is the code I used to implement my intro sequence. I first assign a boolean variable outside of my View Controller that keeps track of whether it's the first time opening the app or not.
var isFirstTime = true
Then, in my ViewDidAppear (it does not work in the ViewDidLoad method), I added this code which checks whether or not I already have a UserDefault for my isFirstTime variable. If yes, I then execute the rest of my program, but if not, I start up my intro sequence's View Controller.
if UserDefaults.standard.object(forKey: "isFirstTime") != nil{
// Not the first time app is opened
isFirstTime = false // I use isFirstTime elsewhere in my code too.
} else {
let introVC = self.storyboard?.instantiateViewController(withIdentifier: "intro")
self.present(introVC!, animated: false, completion: nil)
}
In my intro sequence View Controller, when I am done with my gathering the user's basic information, I do two things: the first is changing the value of isFirstTime and setting it as a UserDefault, and the second is dismissing the View Controller.
isFirstTime = false
UserDefaults.standard.set(isFirstTime, forKey: "isFirstTime")
dismiss(animated: false, completion: nil)

You can achieve it easily. This is code which I have used for it.
Step 1 First create a file called UserDefaultManager.swift
import UIKit
// User Defaults Manager Constants
let kIsFirstTimeLaunch = "IsFirstTimeLaunch"
class UserDefaultsManager: NSObject {
// MARK: Setter Methods
class func setIsFirstTimeLaunch(flag: Bool) {
NSUserDefaults.standardUserDefaults().setBool(flag, forKey:kIsFirstTimeLaunch)
NSUserDefaults.standardUserDefaults().synchronize()
}
// MARK: Getter Methods
class func isFirstTimeLaunch() -> Bool {
return NSUserDefaults.standardUserDefaults().boolForKey(kIsFirstTimeLaunch)
}
// MARK: Reset Methods
class func resetIsFirstTimeLaunch() {
NSUserDefaults.standardUserDefaults().removeObjectForKey(kIsFirstTimeLaunch)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
Step 2: In your Implementation file check it like below :
if(!UserDefaultsManager.isFirstTimeLaunch()) {
// Your code here.
let introVC = self.storyboard?.instantiateViewController(withIdentifier: "intro")
self.present(introVC!, animated: false, completion: nil)
// Update value in user defaults
UserDefaultsManager.setIsFirstTimeLaunch(true)
}

Related

Swift: How to check if UserDefaults exists and if not save a chosen standard value?

I am trying to check if a UserDefaults Key exists and if not set it to a standard value of my choice, but the answers here on stack overflow didn't help me to get this working.
Essentially I have a couple of UISwitches, one is on and the rest is set to off from the beginning. Now my problem is that I don't know how to save these initial states into UserDefaults when the viewController is loaded and those keys do not exists.
This is how I tried to check if the key for the UISwitch exists and if not set it to true (because that's the state I want it to be) and then check again what the bool for the key is and set the UISwitch to it (this is essentially important when the viewController is opened another time):
func setupSwitches() {
let defaults = UserDefaults.standard
//check if the key exists
if !defaults.bool(forKey: "parallax") {
switchParallax.setOn(true, animated: true)
}
//check for the key and set the UISwitch
if defaults.bool(forKey: "parallax") {
switchParallax.setOn(true, animated: true)
} else {
switchParallax.setOn(false, animated: true)
}
}
When the user presses the corresponding button I set the UserDefaults key like this:
#IBAction func switchParallax_tapped(_ sender: UISwitch) {
let defaults = UserDefaults.standard
if sender.isOn == true {
defaults.set(true, forKey: "parallax")
} else {
defaults.set(false, forKey: "parallax")
}
}
This is obviously working, but the problem is in the first code above.
First of all I am not sure how to check if it exists and if not set it to "true" and since the function is called setupSwitches() it is runs every time the viewController is shown.
So I don't know if there is a better way (eg. because of the memory issues) to check if a key exists, if not set it to true and if it already exists get the bool from UserDefaults and set the switch to the right state.
The problem is you can't determine exists with UserDefaults.standard.bool(forKey: key). UserDefaults.standard.object(forKey: key) returns Any? so you can use it to test for nil e.g.
extension UserDefaults {
static func exists(key: String) -> Bool {
return UserDefaults.standard.object(forKey: key) != nil
}
}

Starting at a certain view if user is logged in iOS Swift

I am currently making a small login app using Firebase. I am currently having problems with my login page.
When my users already are logged in, when they open the app, I want to change the initial view controller, so that the user can go straight to the homepage.
So my question is, what line of code do I have to perform in order to do this?
override func viewDidLoad() {
super.viewDidLoad()
if FIRAuth.auth() ? .currentUser ? .uid == nil {
notLoggedIn()
}
}
func notLoggedIn() {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "Startpage") as!ViewController
self.present(nextViewController, animated: true, completion: nil)
}
There's a couple of ways you can do this. If you really want to change the initial view controller, you would want to NOT set an initial view controller in your storyboard, then in your app delegate's application(_:didFinishLaunchingWithOptions:) implementation, you would create a new Window object and set whichever view controller on it you want to present as the rootViewController. Then, you would call makeKeyAndVisible on that window object. Note that if you do it this way, you'll have to separately handle the case when they log out if you want to display your login window again. In that case you would just do the same thing again: make a new window object with your new ViewController object as the rootViewController and present it.
Another option is to check if they are logged in in your initial view controller's viewDidLoad method and then present your login screen if they aren't. This is what I do in one of applications where the app needs some data, either by logging into an account or manually adding it, before it can do anything.
EDIT:
Here's what my viewDidLoad, etc. looks like (note that mine project is in Objective-C, so I'm just kinda guessing without actually testing it what the correct Swift syntax is. You might need to make some adjustments) You have to dispatch the present call to the main queue because in viewDidLoad you (probably) don't have everything in order yet to actually present a new view controller (I did this quite a long time ago, so I don't recall exactly why it has to be dispatched, but because of the fact that we're already in the process of presenting the current view controller, it makes sense that you wouldn't be able to present another one at the same time. Maybe someone else can weigh in on this, because I really don't remember anymore.):
override func viewDidLoad() {
super.viewDidLoad()
if (!userLoggedIn) {
showLoginScreen()
}
}
func showLoginScreen() {
let loginViewController = storyboard?.instantiateViewController(withIdentifier: "Startpage") as! ViewController
DispatchQueue.main.async {
present(loginViewController, animated: true, completion: nil)
}
}
You can use this line of codes.
Keep in mind that you should add storyboard reference with identifier
named respectively for your need - goToLogin - in my case.
Hope It'll be helpful for anyone.
override func viewDidLoad() {
super.viewDidLoad()
Auth.auth().addStateDidChangeListener { auth, user in
if let user = user {
// User is signed in.
print("user signed in")
//Add the rest of the code here because after passig the caluses
// viewdidload will call another funxtions to it can crash
} else {
// User not signed in
self.performSegue(withIdentifier: "goToLogin", sender: Any?.self)
}
}
}

Check if user has opened every viewcontroller

I would like to know, is it possible to check if user has opened every viewcontroller that application has?
I would like to do it because I give user badges and it is the one I would like to give.
I assume I have to store something into userDefaults and somehow gather the info and then do what I want to do, am I right? If I am right then should I do some global variable and add count every time user opens new viewcontroller?
Any info is appreciated.
Make an option set to represent every viewController. In each viewControllers ViewDidAppear, read and update a field from Userdefaults that stores the option set of displayed viewControllers then write it back to Userdefaults.
struct UserDefaultsKey {
static let displayedViewControllers = "displayedViewControllers"
}
struct DisplayedViewControllers: OptionSet {
let rawValue: Int
static let vc1 = DisplayedViewControllers(rawValue: 1 << 0)
static let vc2 = DisplayedViewControllers(rawValue: 1 << 1)
static let vc3 = DisplayedViewControllers(rawValue: 1 << 2)
static let vc4 = DisplayedViewControllers(rawValue: 1 << 3)
static let all = [vc1, vc2, vc3, vc4]
}
class vc1: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
var displayedViewControllers = DisplayedViewControllers(rawValue: UserDefaults.standard.integer(forKey: UserDefaultsKey.displayedViewControllers))
displayedViewControllers.insert(.vc1)
UserDefaults.standard.set(displayedViewControllers.rawValue, forKey: UserDefaultsKey.displayedViewControllers)
}
}
func haveAllViewControllersBeenDisplayed() -> Bool {
let displayedViewControllers = DisplayedViewControllers(rawValue: UserDefaults.standard.integer(forKey: UserDefaultsKey.displayedViewControllers))
for controller in DisplayedViewControllers.all {
if displayedViewControllers.contains(controller) == false {
return false
}
}
return true
}
You can do it in this way, if you are using UINavigationController then at the end of every UINavigationController Stack set a true key in UserDefaul like this
UserDefaults.standard.set(true, forKey: "NavigationStack1")
Now let us suppose your App has 4 diffrent type of Navigations then you can set those like this, with diffrent key
UserDefaults.standard.set(true, forKey: "NavigationStack1")
UserDefaults.standard.set(true, forKey: "NavigationStack2")
UserDefaults.standard.set(true, forKey: "NavigationStack3")
UserDefaults.standard.set(true, forKey: "NavigationStack4")
Then at the end of every UINavigationController's Stack you need to check whether user has visited all the Navigations like this
if UserDefaults.standard.bool(forKey: "NavigationStack1")&&UserDefaults.standard.bool(forKey: "NavigationStack2")&&UserDefaults.standard.bool(forKey: "NavigationStack3")&&UserDefaults.standard.bool(forKey: "NavigationStack4"){
// Give Badge to user
}
Also you can do it for each UIViewController, in the viewDidLoad of each controller set the key for that viewController to true then, check the result of all the key, in this way you will be able to know whether user has visited all the UIViewController of your app.
Assume you have three ViewControllers: ViewController1, ViewController2, ViewController3
Method 1: Array of ViewController names in the NSUserDefaults:
Maintain a Set of opened ViewController Names: (The Set can be serialized/deserialized to NSUserDefaults)
var openedViewControllers = Set<String>()
Once viewController1 has been opened, you insert it to the set.
openedViewControllers.insert(viewController1Name)
How to check if all viewController were opened:
if openedViewController.count == 3{
//All three viewControllers were opened
}
Method 2: Use Bit Masking: (will be save as normal UInt64)
You use an UInt64 = 0 and every view controller will be mapped to a bit of Int64.
Once you open that view controller you changed the corresponding bit from 0 to 1.
Example:
ViewController1 (opened), ViewController2(never opened), ViewController3(opened) => BitMask will be 1010000....
How to check if all viewController were opened:
if BitMask == 3{
//All three viewControllers were opened
}
N.B. With the second approach, you can only have 64 ViewControllers in you app
You could save an array of Bool in CoreData with the list of the View Controller name. And check it every time a ViewController is open.
You can also use UserDefaults.standard.setValue and stock your Dictionary or Array.
Hope it helps!

How to move from one view controller to another using code on a specific condition in Swift?

In my view controller, upon a specific if condition, I want to move automatically to the next view controller. I tried this code. But it won't work. Please help.
if rightCounter == 5 {
var Level2 = self.storyboard?.instantiateViewControllerWithIdentifier("Level2") as? ViewController
self.presentViewController(Level2!, animated: true, completion: nil)
}
}
if condition == true {
//use either VC Name associate or the Storyboard restoration id
//replace this with yours
if let Level2 = self.storyboard!.instantiateViewControllerWithIdentifier("MainVC") as? UIViewController
{
self.presentViewController(Level2, animated: true, completion: nil)
}
}
I think the problem is with you trying to cast the another VC to ViewController which i presume is the one that you are trying to segue from.
Make sure to have the storyboard ID and you can either type case to UIViewController to be broad or your specific VC to be specific.
Hope it helps.
If you want automatic execution of the conditional statement posted, you could put the code in a property observer for rightCounter.
So where rightCounter is declared change its declaration to include a property observer.
Example could be something like-
var rightCounter: Int = 0 {
didSet {
if rightCounter == 5 {
var Level2 = self.storyboard?.instantiateViewControllerWithIdentifier("Level2") as? ViewController
self.presentViewController(Level2!, animated: true, completion: nil)
}
}
}
With the above code didSet will be called every time rightCounter is set, even if set to the same value as to what it currently is.

Retrieve var value from another class

i have those two classes and i want to retrieve a value from one to another:
class SearchUserViewController:UIViewController{
var selectedUser: String!
#IBAction func btn_accept(sender: AnyObject) {
selectedUser = "Norolimba"
self.dismissViewControllerAnimated(true, completion: nil)
}
}
I'm saving the value to "selectedUser" var, then i want to check the value from this class:
class CalendarViewController: UIViewController {
override func viewDidAppear(animated: Bool) {
let vc : SearchUserViewController! = self.storyboard.instantiateViewControllerWithIdentifier("searchView") as SearchUserViewController
println("\(vc.selectedUser)")
if vc.selectedUser == nil {
self.requestData("team")
}else{
self.requestData("user")
}
}
}
But when i println the value "vc.selectedUser" the value is nil. So what can i do here to catch it from the other class and don't get a nil value?
searchView is here:
Hope you can help me.
Thanks
When you use instantiateViewControllerWithIdentifier(), you're not accessing the view controller that was being displayed on the screen, nor are you accessing the controller that has potentially been automatically instantiated by Interface Builder.
What you're doing is instantiating (hence the name) a new instance of that controller. So the instance variable selectedUser of that new instance is going to be nil.
What you should do is probably provide a callback to your SearchUserViewController when you display it, so that it can notify the view that presented it when a user is picked.
Alternatively, you can use the parentViewController property of UIViewController in cases where you (the view controller) are being presented modally to access the view controller that presented you. So in your SearchUserViewController, when it's being dismissed, it can access self.parentViewController, which should be a CalendarViewController, and call a method or set a property.
(But for the modal controller to assume who its parent is, is a bit of a code smell. I recommend using a callback or delegate of some sort.)
Edit: An example of using the completion callback:
class CalendarViewController : UIViewController {
public func displayUserSelectionController() {
let suvc : SearchUserViewController = ... (how are you displaying it?) ...
self.presentViewController(suvc, animated:true, completion: {
// The SUVC is hiding
if let user = suvc.selectedUser {
self.requestData("team")
} else {
self.requestData("user")
}
})
}
...
}

Resources