Trying to update a field in ObservedRealmObject that depends on an Enum

I read the answers to the question asked by TomF about … How to update an @ObservedRealmObject in a SwiftUI view. My question is quite similar except for the fact that the field value is drawn from an Enum. The object of the exercise is to change the value in the field(itemStatus) by clicking on the field.
I have run this basic code as a standalone view and it works without problems leading me to believe the problem is caused by my use of the ObservedRealmObject.
I am hoping that someone will be able to show me how to update the value of itemStatus in the View and in the DB(.realm).

Here is the View…

import SwiftUI
import RealmSwift

struct InspItemStatusView: View {
    @ObservedObject var rlmMgr = RealmManager()
    @ObservedRealmObject var item:InspItem
    
       
    
    var body: some View {
           VStack(alignment:.leading) {
                var newStatus:ItemStatusEnum = .na
                Button {
                    
                    if item.itemStatus.index < ItemStatusEnum.allCases.count - 1 {
                        //item.itemStatus =
                        newStatus = ItemStatusEnum.allCases[item.itemStatus.index + 1]
                        
                    } else {
                        //item.itemStatus =
                        newStatus = ItemStatusEnum.allCases[0]
                    }

                    updateItemStatus(newValue: newStatus.rawValue)
                } label: {
                    Text(item.thaw()!.itemStatus.rawValue)
                }
                .minimumScaleFactor(0.5)
                .buttonStyle(.plain)
                .fontWeight(.semibold)
                
                .foregroundColor(
                    item.itemStatus.rawValue == "N/A" ? .secondary :item.itemStatus.rawValue == "Rec" ? .orange : item.itemStatus.rawValue == "IP" ?.red : .green)
            }//Vstack
        //}
    }
    
    func updateItemStatus(newValue:String){
        do {
            try Realm().write() {
                guard let thawedItem = item.thaw() else {
                    print("Unable to thaw item")
                    return
                }
                
                print("ItemStatus Update hit")
                // thawedItem.itemStatus = newValue) ?? ItemStatusEnum.na
            }
        } catch {
            print("Failed to save Item: \(error.localizedDescription)")
        }
    }
}

Based on the previous example (TomF), I applied the thaw to the item and it worked as evidenced by the execution of the print statement; “ItemStatus Update hit”

The InspItem object and Enum…

import Foundation
import RealmSwift

public enum ItemStatusEnum: String, PersistableEnum, CaseIterable{
       
    case na = "N/A"
    case recorded = "Rec"
    case ip = "IP"
    case closed = "Closed"
    
    
    var index: Int {
        ItemStatusEnum.allCases.firstIndex(where: {$0 == self}) ?? 1
    }
}

public enum ItemTypeEnum: String, PersistableEnum, CaseIterable {
    case O = "Observation"
    case D = "Deficiency"
    case R = "Recommendation"
}

final class InspItem : Object, ObjectKeyIdentifiable {
    
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted(indexed: true) var inspId:Int = 0
    @Persisted(indexed: true) var itemId:Int = 0
   
    @Persisted var itemType : String =  "" //O,D,R
   // @Persisted var itemStatus : String = "Recorded" //"IP", "Closed" //<==First Try
    @Persisted var itemStatus : ItemStatusEnum = ItemStatusEnum.recorded //"IP", "Closed"
    @Persisted var itemDescript : String =  ""
    @Persisted var photos = RealmSwift.List<Photo>()
    
    @Persisted(originProperty: "items") var inspection:LinkingObjects<Inspection> //backlink
    
    //convenience init(inspId:Int ,itemId:Int,  iType:String, iStat:String, iDescript:String) {
    convenience init(inspId:Int ,itemId:Int,  iType:String, iStat:ItemStatusEnum, iDescript:String) {
        self.init()
        self.inspId = inspId
        self.itemId = itemId
        self.itemType = iType
        self.itemStatus = iStat
        self.itemDescript = iDescript
        
        
    }
}

At this point, I am not completely sure on how to update the value of itemStatus. I presume that the code that I have will save and display the updated value, but if not, please show me what to do.

Thanks
KenT

Hi Ken, I don’t see the issue her. I tested your code and it is working, when I change the status of the Enum, the view changes. Can you please how are you initialising the InspItemStatusView, maybe it will give a hint why this is happening.

1 Like

Sorry, but I’m not quite sure what you are asking for. Are you asking about the view that calls this view?

Can we investigate that line? What does the console output look like if it’s changed to

