Search code examples
iosswiftmodeldtounwrap

Issue with unwrapping optionals in my model and DTO


This might be something really easy but I don't understand how to do it: so I have this DTO struct I use to get API data into it and map it to Model struct

my DTO:

struct PetDTO: Codable {
    var id: Int
    var category: CategoryDTO?
    var name: String?
    var photoUrls: [String]?
    var tags: [TagDTO]?
    var status: StatusDTO?
}

public class CategoryDTO: NSObject, Codable {
    var id: Int
    var name: String?
    
    private enum CodingKeys: String, CodingKey {
        case id = "id"
        case name = "name"
    }
    
    required public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
    }
}

public class TagDTO: NSObject, Codable {
    var id: Int
    var name: String?
    
    private enum CodingKeys: String, CodingKey {
        case id = "id"
        case name = "name"
    }
    
    required public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
    }
}

enum StatusDTO: String, Codable {
    case available
    case sold
    case pending
}

And my model:

struct PetDataModel {
    var id: Int
    var category: Category
    var name: String?
    var photoUrls: [String]?
    var tags: [Tags]?
    var status: Status?
    
    init(petDto: PetDTO) {
        self.id = petDto.id
        self.category = Category(categoryDto: petDto.category)
        self.name = petDto.name
        self.photoUrls = petDto.photoUrls
        for tag in petDto.tags ?? [] {
            self.tags = [Tags(tagDTO: tag)] // petDto?.map { Tags(tagDTO: $0) }
        }
        self.status = Status(rawValue: petDto.status?.rawValue)
    }
}

struct Category {
    var id: Int
    var name: String?
    
    init(categoryDto: CategoryDTO) {
        self.id = categoryDto.id
        self.name = categoryDto.name
    }
}

struct Tags {
    var id: Int
    var name: String?
    
    init(tagDTO: TagDTO) {
        self.id = tagDTO.id
        self.name = tagDTO.name
    }
}

enum Status: String, Codable {
    case available
    case sold
    case pending
}

As you can see, the mapping happens in Init of PetDataModel. I have errors on this lines

Please tell me how to fix this without making CategoryDto from PetDTO non optional, I need it to stay optional.


Solution

  • You can make category form your PetDataModel optional too.

    struct PetDataModel {
        var id: Int
        var category: Category?
        var name: String?
        var photoUrls: [String]?
        var tags: [Tags]?
        var status: Status?
        
        init(petDto: PetDTO) {
            self.id = petDto.id
            self.category = Category(categoryDto: petDto.category)
            self.name = petDto.name
            self.photoUrls = petDto.photoUrls
            for tag in petDto.tags ?? [] {
                self.tags = [Tags(tagDTO: tag)] // petDto?.map { Tags(tagDTO: $0) }
            }
            self.status = Status(rawValue: petDto.status?.rawValue)
        }
    }
    

    and make your initializer optional:

    struct Category {
        var id: Int
        var name: String?
        
        init?(categoryDto: CategoryDTO?) {
            guard let categoryDto = categoryDto else{
                return nil
            }
    
            self.id = categoryDto.id
            self.name = categoryDto.name
        }
    }
    

    if you don´t want an optional initializer you can check and assign like this:

    self.category = petDto.category != nil ? Category(categoryDto: petDto.category!) : nil