Mutaeb Alqahtani
Mutaeb Alqahtani

Reputation: 354

Optional class initializer parameter in class initializer in Swift

I have a class init that is named User, and the class User contains some parameters. Now in the Offer class, you can see that I pass the User class as a param, but I want to have it as optional instead. Is there a way to have an optional class in your params? Thank you

struct User {
    let uid: String
    let username: String

    init(uid: String, dictionary: [String: Any]) {
        self.uid = uid
        self.username = dictionary["username"] as? String ?? ""
    }
}

struct Offer {
    let user: User
    let caption: String
    let imageURL: String
    let creationDate: Date

    init(user: User, dictionary: [String: Any]) {
        self.user = user
        self.caption = dictionary["caption"] as? String ?? ""
        self.imageURL = dictionary["image_url"] as? String ?? ""

        let secondsFrom1970 = dictionary["creation_date"] as? Double ?? 0
        self.creationDate = Date(timeIntervalSince1970: secondsFrom1970)
    }
}

Upvotes: 0

Views: 457

Answers (2)

Alexander
Alexander

Reputation: 63137

You code is muddling two distinct things: initialization of an object with a set of values for its members, and extracting those members from a dictionary. Just write two seperate initializers:

import Foundation

struct User {
    let uid: String
    let username: String

    /*
    // Due to the absence of an explicit initializer declaration,
    // the compiler will synthesize an implicit member-wise initailizer like this:
    init(uid: String, username: String) {
        self.uid = uid
        self.username = username
    }
    */
}

extension User {
    // Putting this initializer in an extension preserves he member-wise intializer
    init?(fromDict dict: [String: Any]) {
        guard let uid = dict["uid"] as? String,
              let username = dict["username"]  as? String
        else { return nil }

        self.init(uid: uid, username: username)
    }
}

struct Offer {
    let user: User
    let caption: String
    let imageURL: String
    let creationDate: Date

    /*
    // Due to the absence of an explicit initializer declaration,
    // the compiler will synthesize an implicit member-wise initailizer like this:
    init(
        user: User,
        caption: String,
        imageURL: String,
        creationDate: Date
    ) {
        self.user = user
        self.caption = caption
        self.imageURL = imageURL
        self.creationDate = creationDate
    }
    */
}

extension Offer {
    // Putting this initializer in an extension preserves he member-wise intializer
    init?(fromDict dict: [String: Any]) {
        guard let user = dict["user"] as? User,
              let caption = dict["caption"] as? String,
              let imageURL = dict["image_url"] as? String,
              let secondsFrom1970 = dict["creation_date"] as? Double
        else { return nil }

        self.init(
            user: user,
            caption: caption,
            imageURL: imageURL,
            creationDate: Date(timeIntervalSince1970: secondsFrom1970)
        )
    }
}

Some notes:

  1. Using the nil-coalescing operator (??) to provide non-sensical default values in the case of nil is really bad practice. It hides failures and silently introduces data integrity issues; don't do it.
  2. String is not an appropriate type for a member called imageURL. Use URL.
  3. If these dicts are coming from JSON, use the Codable protocol to automate all of this boilerplate code.
  4. String is a poor type for an ID, primarily because it's really slow, in comparison to more suitable types like UUID and Int. This is particularly true in most databases, where textual comparison is much slower than Int/UUID comparison.

Upvotes: 1

Prashant Tukadiya
Prashant Tukadiya

Reputation: 16416

First of all this is not class this is struct and they both are different.

you can easily create optional argument like below. You also need to mark user property as optional as well.

struct Offer {
    let user: User?
    let caption: String
    let imageURL: String
    let creationDate: Date

    init(user: User? = nil, dictionary: [String: Any]) {
        self.user = user
        self.caption = dictionary["caption"] as? String ?? ""
        self.imageURL = dictionary["image_url"] as? String ?? ""

        let secondsFrom1970 = dictionary["creation_date"] as? Double ?? 0
        self.creationDate = Date(timeIntervalSince1970: secondsFrom1970)
    }
}

Upvotes: 0

Related Questions