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.
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.