Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to guard initialization of property that may fail

Tags:

swift

optional

My class has a property of type NSURL that is initialized from a string. The string is known at compile time.

For the class to operate appropriately, it must be set to its intended value at initialization (not later), so there is no point in defining it as an optional (implicitly unwrapped or otherwise):

class TestClass: NSObject {

    private let myURL:NSURL

    ...

Assuming that NSURL(string:) (which returns NSURL?) will never fail if passed a valid URL string that is known at compile time, I can do something like this:

    override init() {

        myURL = NSURL(string: "http://www.google.com")!

        super.init()
    }

However, I somehow don't feel comfortable around the forced unwrapping and would like to guard the URL initialization somehow. If I try this:

    guard myURL = NSURL(string: "http://www.google.com") else {
        fatalError()
    }

Value of optional type 'NSURL?' not unwrapped; did you mean to use '!' or '?'?

(Note: there's no way to add a ! or ? anywhere the code above that will fix the error. Conditional unwrapping only happens with guard let... guard var..., and myURL is already defined)

I understand why this fails: Even a successful call to NSURL(string:) is returning the (valid) NSURL wrapped inside an optional NSURL?, so I still need to unwrap it somehow before assigning to myURL (which is non-optional, hence not compatible for assignment as-is).

I can get around this by using an intermediate variable:

    guard let theURL = NSURL(string: "http://www.google.com") else {
        fatalError()
    }

    myURL = theURL

...but this is obviously not elegant at all.

What should I do?

like image 386
Nicolas Miari Avatar asked Jan 15 '16 05:01

Nicolas Miari


People also ask

How do you designate a Failable initializer?

You write a failable initializer by placing a question mark after the init keyword ( init? ). Note: You cannot define a failable and a nonfailable initializer with the same parameter types and names. A failable initializer creates an optional value of the type it initializes.

Has no initializer and is not definitely assigned in the construction?

The error "Property has no initializer and is not definitely assigned in the constructor" occurs when we declare a class property without initializing it. To solve the error, provide an initial value for the class property, e.g. name: string = 'James'; or use a non-null assertion.

How do you initialize an object in Teamspeak?

To initialize an object in TypeScript, we can create an object that matches the properties and types specified in the interface. export interface Category { name: string; description: string; } const category: Category = { name: "My Category", description: "My Description", };


1 Answers

Update Another approach, that doesn't use guard, would be to use a switch, as optionals map to the Optional enum:

init?() {
    switch URL(string: "http://www.google.com") {
    case .none:
        myURL = NSURL()
        return nil
    case let .some(url):
        myURL = url
    }
}

although you'd still get a url local variable.


Original answer

You can declare your initializer as a failable one and return nil in case the url string parsing fails, instead of throwing a fatal error. This will make it more clear to clients your the class that the initializer might fail at some point. You still won't get rid of the guard, though.

init?() {

    guard let url = URL(string: "http:www.google.com") else {
        // need to set a dummy value due to a limitation of the Swift compiler
        myURL = URL()
        return nil
    }
    myURL = url

}

This add a little complexity on the caller side, as it will need to check if the object creation succeeded, but it's the recommended pattern in case the object initializer can fail constructing the object. You'd also need to give up the NSObject inheritance as you cannot override the init with a failable version (init?).

You can find out more details about failable initializers on the Swift blog, Apple's documentation, or this SO question.

like image 122
Cristik Avatar answered Oct 05 '22 04:10

Cristik