0

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)
    }
}

1
  • Yes , make both the property user and the corresponding parameter optional Commented Jul 11, 2019 at 4:49

2 Answers 2

1

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.
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you, and you really know what you are doing! I really appreciate it.
One problem I found. In the User Struct, I have to have a uid param and for the Offer Struct I have to have the User param
@MutaebAlqahtani Why do you "have" to have those params? What does the calling code look like?
0

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)
    }
}

4 Comments

Thank you, but it still there is a problem when I tried so before. I get this error which says (Missing argument for parameter 'user' in call). I am trying to tall it like this Offer(dictionary: [String: Any]). Without the user param!
@MutaebAlqahtani Just see the edit you can use default argument init here
@MutaebAlqahtani Why you have removed correct mark from my answer ?, if you still face any problem you can let me know
I did not remove anything. please vote for my question

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.