Search code examples
swiftbindingconditional-statementsoption-typeinitializer

instance of a failable init method


This is my code:

struct Book{
    let title: String
    let author: String
    let price: String?
    let pubDate: String?

init?(title: String, author: String, price: String, pubDate: String){
    guard let title = title, let author = author else { // error here
        return nil
    }

    self.title = title
    self.author = author
    self.price = price
    self.pubDate = pubDate
}

}

My error says Initializer for conditional binding must have Option type not 'String' My question is, why do I get this error, if I understand the guard statement correctly, I should only pass in the non-Optional properties before the else, and after the braces, the optional ones, like I've done here.


Solution

  • I would suggest you forego the failable init altogether. Your structure holds a title and author which are not Optional, so make your initializer reflect that. Don't let them try to create a book unless they can provide a title and and author.

    The other two properties are Optional. I suggest you provide default values of nil in the initializer for those, which gives your user the most flexibility in creating a Book:

    struct Book {
        let title: String
        let author: String
        let price: String?
        let pubDate: String?
    
        init(title: String, author: String, price: String? = nil, pubDate: String? = nil) {
            self.title = title
            self.author = author
            self.price = price
            self.pubDate = pubDate
        }
    }
    
    let book1 = Book(title: "The Hitchhiker's Guide to the Galaxy", author: "Douglas Adams")
    let book2 = Book(title: "The Shining", author: "Stephen King", price: "20.00")
    let book3 = Book(title: "Little Women", author: "Louisa May Alcott", price: "15.99", pubDate: "1868")
    let book4 = Book(title: "Harry Potter", author: "J. K. Rowling", pubDate: "1997")
    

    If you really want to be able to pass in nil for anything, but fail if they don't provide a title and an author, then use guard to unwrap title and author, otherwise fail and return nil:

    init?(title: String? = nil, author: String? = nil, price: String? = nil, pubDate: String? = nil) {
        guard let title = title, author = author else { return nil }
        self.title = title
        self.author = author
        self.price = price
        self.pubDate = pubDate
    }
    
    // These all return nil
    let book5 = Book()
    let book6 = Book(title: "War and Peace")
    let book7 = Book(author: "Ray Bradbury")
    let book8 = Book(title: "Pride and Prejudice", price: "40.00", pubDate: "1813")