1

I found myself using a lot of implicitly unwrapped optionals when initialiser injection would not work or when creating mvvm modules for examples:

class TodoView: UIViewController {
    
    var viewModel: TodoViewModelProtocol!

}

Not only it doesn't look so good, but its also a pain to always force unwrap if I need to use a switch statement on an explicitly unwrapped optional variable for example.

Is there any way to get rid of the implicitly unwrapped optional, for example using @properyWrapper in swift 5?

  • 1
    Switch to SwiftUI where dependency injection is a viable option. Also, if you don't use Storyboards, you can use dependency injection for `UIViewController` subclasses. However, for `UIView`s there's no way to create custom initialisers and hence use dependency injection properly. – Dávid Pásztor May 07 '21 at 11:48
  • 1
    How do you assign the property in your example then? – Joakim Danielson May 07 '21 at 11:50
  • Thank you for your coments! swiftUI looks very promising, I will check it! at the moment i am assigning each property using a "builder" class. –  May 07 '21 at 14:04

1 Answers1

1

You can simulate implicitly unwrapped optionals with a property wrapper as follows:

@propertyWrapper
struct MaybeUninitialized<T> {
    private var storage: T?
    var wrappedValue: T {
    get { storage! }
    set { storage = newValue}
    }
}

Then you can even use possibly uninitialized fields storing optionals without accidentially unwrapping the optional. Something like this:

@MaybeUninitialized var x: Int?

print(x) // will crash
x = nil
print(x) // print nil
  • Thank you so much, its exactly what i was looking for and i think it's very good! How can i handle the case where a variable needs to be weak? –  May 07 '21 at 14:03
  • 1
    How about creating a variant of the property wrapper (`struct WeakMaybeUninitialized`) that uses `private weak var storage: T?` instead? This has the nice side-effect that the optional needed for the weak field is also automatically unwrapped. – Tobias Tebbi May 07 '21 at 21:14
  • thank you very much, I tried like you said but when I apply it to a protocol, I get the error "Property type 'SomeProtocol does not match that of the 'wrappedValue' property of its wrapper type 'WeakMaybeUninitialized'" even tho SomeProtocol conforms to AnyObject. Do you have any idea what is happening? that would be very helpful! –  May 08 '21 at 08:07
  • for example if i have protocol SomeProtocol: AnyObject {} this works: weak var test: SomeProtocol? but this doesnt work and returns error above: @ WeakMaybeUninitialized var test: SomeProtocol –  May 08 '21 at 08:14
  • 1
    Using a protocol as a type results in a type that doesn't conform to any protocols, including AnyObject, unfortunately. So the only way I'm aware of to make this work with protocol types is to use dynamic casting: `@propertyWrapper struct WeakMaybeUninitialized { private weak var storage: AnyObject? = nil var wrappedValue: T { get { storage! as! T } set { storage = newValue as! AnyObject} } }` – Tobias Tebbi May 08 '21 at 13:06
  • Thank you so much for your help, it works as you said! But I don't understand exactly what do you mean exactly by "Using a protocol as a type results in a type that doesn't conform to any protocols, including AnyObject" ? that would be great if you clarify! Anyways I set your answer as the accepted, because its perfect! –  May 08 '21 at 13:14
  • Given a protocol P, declaring a variable of type P results in this variable having a protocol type. Such a protocol type doesn't conform to any protocols, not even P. This is a very confusing aspect of Swift's type system, see for example this discussion for more details: https://forums.swift.org/t/very-confused-about-some-compiler-warnings-related-to-protocols-bugs-or-intended/39754/5 – Tobias Tebbi May 09 '21 at 14:59