how to handle the nil value variables
Asked Answered
E

1

0

I have model as below.

struc Info: Decodable {
    var firstName: String?
    var lastName: String?
}

While displaying in tableview cell, what I am doing is as below.

personName.text = "\(personArray[indexPath.row].firstName!) \(personArray[indexPath.row].lastName!)"

Now the app is crashing if I have data in below format

[
    {
        "firstName" : "F 1",
        "lastName" : "L 1"
    },
    {
        "firstName" : "F 2"
    },
    {
        "lastName" : "L 3"
    }
]

The app is crashing saying lastName is nil


Solution 1

The solution for this check for nil & then show name, however I don't want to do the check at run time because that I have to check this for all variables (considering I have model of 25 variables). Below is what I could have done.

var firstName = ""
if (personArray[indexPath.row].firstName == nil) {
    firstName = ""
} else {
    firstName = personArray[indexPath.row].firstName!
}

var lastName = ""
if (personArray[indexPath.row].lastName == nil) {
    lastName = ""
} else {
    lastName = personArray[indexPath.row].lastName!
}

personName.text = "\(firstName) \(lastName)"

Solution 2

I can do the update in the model itself as below.

struc Info: Decodable {
    var firstName: String?
    var lastName: String?

    var firstName2 : String? {
    get {
        if (self.firstName==nil) {
            return ""
        }
        return firstName
    }

    var lastName2 : String? {
    get {
        if (self.lastName==nil) {
            return ""
        }
        return lastName
    }
}

personName.text = "\(personArray[indexPath.row].firstName2!) \(personArray[indexPath.row].lastName2!)"

However I have problem with this also. This way, again I have to create N number of variables again.

Is there any other alternate way where default value will get assigned if that variable is missing in the webservice?

Edmonds answered 28/5, 2018 at 12:28 Comment(4)
Write a custom initializer and use decodeIfPresentGradey
@Gradey : sample link or sample answer would be great...Edmonds
stackoverflow.com/search?q=decodeIfPresentGradey
@Gradey : thank you for the link... Will look into it tomorrow morning...Edmonds
V
0

I would recommend one of two options:

  1. Add computed property to the struct to determine the display name.
  2. Manually decode, providing default values. (And also add a display name property if you want)

Personally, I like option 1. I think it's the most compact and also the easiest to maintain.

Option 1 Example:

struct Info1: Decodable {
    var firstName: String?
    var lastName: String?

    var displayName: String {
        return [self.firstName, self.lastName]
            .compactMap { $0 } // Ignore 'nil'
            .joined(separator: " ") // Combine with a space
    }
}

print(Info1(firstName: "John", lastName: "Smith").displayName)
// Output: "John Smith"

print(Info1(firstName: "John", lastName: nil).displayName)
// Output: "John"

print(Info1(firstName: nil, lastName: "Smith").displayName)
// Output: "Smith"

print(Info1(firstName: nil, lastName: nil).displayName)
// Output: ""

Option 2 Example:

struct Info2: Decodable {
    var firstName: String
    var lastName: String

    enum CodingKeys: String, CodingKey {
        case firstName, lastName
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        self.firstName = try container.decodeIfPresent(String.self, forKey: .firstName) ?? ""
        self.lastName = try container.decodeIfPresent(String.self, forKey: .lastName) ?? ""
    }

    // Optional:
    var displayName: String {
        return [self.firstName, self.lastName]
            .compactMap { $0.isEmpty ? nil : $0 } // Ignore empty strings
            .joined(separator: " ") // Combine with a space
    }

    // TEST:
    init(from dict: [String: Any]) {
        let data = try! JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
        self = try! JSONDecoder().decode(Info2.self, from: data)
    }
}

print(Info2(from: ["firstName": "John", "lastName": "Smith"]).displayName)
// Output: "John Smith"

print(Info2(from: ["lastName": "Smith"]).displayName)
// Output: "Smith"

print(Info2(from: ["firstName": "John"]).displayName)
// Output: "John"

print(Info2(from: [String: Any]()).displayName)
// Output: ""
Verbatim answered 29/5, 2018 at 3:22 Comment(8)
I provided sample data for understanding. However I have inner inner models. Do I need to enum CodingKeys, init(from decoder: Decoder) for all variables? I am asking because I just implement just for 1 and app is crashing for other variables. I have to go with option 2 as 1 will not be easy to implement for all variables.Edmonds
Unfortunately, Decodable (and Encodable) is either fully automatic or fully manual. If you choose my option 2, then you will need to provide CodingKeys cases for all properties and include them in init(from: Decoder).Verbatim
I can't go with option 1 even because I have to create N number of variables. Right now I have issue for image variable, but tomorrow I might have issue for another variable. So option 2 is better is what I feel... For option 1, problem is I can fix only if I get crash, else I won't.Edmonds
Both options have good points and bad points. Use whichever you think will be better or easier for your app. The bad part of option 2 is that you will need to manually decode every value from the JSON Decoder.Verbatim
Yes. Option 2 have more work, but good point is I am using json4swift.com who is giving me this data, so I am safe :). The worst point of Option 1 is we can implement if there is crash. if there is no crash, we don't know which variable will hold nil data.Edmonds
In all of Swift, you never know what optional has nil. That's why Optionals exist. That's also why if let and guard let exist.Verbatim
You should not force unwrap optionals unless you know it is not nil. Since you don't know if it is nil, you should use if let or guard let. That is also why I think option 1 is better. But you can choose the option you like.Verbatim
@FahimParkar - hi my senior what the issue u faced bro, i think option 1 is very usefulSubsidy

© 2022 - 2024 — McMap. All rights reserved.