skip to Main Content

I am new to Swift and am building an app to learn. Right now I am making the registration section of the app.
I thought the UX would be better if there were multiple VC’s asking a single question, i.e. one for your name, one for your birthdate, etc as opposed to jamming all that into a single view controller. The final view controller collects all of that information and sends a dictionary as FUser object to be saved on Firebase.

I figured I could instantiate the final view controller on each of the previous five view controllers and pass that data directly to the end. I kept getting errors and figured out that the variables were nil. It works just fine if I pass the data directly to the next view controller but it doesn’t seem to let me send it several view controllers down. Obviously there’s a nuance to how the memory is being managed here that I’m not tracking.

Is there a way to do what I am trying to do or do I have to pass the data through each view controller along the way?

import UIKit

class FirstViewController: UIViewController {

    
    //MARK: - IBOutlets
    @IBOutlet weak var firstNameTextField: UITextField!
    
    //MARK: - ViewLifeCycle
    override func viewDidLoad() {
        super.viewDidLoad()

    }
    
    //MARK: - IBActions
    @IBAction func continueToMiddleViewController(_ sender: Any) {
            
       let vcFinal = storyboard?.instantiateViewController(withIdentifier: 
 "finalVC") as! finalViewController
       vcFinal.firstName = firstNameTextField.text


       let vc = storyboard?.instantiateViewController(withIdentifier: 
 "middleVC") as! middleViewController
       vc.modalPresentationStyle = .fullScreen
       present(vc, animated: false)
        
    }

    ...
    
}
import UIKit

class FinalViewController: UIViewController {

    var firstName: String?
    ...

    //MARK: - ViewLifeCycle
    override func viewDidLoad() {
        super.viewDidLoad()

    }

    ...

}

3

Answers


  1. TL;DR: The fastest one that would solve your problem is creating a singleton

    There are many strategies for this. For a starter, it might be a good idea to read some begginer articles, like this one. I can update this answer if you don’t find it useful, but it’d look just like the article

    Login or Signup to reply.
  2. Viewcontroller‘s variable can’t be initiated until any of the init method is called.

    There are detailed answers on this thread.

    Passing Data between ViewControllers

    Login or Signup to reply.
  3. Another way to approach this problem could be to make use of closures. Note that personally I’ve moved away from using storyboards but I’ll try to explain still. Closures are also referred to as callbacks, blocks, or in some context like here – completions.

    You can declare a closure like let onSubmitInfo: (String?) -> Void below, it stores a reference to a block of code that can be executed at a later stage just like a function and it takes an optional string as a parameter just like a function can.

    The closures are specified in the initialisers where a block of code is passed into the respective classes below and the closures are then called in the IBActions that will trigger the block of code that is defined where the below classes are initialised:

    class First: UIViewController {
        // MARK: - IBOutlets
        @IBOutlet weak var firstNameTextField: UITextField!
    
        // MARK: - Properties
        private let onSubmitInfo: (String?) -> Void
    
        init(onSubmitInfo: (String?) -> Void) {
            self.onSubmitInfo = onSubmitInfo
        }
    
        // MARK: - IBActions
        @IBAction func continue(_ sender: Any) {
            onSubmitInfo(firstNameTextField.text)
        }
    }
    
    class Second: UIViewController {
        // MARK: - IBOutlets
        @IBOutlet weak var lastNameTextField: UITextField!
    
        // MARK: - Properties
        private let onSubmitInfo: (String?) -> Void
    
        init(onSubmitInfo: (String?) -> Void) {
            self.onSubmitInfo = onSubmitInfo
        }
    
        // MARK: - IBActions
        @IBAction func continue(_ sender: Any) {
            onSubmitInfo(lastNameTextField.text)
        }
    }
    

    To manage showing the above views and collecting the values returned by their closures (i.e. onSubmitInfo) we create a FlowController class that will also show the next view when the closure is called.
    In FlowController we define the closures or blocks of code to be executed when it is called inside the IBAction in the respective First and Second classes above.
    The optional string that is provided in the respective First and Second classes is used as the (firstName) and (secondName) closure properties below:

    class FlowController: UIViewController {
        private var fistName: String?
        private var lastName: String?
    
        ...
    
        private func showFirstView() {
            let firstViewController = First(onSubmitInfo: { (firstName) in
                self.firstName = firstName
                showSecondView()
            })
            navigationController?.pushViewController(
                firstViewController, 
                animated: true)
        }
    
        private func showSecondView() {
            let secondViewController = Second(onSubmitInfo: { (lastName) in
                self.lastName = lastName
                showFinalView()
            })
            navigationController?.pushViewController(
                secondViewController, 
                animated: true)
        }
    
        private func showFinalView() {
            let finalViewController = Final(
                firstName: firstName, 
                lastName: lastName)
            navigationController?.pushViewController(
                finalViewController, 
                animated: true)
        }
    }
    

    The FlowController finally shows the Final view controller after it has collected the firstName form the First view controller and the lastName form the Second view controller in the showFinalView function above.

    class Final: UIViewController {
        let firstName: String
        let lastName: String
        ...
    }
    

    I hope this is a shove in the right direction. I have moved away from storyboards because I find creating views in code is more verbose and clear on peer reviews and it was also easier for me to manage constraints and just to manage views in general.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search