ios – Downside with updating Realm Object utilizing Objects DAO in Swift

0
15
ios – Downside with updating Realm Object utilizing Objects DAO in Swift


Hi guys i’ve a selected drawback with updating realm object. I’ve an object ToDo which have properties and ToDoDAO:

public struct ToDo: LocalStorable {
    public let id: String
    public let title: String
    public var image: Icon
    public let picture: Information?
    public let executedDate: Date
    public let executedTime: Date
    public let remindTime: Date?
    public let reminderRepetition: Repetition
    public let tag: String
    public var subtasks: [SubToDo]
    public let isArchived: Bool
    public var isDone: Bool
    
    public init(
        id: String = UUID().uuidString,
        title: String,
        image: Icon,
        picture: Information?,
        executedDate: Date,
        executedTime: Date,
        remindTime: Date?,
        reminderRepetition: Repetition,
        tag: String,
        subtasks: [SubToDo],
        isArchived: Bool = false,
        isDone: Bool = false
    ) {
        self.id = id
        self.title = title
        self.image = image
        self.picture = picture
        self.executedDate = executedDate
        self.executedTime = executedTime
        self.remindTime = remindTime
        self.reminderRepetition = reminderRepetition
        self.tag = tag
        self.subtasks = subtasks
        self.isArchived = isArchived
        self.isDone = isDone
    }
    
    public init(activity: ToDo, isDone: Bool) {
        self.id = activity.id
        self.title = activity.title
        self.image = activity.image
        self.picture = activity.picture
        self.executedDate = activity.executedDate
        self.executedTime = activity.executedTime
        self.remindTime = activity.remindTime
        self.reminderRepetition = activity.reminderRepetition
        self.tag = activity.tag
        self.subtasks = activity.subtasks
        self.isArchived = activity.isArchived
        self.isDone = isDone
    }
    
    public init(from dao: ToDoDAO) {
        self.id = dao.id
        self.title = dao.title
        if let iconDAO = dao.image {
            print("DEBUG - ToDo iconDAO:", iconDAO)
            self.image = Icon(from: iconDAO)
        } else {
            print("DEBUG - ToDo fail")
            self.image = .clipboardIcon
        }
        self.picture = dao.picture
        self.executedDate = dao.executedDate
        self.executedTime = dao.executedTime
        self.remindTime = dao.remindTime
        self.reminderRepetition = Repetition(rawValue: dao.reminderRepetition ?? "") ?? .day by day
        self.tag = dao.tag
        self.subtasks = dao.subtasks.map { SubToDo(from: $0) }
        self.isArchived = dao.isArchived
        self.isDone = dao.isDone
    }
    
    public enum CodingKeys: String, CodingKey {
        case id
        case title
        case image
        case picture
        case executedDate
        case executedTime
        case remindTime
        case reminderRepetition
        case tag
        case subtasks
        case isArchived
        case isDone
    }
}

That is ToDoDAO:

public last class ToDoDAO: RealmSwift.Object, LocalDAOInterface {
    @Continued(primaryKey: true) public var id: String
    @Continued public var title: String
    @Continued public var image: IconDAO?
    @Continued public var picture: Information?
    @Continued public var executedDate: Date
    @Continued public var executedTime: Date
    @Continued public var remindTime: Date?
    @Continued public var reminderRepetition: String
    @Continued public var tag: String
    @Continued public var subtasks: RealmSwift.Checklist
    @Continued public var isArchived: Bool
    @Continued public var isDone: Bool
    
    override public init() {
        tremendous.init()
        self.title = ""
        self.image = IconDAO()
        self.picture = Information()
        self.executedDate = Date()
        self.executedTime = Date()
        self.remindTime = nil
        self.reminderRepetition = ""
        self.tag = Tag.otherEvent.rawValue
        self.subtasks = RealmSwift.Checklist()
        self.isArchived = false
        self.isDone = false
    }
    
    public init(from todo: ToDo) {
        tremendous.init()
        self.id = todo.id
        self.title = todo.title
        self.image = IconDAO(from: todo.image)
        self.picture = todo.picture
        self.executedDate = todo.executedDate
        self.executedTime = todo.executedTime
        self.remindTime = todo.remindTime
        self.reminderRepetition = todo.reminderRepetition.rawValue
        self.tag = todo.tag
        self.subtasks.append(objectsIn: todo.subtasks.map { SubToDoDAO(from: $0) })
        self.isArchived = todo.isArchived
        self.isDone = todo.isDone
    }
}

and that i can not replace two properies: image and subtasks and that is Icon mannequin:

public struct Icon: LocalStorable {
    public let id: String
    public let image: Information
    public let circleColor: Coloration

