Adding @Model (SwiftData) causes error 'does not conform to Decodable/Encodable'

29 views Asked by At

I have a class called Resource that maps across to some fairly complex JSON structure that I receive from the API. It has some nested properties and objects that I defined within my class. Everything is working as expected in that I get the JSON back and it gets decoded successfully and I can use all of my properties in my UI, such as resource.name, resource.timezone, etc.

I am now trying to implement SwiftData and as soon as I add the @Model macro to my class file, I immediately get two errors: Type 'Resource' does not conform to protocol 'Decodable' and Type 'Resource' does not conform to protocol 'Encodable'

I imagine this is something to do with an init(from:) not being in my class, but the compiler cannot synthesize the init(from: automatically and I'm stumped. I can't wrap my head around how it can be decoded successfully but now has a problem? Unless I'm fundamentally misunderstanding something.

This is my Resource class:

import Foundation
import SwiftData

@Model
class Resource: Codable, Identifiable {
    var id: Int
    var creatorId: Int?
    var archived: Bool?
    var bookable: Bool?
    var email: String?
    var jobTitle: String?
    var notes: String?
    var color: String
    var name: String
    var firstName: String?
    var lastName: String?
    var image: String?
    var images: Images?
    var account: RGAccount
    var phone: String?
    var human: Bool
    var minutesPerDay: Int?
    var createdAt: String
    var updatedAt: String
    var lastUpdatedBy: Int?
    var url: String?
    var resourceType: ResourceType
    var timezone: Timezone
    var vacationAllowance: Int?
    var userId: Int?
    var role: String?
    var permissions: Permissions?
    var availablePeriods: [AvailablePeriod]?
    var customAttributes: [String: String]?
    var customAvailablePeriods: [CustomAvailablePeriod]?
    var selectedCustomFieldOptions: [CustomFieldOption]?
    var bookingApproverIds: [Int]?
    var bookedClientIds: [Int]?
    var bookedProjectIds: [Int]?
    var overtimes: [Overtime]?
    var historicalTimezones: [HistoricalTimezone]?
    
    enum CodingKeys: String, CodingKey {
        case id, archived, bookable, email, notes, color, name, image, images, account, phone, human, url, timezone, overtimes, role, permissions
        case creatorId = "creator_id"
        case jobTitle = "job_title"
        case firstName = "first_name"
        case lastName = "last_name"
        case minutesPerDay = "minutes_per_day"
        case createdAt = "created_at"
        case updatedAt = "updated_at"
        case lastUpdatedBy = "last_updated_by"
        case resourceType = "resource_type"
        case vacationAllowance = "vacation_allowance"
        case userId = "user_id"
        case availablePeriods = "available_periods"
        case customAttributes = "custom_attributes"
        case customAvailablePeriods = "custom_available_periods"
        case selectedCustomFieldOptions = "selected_custom_field_options"
        case bookingApproverIds = "booking_approver_ids"
        case bookedClientIds = "booked_client_ids"
        case bookedProjectIds = "booked_project_ids"
        case historicalTimezones = "historical_timezones"
    }


    
    init(id: Int, creatorId: Int, archived: Bool, bookable: Bool, email: String, jobTitle: String, notes: String, color: String, name: String, firstName: String, lastName: String, image: String, images: Images, account: RGAccount, phone: String, human: Bool, minutesPerDay: Int, createdAt: String, updatedAt: String, lastUpdatedBy: Int, url: String, resourceType: ResourceType, timezone: Timezone, vacationAllowance: Int, userId: Int, role: String, permissions: Permissions, availablePeriods: [AvailablePeriod], customAttributes: [String: String], customAvailablePeriods: [CustomAvailablePeriod], selectedCustomFieldOptions: [CustomFieldOption], bookingApproverIds: [Int], bookedClientIds: [Int], bookedProjectIds: [Int], overtimes: [Overtime], historicalTimezones: [HistoricalTimezone]) {
        self.id = id
        self.creatorId = creatorId
        self.archived = archived
        self.bookable = bookable
        self.email = email
        self.jobTitle = jobTitle
        self.notes = notes
        self.color = color
        self.name = name
        self.firstName = firstName
        self.lastName = lastName
        self.image = image
        self.images = images
        self.account = account
        self.phone = phone
        self.human = human
        self.minutesPerDay = minutesPerDay
        self.createdAt = createdAt
        self.updatedAt = updatedAt
        self.lastUpdatedBy = lastUpdatedBy
        self.url = url
        self.resourceType = resourceType
        self.timezone = timezone
        self.vacationAllowance = vacationAllowance
        self.role = role
        self.permissions = permissions
        self.userId = userId
        self.availablePeriods = availablePeriods
        self.customAttributes = customAttributes
        self.customAvailablePeriods = customAvailablePeriods
        self.selectedCustomFieldOptions = selectedCustomFieldOptions
        self.bookingApproverIds = bookingApproverIds
        self.bookedClientIds = bookedClientIds
        self.bookedProjectIds = bookedProjectIds
        self.overtimes = overtimes
        self.historicalTimezones = historicalTimezones
    }
}

class Permissions: Codable {
    var resources: String?
    var bookings: String?
    var projects: String?
    var clients: String?
    var reports: String?
    var downtimes: String?
    
    init(resources: String, bookings: String, projects: String, clients: String, reports: String, downtimes: String) {
        self.resources = resources
        self.bookings = bookings
        self.projects = projects
        self.clients = clients
        self.reports = reports
        self.downtimes = downtimes
    }
}

class HistoricalTimezone: Codable {
    var timezone: String?
    var lastValidDate: String?
    
    enum CodingKeys: String, CodingKey {
        case timezone
        case lastValidDate = "last_valid_date"
    }
    
    init(timezone: String, lastValidDate: String) {
        self.timezone = timezone
        self.lastValidDate = lastValidDate
    }
}

class Overtime: Codable {
    var createdAt: String?
    var creatorId: Int?
    var date: String?
    var duration: Int?
    var id: Int?
    var resourceId: Int?
    var updatedAt: String?
    
    enum CodingKeys: String, CodingKey {
        case date, duration, id
        case createdAt = "created_at"
        case creatorId = "creator_id"
        case resourceId = "resource_id"
        case updatedAt = "updated_at"
    }
    
    init(createdAt: String, creatorId: Int, date: String, duration: Int, id: Int, resourceId: Int, updatedAt: String) {
        self.createdAt = createdAt
        self.creatorId = creatorId
        self.date = date
        self.duration = duration
        self.id = id
        self.resourceId = resourceId
        self.updatedAt = updatedAt
    }
}

class CustomFieldOption: Codable, Identifiable {
    var id: Int
    var name: String
    var value: String
    
    init(id: Int, name: String, value: String) {
        self.id = id
        self.name = name
        self.value = value
    }
}

class CustomAvailablePeriod: Codable {
    var date: String?
    var startTime: Int?
    var endTime: Int?
    
    enum CodingKeys: String, CodingKey {
        case date
        case startTime = "start_time"
        case endTime = "end_time"
    }
    
    init(date: String, startTime: Int, endTime: Int) {
        self.date = date
        self.startTime = startTime
        self.endTime = endTime
    }
}

class CustomAttribute: Codable {
    var phone: String?
    var capacity: String?
    var registrationNumber: String?
    
    enum CodingKeys: String, CodingKey {
        case phone, capacity
        case registrationNumber = "registration_number"
    }
    
    init(phone: String, capacity: String, registrationNumber: String) {
        self.phone = phone
        self.capacity = capacity
        self.registrationNumber = registrationNumber
    }
}

class AvailablePeriod: Codable {
    var weekDay: Int?
    var startTime: Int?
    var endTime: Int?
    var validFrom: String?
    var validUntil: String?
    
    enum CodingKeys: String, CodingKey {
        case weekDay = "week_day"
        case startTime = "start_time"
        case endTime = "end_time"
        case validFrom = "valid_from"
        case validUntil = "valid_until"
    }
    
    init(weekDay: Int, startTime: Int, endTime: Int, validFrom: String, validUntil: String) {
        self.weekDay = weekDay
        self.startTime = startTime
        self.endTime = endTime
        self.validFrom = validFrom
        self.validUntil = validUntil
    }
}

class Images: Codable {
    var defaultImage: String?
    var tiny: String?
    var thumb: String?
    var card: String?
    var header: String?
    var detail: String?
    
    enum CodingKeys: String, CodingKey {
        case tiny, thumb, card, header, detail
        case defaultImage = "default"
    }
    
    init(defaultImage: String, tiny: String, thumb: String, card: String, header: String, detail: String) {
        self.defaultImage = defaultImage
        self.tiny = tiny
        self.thumb = thumb
        self.card = card
        self.header = header
        self.detail = detail
    }
    
}

class RGAccount: Codable {
    var id: Int?
    var name: String?
    var url: String?
    
    init(id: Int, name: String, url: String) {
        self.id = id
        self.name = name
        self.url = url
    }
}

class ResourceType: Codable {
    var id: Int
    var name: String
    var url: String
    
    init(id: Int, name: String, url: String) {
        self.id = id
        self.name = name
        self.url = url
    }
}

class Timezone: Codable {
    var name: String
    var offset: Int
    
    init(name: String, offset: Int) {
        self.name = name
        self.offset = offset
    }
}

I've tried to implement an init(from:) initalizer, but with being relatively new to Swift, I cannot get my head around what the actual issue is so I'm poking in the dark. I was under the impression that using Codable meant that it was essentially Decodable and Encodable conformant and because I had no errors/warning before adding @Model I assumed the code was fine.

In my head, I'm imagining it's likely something to do with my custom class types, or maybe the optionals? But they are all basic data types, String, Int, Bool etc. There's no UIImage or anything that doesn't conform to Codable (that I can see), and no previous errors

0

There are 0 answers