2

I have the following reproduced example in Swift/SwiftUI.

Intended Functionality: List of numbers, where upon deletion of any number, the numbers update and reorganize so that they are in consecutive order.

Example: Suppose we have numbers 1, 2, 3, 4 in a list. When we delete number 2, the list should become 1, 2, 3 instead of staying like 1, 3, 4.

Problem: When @State is used to hold the array of numbers, the code works as expected. However, when an array in @AppStorage is used to hold the array, the view does not seem to update/change the numbers.

So, why can't I use the @AppStorage approach, and how can I fix this?

I know the code looks like a lot, but you can mostly ignore it if you just read the comments. It's just a body with a couple of functions, nothing crazy.

class MyObj: Codable, Identifiable {      //SIMPLE OBJECT TO HOLD A NUMBER
    init(num: Int) {
        self.num = num
    }
    
    var id: UUID = UUID()
    var num: Int
}

struct test2: View {
    
    @State var array: [MyObj] = []                             //USING THIS (@State) WORKS
    //AppStorage("array") var array: [MyObj] = []              //USING THIS (@AppStorage) DOESN’T UPDATE NUMBERS
    
    func minimizeNums() {
        for i in 0..<array.count {         //MAKES NUMBERS IN ORDER/CONSECUTIVE
            array[i].num = i
        }
    }
    
    var body: some View {
        VStack {
            Button("add number object") {
                array.append(MyObj(num: array.count))
            }
            
            List {
                ForEach(array) { obj in
                    Text(String(obj.num))
                }
                .onDelete(perform: { index in         
                    array.remove(atOffsets: index)      //REMOVES NUMBER OBJECT FROM LIST
                    minimizeNums()                //MAKES THE REMAINING OBJECT'S NUMBERS CONSECUTIVE
                })
            }
        }
    }
}

Important: I used the extension from this accepted answer in order to store arrays in @AppStorage. I assume this extension may be contributing to the problem, but I'm not sure how!

Stoic
  • 945
  • 12
  • 22
  • What do you mean by FAILS? What is the error message? – Dris Apr 26 '22 at 08:56
  • @Dris , sorry for the confusion! I edited the question, but i meant that using that line of code instead doesn’t update the numbers like I want it to. No error message. – Stoic Apr 26 '22 at 09:00

1 Answers1

4

This is failing, because you are using a class for your model MyObj. There are multiple reasons for using structs with SwiftUI. Please read Blog entry or any other tutorial or documentation.

[TLDR]: Don´t use classes use structs.

Changing MyObj to:

struct MyObj: Codable, Identifiable {     
    init(num: Int) {
        self.num = num
    }
    
    var id: UUID = UUID()
    var num: Int
}

should work.

burnsi
  • 6,194
  • 13
  • 17
  • 27
  • Incorrect. Structs should be used for Views. Either structs or classes may be used for models. In fact, Classes are used extensively for models in SwiftUI. If you want to use ObservableObject, which is necessary for `@StateObject`, `@ObservedObject` etc. then you HAVE to use a class. For more info see WWDC: https://developer.apple.com/wwdc20/10040 – Daniel Lyons Apr 10 '23 at 16:57
  • Using a class in this context is wrong. You can use them as model and as state holder but that is cumbersome and error prone as you can see in this case. @DanielLyons – burnsi Apr 12 '23 at 17:54