    public init(id: String = UUID().uuidString, image: Information, circleColor: Coloration) {
        self.id = id
        self.image = image
        self.circleColor = circleColor
    }
    
    public init(from dao: IconDAO) {
        self.id = dao.id
        self.image = dao.image
        self.circleColor = Coloration(hex: dao.circleColor) ?? .accentColor
    }
    
    public init(icon: Icon) {
        self.id = icon.id
        self.image = icon.image
        self.circleColor = icon.circleColor
    }
    
    public enum CodingKeys: String, CodingKey {
        case id
        case image
        case circleColor
    }

    public static let tapIcon       = Icon(image: Icons.faucet?.pngData() ?? Information(), circleColor: .blue.opacity(0.2))
    public static let dishIcon      = Icon(image: Icons.dinner?.pngData() ?? Information(), circleColor: .brown)
    public static let trayIcon      = Icon(image: Icons.tray?.pngData() ?? Information(), circleColor: .blue.opacity(0.2))
    public static let clipboardIcon = Icon(image: Icons.clipboard?.pngData() ?? Information(), circleColor: .yellow)
    public static let photoIcon     = Icon(image: Icons.photograph?.pngData() ?? Information(), circleColor: .blue.opacity(0.1))
    public static let birthdayIcon  = Icon(image: Icons.birthday?.pngData() ?? Information(), circleColor: .yellow.opacity(0.5))
    public static let catIcon       = Icon(image: Icons.cat?.pngData() ?? Information(), circleColor: .inexperienced.opacity(0.6))
    public static let cinemaIcon    = Icon(image: Icons.cinema?.pngData() ?? Information(), circleColor: .pink.opacity(0.5))
    public static let dentist       = Icon(image: Icons.dentist?.pngData() ?? Information(), circleColor: .inexperienced.opacity(0.7))
    public static let canine           = Icon(image: Icons.canine?.pngData() ?? Information(), circleColor: .yellow.opacity(0.7))
    public static let soccer      = Icon(image: Icons.soccer?.pngData() ?? Information(), circleColor: .purple.opacity(0.4))
    public static let studying      = Icon(image: Icons.studying?.pngData() ?? Information(), circleColor: .blue.opacity(0.4))
    public static let roadTrip      = Icon(image: Icons.roadTrip?.pngData() ?? Information(), circleColor: .pink.opacity(0.3))
    public static let working       = Icon(image: Icons.working?.pngData() ?? Information(), circleColor: .pink.opacity(0.4))
    public static let purchasing      = Icon(image: Icons.purchasing?.pngData() ?? Information(), circleColor: .yellow.opacity(0.5))
    public static let trip      = Icon(image: Icons.trip?.pngData() ?? Information(), circleColor: .orange.opacity(0.6))
    
    public static let allIcons: [Icon] = [
        dishIcon,
        clipboardIcon,
        birthdayIcon,
        catIcon,
        cinemaIcon,
        dentist,
        dog,
        football,
        learning,
        roadTrip,
        running,
        shopping,
        vacation
    ]
}

and IconDAO:

public last class IconDAO: RealmSwift.Object, LocalDAOInterface {
    @Continued(primaryKey: true) public var id: String
    @Continued public var image: Information
    @Continued public var circleColor: String
    
    override public init() {
        tremendous.init()
        self.image = Information()
        self.circleColor = ""
    }
    
    public init(from icon: Icon) {
        tremendous.init()
        self.id = icon.id
        self.image = icon.image
        self.circleColor = icon.circleColor.toHex() ?? ""
    }
}

and likewise SubToDo:

public struct SubToDo: LocalStorable {
    public let id: String
    public var title: String
    public var isCompleted: Bool
    
    public init(
        id: String = UUID().uuidString,
        title: String,
        isCompleted: Bool = false
    ) {
        self.id = id
        self.title = title
        self.isCompleted = isCompleted
    }
    
    public init(from dao: SubToDoDAO) {
        self.id = dao.id
        self.title = dao.title
        self.isCompleted = dao.isCompleted
    }
    
    public enum CodingKeys: String, CodingKey {
        case id
        case title
        case isCompleted
    }
}

and DAO:

public last class SubToDoDAO: RealmSwift.Object, LocalDAOInterface {
    @Continued(primaryKey: true) public var id: String
    @Continued public var title: String
    @Continued public var isCompleted: Bool
    
    override public init() {
        tremendous.init()
        self.title = ""
        self.isCompleted = false
    }
    
    public init(from subtodo: SubToDo) {
        tremendous.init()
        self.id = subtodo.id
        self.title = subtodo.title
        self.isCompleted = subtodo.isCompleted
    }
}