print("thawed before = \(thawedItem.itemStatus) and newValue = \(newValue)")
thawedItem.itemStatus = newValue) ?? ItemStatusEnum.na
print("thawed after  = \(thawedItem.itemStatus)")

It may not reveal anything but need to establish that vars are what you expect them to be

I have been trying different tacks to solve this since I originally submitted it. At this point I am getting an Initializer error

This is the enum that I was using at that time…

public enum ItemStatusEnum: String, PersistableEnum, CaseIterable{
       
    case na = "N/A"
    case recorded = "Rec"
    case ip = "IP"
    case closed = "Closed"
    
    var asString: String {
            self.rawValue
        }
    
    var index: Int {
        ItemStatusEnum.allCases.firstIndex(where: {$0 == self}) ?? 1
    }
}

I have been building standalone app to test the view in isolation and have developed an Enum that is working better but still has problems.

import SwiftUI
import RealmSwift

final class InspItem : Object, ObjectKeyIdentifiable {
    
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted(indexed: true) var inspId:Int
    @Persisted(indexed: true) var itemId:Int
   
    @Persisted var itemType : String  //O,D,R
    @Persisted var iStatus : ItemStatus = .recorded //"IP", "Closed"
    @Persisted var itemDescript : String =  ""
    @Persisted var photos = RealmSwift.List<Photo>()
    
        
    enum ItemStatus: Int, PersistableEnum, CaseIterable{
       case na,recorded,ip,closed
        
        var text:String{
            switch self{
            case .na:
                return "N/A"
            case .recorded:
                return "Recorded"
            case .ip:
                return "IP"
            case .closed:
                return "Closed"
            }
        }
        
        var color: Color {
            switch self{
            case .na:
                return .secondary
            case .recorded:
                return .orange
            case .ip:
                return .red
            case .closed:
                return .green
            }
        }
        
        
//        var asString: String {
//                self.rawValue
//            }
        
//        var index: Int {
//            ItemStatusEnum.allCases.firstIndex(where: {$0 == self}) ?? 1
//        }
    }
    
    func increment() -> ItemStatus {
        switch iStatus{
        case .na:
            return .recorded
        case .recorded:
            return .ip
        case .ip:
            return .closed
        case .closed:
            return .na
        }
        
    }
    
    convenience init(inspId:Int ,itemId:Int, iType:String = "0", iDescript:String) {
        self.init()
        self.inspId = inspId
        self.itemId = itemId
        self.itemType = iType
        //self.iStatus = iStat
        self.itemDescript = iDescript
           
    }
}

I will try and get your suggested code to work but my memory said that I did something similar and did get the values that I want to save…possible exception of iStatus. Saving was an issue.

With the new code I am able to Save but without the proper iStatus

Oh - try this. Move the Realm PersisstableEnum outside of the class and move it to the top level of your hierarchy, like the rest of your Realm Models:

import SwiftUI
import RealmSwift

enum ItemStatus: Int, PersistableEnum, CaseIterable {
    case na,recorded,ip,closed

    var text:String{
        switch self{
        case .na:
            return "N/A"
        case .recorded:
            return "Recorded"
        case .ip:
            return "IP"
        case .closed:
            return "Closed"
        }
    }
}

final class InspItem : Object, ObjectKeyIdentifiable {
    @Persisted(primaryKey: true) var _id: ObjectId
1 Like

Thanks for the reply Jay. There are three related parts to the enum and I am assuming that all of them should be moved out of the class.

enum ItemStatus: Int, PersistableEnum, CaseIterable{
       case na,recorded,ip,closed
        
        var text:String{
            switch self{
            case .na:
                return "N/A"
            case .recorded:
                return "Recorded"
            case .ip:
                return "IP"
            case .closed:
                return "Closed"
            }
        }
        
        var color: Color {
            switch self{
            case .na:
                return .secondary
            case .recorded:
                return .orange
            case .ip:
                return .red
            case .closed:
                return .green
            }
        }
        
        
 }

As well as a function to allow the user to update the status.

func increment() -> ItemStatus {
        switch iStatus{
        case .na:
            return .recorded
        case .recorded:
            return .ip
        case .ip:
            return .closed
        case .closed:
            return .na
        }
        
    }

The code in the previous reply that had been commented out was for a previous iteration that converted the value to text and allowed incrementing based in index.
The new code works very well but does save the value in Realm as an int

Often times when you’re finding a managed property not saving, it’s because Realm can’t resolve it so it doesn’t fail, but it’s also not set.

Yes, try moving PersistableEnums to the top level of the app and report back.