0

I'm trying to achieve a seemingly easy thing: initialize constant variable using another one from the same class which is inheriting from UIViewController. I had 2 ideas that worked, but have their problems and don't seem to be the best solution.

Idea 1 - problem: isn't constant

class MyViewController: UIViewController {
    let db = Firestore.firestore()
    let uid: String = UserDefaults.standard.string(forKey: "uid")!
    lazy var userDocRef = db.collection("users").document(uid)
}

Idea 2 - problem: isn't constant and is optional

class MyViewController: UIViewController {
    let db = Firestore.firestore()
    let uid: String = UserDefaults.standard.string(forKey: "uid")!
    var userDocRef: DocumentReference?
    override func viewDidLoad() {
        super.viewDidLoad()
        userDocRef = db.collection("users").document(uid)
    }
}

I think it should be possible to achieve that by overriding init(). I've tried couple of implementations I found Googling, but everyone I've tried gave me some kind of error. Few examples:

From this answer.

convenience init() {
    self.init(nibName:nil, bundle:nil) // error: Argument passed to call that takes no arguments
    userDocRef = db.collection(K.Firestore.usersCollection).document(uid)
}

From this article.

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)! // Property 'self.userDocRef' not initialized at super.init call
}

init() {
   super.init(nibName: nil, bundle: nil) // Property 'self.userDocRef' not initialized at super.init call
   userDocRef = db.collection(K.Firestore.usersCollection).document(uid)
}

I'm guessing I'm either missing something or those are outdated? I'm surprised such a simple task as overriding initializer is such a bother. What is the proper way to do it?

1 Answer 1

2

Your second try is quite close. You've really just missed this rule that you have to follow:

all properties declared in your class must be initialised before calling a super.init.

In your second try, userDocRef is not initialised in init(coder:) and initialised after a super.init call in init().

You should write it like this:

init() {
    userDocRef = db.collection(K.Firestore.usersCollection).document(uid)
    super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
    userDocRef = db.collection(K.Firestore.usersCollection).document(uid)
    super.init(coder: coder)
}

I don't think you can get rid of the duplicate code here... The best you can do is to create a static helper method that returns db.collection(K.Firestore.usersCollection).document(uid), which means making db static as well, which might be worse now that I think about it.

Note that anything from the storyboards will be created using init(coder:), so it is important that you do your initialiser there properly as well if you are using storyboards.

Sign up to request clarification or add additional context in comments.

2 Comments

"it is important that you do your initialiser there properly as well if you are using storyboards" I'm not sure what that means? I am using storyboards. Do I have to initialize elements from storyboard in initializer in code somehow?
@Stormwaker No, I meant that you should implement init(coder:) properly, also initialising the properties that needs to be initialised, like I did in my answer. Unlike what you did, which is to not initialise anything in init(coder:). I was trying to point out that you should actually focus more on init(coder:) because that is the initialiser that is actually called. That aside, does my answer work?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.