i attempt to replace it utilizing func under but it surely did not work, for instance updating ICON: ToDo will get deafult worth which is .clipboardIcon and in Realm Studio it is show nil.
enter image description here
Replace ToDo



    func updateToDo(toDo: ToDo) async throws {
            state = .loading
            
            var updatedToDo = ToDo(
                id: toDo.id,
                title: newTaskTitle,
                image: newTaskSymbol,
                picture: selectedTaskImage?.jpegData(compressionQuality: 0.9),
                executedDate: taskDay,
                executedTime: taskTime,
                remindTime: remindTime,
                reminderRepetition: repetition,
                tag: tag.rawValue,
                subtasks: subtasks
            )
            
            print("DEBUG - todo: ", toDo.image)
            print("DEBUG - updatedToDo: ", updatedToDo.image)
            
            let updates = examine(outdated: toDo, up to date: updatedToDo)
            
            attempt await toDoManager.updateToDo(todo: toDo, information: updates)
            
            state = .loaded
        }

that is additionally examine func:


    public func examine(outdated: any LocalStorable, up to date: any LocalStorable) -> [String: Any] {
        var variations: [String: Any] = [:]
    
        let oldMirror = Mirror(reflecting: outdated)
        let newMirror = Mirror(reflecting: up to date)
    
        for (propertyName, oldValue) in oldMirror.kids {
            guard let propertyName else { proceed }
    
            if let newValue = newMirror.descendant(propertyName) {
                if let oldEnumValue = oldValue as? (any RawRepresentable),
                   let newEnumValue = newValue as? (any RawRepresentable),
                   "(oldEnumValue.rawValue)" != "(newEnumValue.rawValue)" {
                    variations[propertyName] = newEnumValue.rawValue
                } else if "(oldValue)" != "(newValue)" {
                    variations[propertyName] = newValue
                }
            }
        }
    
        return variations
    }

the enjoyable reality is that when i create a ToDo the whole lot works superb.
Create ToDo

func createToDo() async throws {
        state = .loading
        
        let newToDo = ToDo(
            title: newTaskTitle,
            image: newTaskSymbol,
            picture: selectedTaskImage?.jpegData(compressionQuality: 0.9),
            executedDate: taskDay,
            executedTime: taskTime,
            remindTime: remindTime,
            reminderRepetition: repetition,
            tag: tag.rawValue,
            subtasks: subtasks
        )
        
        attempt await toDoManager.createToDo(todo: newToDo)
        
        state = .loaded
    }

that is additionally viewModel the place i’ve features:
View Mannequin

@MainActor
last class AddTaskViewModel: ObservableObject {
    enum State: Equatable {
        case loading
        case loaded
        case error(String)
    }
    
    @Printed non-public(set) var state: State = .loaded
    @Printed non-public(set) var showDivider: Bool = true
    @Printed non-public(set) var selectedPicker: Picker?
    @Printed non-public(set) var selectedTaskImage: UIImage? = nil
    @Printed var newTaskTitle: String = ""
    @Printed var isScrolled: Bool = false
    @Printed var newTaskSymbol: Icon = .clipboardIcon
    @Printed var taskDay: Date = .now
    @Printed var taskTime: Date = .now
    @Printed var remindTime: Date?
    @Printed var repetition: Repetition = .noRepeat
    @Printed var tag: Tag = .all
    @Printed var newSubtaskTitle: String = ""
    @Printed var editSubtaskTextFieldText: String = ""
    @Printed var editingSubtaskIndex: Int? = nil
    @Printed var subtasks: [SubToDo] = []
    @Printed var defaultScrollAnchor: UnitPoint? = .prime
    @Printed var taskImageSelection: PhotosPickerItem? = nil {
        didSet {
            Activity {
                do {
                    attempt await setImage(from: taskImageSelection)
                } catch {
                    handleError(error: error)
                }
            }
        }
    }
    //TODO: add validation to newTaskTitle
   
    @Inject non-public var toDoManager: ToDoManagerInterface
    
    non-public var existingToDo: ToDo?
    
    non-public var isEditing: Binding
    
    var buttonTitle: LocalizedStringKey {
        existingToDo == nil ? "Create Activity" : "Replace Activity"
    }
    
    var isPickerSelected: Binding {
        Binding(
            get: {
                self.selectedPicker != nil },
            set: {
                if !$0 {
                    self.selectedPicker = nil
                }
            }
        )
    }
    
    var selectedDate: String { taskDay.isToday ? "Right this moment" : dateFormatter(dateFormat: .dateWithDots).string(from: taskDay) }
    
    var selectedTime: String { "Time: (dateFormatter(dateFormat: .time).string(from: taskTime))" }
    
    var selectedReminder: String { remindTime != nil ? dateFormatter(dateFormat: .timeWithPeriods).string(from: remindTime ?? Date()) : "No reminder" }
    
    init(isEditing: Binding = .fixed(nil)) {
        if let toDo = existingToDo {
            self.existingToDo = toDo
            self.newTaskTitle = toDo.title
            self.newTaskSymbol = toDo.image
            self.taskDay = toDo.executedDate
            self.taskTime = toDo.executedTime
            self.remindTime = toDo.remindTime
            self.repetition = toDo.reminderRepetition
            if let tag = Tag(rawValue: toDo.tag) {
                self.tag = tag
            }
            self.subtasks = toDo.subtasks
            self.selectedTaskImage = toDo.picture.flatMap { UIImage(information: $0) }
        }
        
        self.isEditing = isEditing
    }
    
    func handleScrollActions(place: Double) {
        showDivider = place >= -80.0
        isScrolled = place < -10
    }
    
     func onPickerSelected(picker: Picker?) {
         if let index = editingSubtaskIndex, selectedPicker == .editSubtask {
             subtasks[index].title = editSubtaskTextFieldText
         }
         
         if picker == .subtask {
            createSubtask()
             print("DEBUG - created Subtask:", subtasks)
        }
        
        onShowOptionToggle()
    }
    
    non-public func onShowOptionToggle() {
        withAnimation(.easy(period: 0.1)) {
            selectedPicker = nil
        }
    }
    
    func handlePickerSelection(_ picker: Picker, subtaskIndex: Int? = nil) {
        withAnimation(.easy(period: 0.7)) {
            selectedPicker = picker
            
            if let index = subtaskIndex {
                editingSubtaskIndex = index
                editSubtaskTextFieldText = subtasks[index].title
            }
        }
    }
    
    typealias Picker = ToDoInterface.Picker
}

