1

I have a class that looks like this (simplified):

class GuideViewController: UIViewController, StoreSubscriber {
    var tileRenderer: MKTileOverlayRenderer! // <------ this needs to be set by whoever instantiates this class
    override func viewDidLoad() {
        super.viewDidLoad()
         ...
    }
}

My app uses this GuideViewController class to display many different styles of maps, so the tileRenderer instance variable can have many different values.

I want a compile-time guarantee that tileRenderer will never be nil, instead of using an implicitly-unwrapped optional.

How can I achieve this?

Things I've considered so far but am unsure about

  • Setting tileRenderer in the init() method of the GuideViewController. This was my first instinct by this answer implies that this is not possible, or an antipattern.
  • Setting tileRenderer in viewDidLoad(). This seems to require using an implicitly unwrapped optional which bypasses compile-time checks. Also, I'm under the impression that viewDidLoad() is only called once for the view controller in the lifecycle of the app
  • Manually setting tileRenderer after instantiating the VC. E.g.,

    let vc = self.storyboard?.instantiateViewController(withIdentifier: "GuideViewController")
    vc.tileRenderer = MKTileOverlayRenderer(...) // <----- can I make the compiler force me to write this line?
    navigationController?.pushViewController(vc!, animated: true)
    

Forgive me for asking such a naive question—I'm fairly new to iOS development.

RP-3
  • 684
  • 4
  • 22
  • There's no compile-time tricks that I know of, but there is a run-time check you can add, that `tileRenderer` is non-nil, and that its type is `MKTileOverlayRenderer` or a descendant. – Alexander Oct 30 '18 at 23:01
  • Does the property really need to be an IOU? Why cannot it be set during initialization? – Dávid Pásztor Oct 30 '18 at 23:18
  • @DávidPásztor I'm not sure what an IOU is. By 'set during initialization' do you mean like in my third bullet point, or in a method like `viewWillAppear()` ? – RP-3 Oct 30 '18 at 23:25
  • @DávidPásztor You mean IUO - Implicitly Unwrapped Optional, not IOU - I owe you. – rmaddy Oct 30 '18 at 23:26
  • @rmaddy That makes sense. I actually don't want it to be an IUO. I'd way prefer it to be completely non-optional. Any idea how I can achieve that? – RP-3 Oct 30 '18 at 23:28
  • @DávidPásztor You can't set it during initialisation, since as the OP states in their question, custom initialisers for `UIViewController` subclasses is an anti-pattern; it won't work with a storyboard, for example. – Paulw11 Oct 30 '18 at 23:30

1 Answers1

1

It isn't possible for there to be a compile-time check, since that would require the compiler to completely analyse the flow of your program.

You can't make the property a non-optional (well, you can - see point 4), since that requires a custom initialiser, which doesn't really work with UIViewController subclasses.

You have a few choices:

  1. Use an implicitly unwrapped optional and crash at runtime if it is nil - hopefully the developer will quickly identify their mistake

  2. Check for nil at a suitable point (such as viewWillAppear) and issue a warning to the console, followed by a crash when you try and access the value or call fatalError - This will give the developer more hints as to what they have done wrong

  3. Use an optional and unwrap it before you use it

  4. Use a non-optional and provide a default value

Option 4 may be the best option; Provide a default render that does nothing (or perhaps issues warnings to the console log that it is a default renderer and that the developer needs to provide one).

Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Thanks! I suspected this was the case but didn't know for sure. Why don't we use custom initializers with `UIViewController` subclasses? – RP-3 Oct 30 '18 at 23:38
  • 1
    Because you can't always call a custom initialiser when creating a view controller instance; In your sample snippet, the initialiser is going to be called by the storyboard `instantiateViewController` - That function doesn't know anything about your property or your custom initialiser. – Paulw11 Oct 30 '18 at 23:41