extension AddTaskViewModel {
    non-public func setImage(from choice: PhotosPickerItem?) async throws {
        guard let choice else { return }
        
        let information = attempt await choice.loadTransferable(kind: Information.self)
        
        guard let information, let uiImage = UIImage(information: information) else {
            throw AppError.unableToLoadImage
        }
        
        selectedTaskImage = uiImage
    }
    
    func createToDo() async throws {
        state = .loading
        
        let newToDo = ToDo(
            title: newTaskTitle,
            image: newTaskSymbol,
            picture: selectedTaskImage?.jpegData(compressionQuality: 0.9),
            executedDate: taskDay,
            executedTime: taskTime,
            remindTime: remindTime,
            reminderRepetition: repetition,
            tag: tag.rawValue,
            subtasks: subtasks
        )
        
        attempt await toDoManager.createToDo(todo: newToDo)
        
        state = .loaded
    }
    
    func handleError(error: Error) {
        withAnimation {
            if let localizedError = error as? LocalizedError {
                state = .error(localizedError.localizedDescription)
            } else {
                state = .error(AppError.unexpectedError(error.localizedDescription).errorDescription ?? error.localizedDescription)
            }
        }
    }
    
    func handleDismissErrorView() {
        withAnimation {
            state = .loaded
        }
    }
    
    non-public func createSubtask() {
        guard !newSubtaskTitle.isEmpty else { return }
        
        let newSubtask = SubToDo(title: newSubtaskTitle)
        
        subtasks.append(newSubtask)
        
        newSubtaskTitle = ""
    }
}

//MARK: Activity Administration
extension AddTaskViewModel {
    func manageToDo() async throws {
        if let existingToDo = existingToDo {
            attempt await updateToDo(toDo: existingToDo)
            isEditing.wrappedValue = false
        } else {
            attempt await createToDo()
        }
    }
    
    func updateToDo(toDo: ToDo) async throws {
        state = .loading
        
        var updatedToDo = ToDo(
            id: toDo.id,
            title: newTaskTitle,
            image: newTaskSymbol,
            picture: selectedTaskImage?.jpegData(compressionQuality: 0.9),
            executedDate: taskDay,
            executedTime: taskTime,
            remindTime: remindTime,
            reminderRepetition: repetition,
            tag: tag.rawValue,
            subtasks: subtasks
        )
        
        print("DEBUG - todo: ", toDo.image)
        print("DEBUG - updatedToDo: ", updatedToDo.image)
        
        let updates = examine(outdated: toDo, up to date: updatedToDo)
        
        attempt await toDoManager.updateToDo(todo: toDo, information: updates)
        
        state = .loaded
    }
    
    func readActiveToDo(primaryKey: String) async throws {
        self.existingToDo = attempt await toDoManager.readToDo(primaryKey: primaryKey)
        print("DEBUG - learn todo:", existingToDo)
    }
}

If anybody have a thought why is that this not working please assist me…
Possibly I created the objects incorrectly and that is why updating does not work, however the query is why including ToDo works.

LEAVE A REPLY

Please enter your comment!
Please enter your name here