Home > Mobile >  Making custom get {} set{} to work like dynamic proxy/shortcut to different objects in Array. (Swift
Making custom get {} set{} to work like dynamic proxy/shortcut to different objects in Array. (Swift

Time:10-01

I'm trying to achieve a two way binding-like functionality.

I have a model with an array of identifiable Items, var selectedID holding a UUID of selected Item, and var proxy which has get{} that looks for an Item inside array by UUID and returns it.

While get{} works well, I can't figure out how to make proxy mutable to change values of selected Item by referring to proxy.

I have tried to implement set{} but nothing works.

import SwiftUI

var words = ["Aaaa", "Bbbb", "Cccc"]

struct Item: Identifiable {
    var id = UUID()
    var word: String
}

class Model: ObservableObject {
    @Published var items: [Item] = [Item(word: "One"), Item(word: "Two"), Item(word: "Three")]
    
    @Published var selectedID: UUID?
    
    var proxy: Item? {
        set {
            // how to set one property of Item?, but not the whole Item here?
        }
        get {
            let index = items.firstIndex(where: { $0.id == selectedID })
            return index != nil ? items[index!] : nil
        }
    }
}

struct ContentView: View {
    @StateObject var model = Model()
    var body: some View {
        VStack {
            // monitoring
            MonitorkVue(model: model)
            //selections
            HStack {
                ForEach(model.items.indices, id:\.hashValue) { i in
                    SelectionVue(item: $model.items[i], model: model)
                }
            }
        }.padding()
    }
}

struct MonitorkVue: View {
    @ObservedObject var model: Model
    var body: some View {
        VStack {
            Text(model.proxy?.word ?? "no proxy")
            
            // 3rd: cant make item change by referring to proxy
            // in order this to work, proxy's set{} need to be implemented somehow..
            Button {
                model.proxy?.word = words.randomElement()!
            } label: {Text("change Proxy")}
        }
    }
}

struct SelectionVue: View {
    @Binding var item: Item
    @ObservedObject var model: Model
    
    var body: some View {
        VStack {
            Text(item.word).padding()
            
            // 1st: making selection
            Button {
                model.selectedID = item.id } label: {Text("SET")
                }.disabled(item.id != model.selectedID ? false : true)
            
            // 2nd: changing item affects proxy,
            // this part works ok
            Button {
                item.word = words.randomElement()!
            }label: {Text("change Item")}
        }
    }
}

Once you SET selection you can randomize Item and proxy will return new values. But how to make it works the other way around when changing module.proxy.word = "Hello" would affect selected Item?

Does anyone knows how to make this two-way shortct? Thank You

CodePudding user response:

Here is a correction and some fix:

struct Item: Identifiable {
    var id = UUID()
    var word: String
}

class Model: ObservableObject {
    
    @Published var items: [Item] = [Item(word: "One"), Item(word: "Two"), Item(word: "Three")]
    
    @Published var selectedID: UUID?
    
    var proxy: Item? {
        
        get {

            let index: Int? = items.firstIndex(where: { value in (selectedID == value.id) })
            
            if let unwrappedIndex: Int = index { return items[unwrappedIndex] } else { return nil }
            
        }
        set(newValue) {

            if let unwrappedItem: Item = newValue {
                
                let index: Int? = items.firstIndex(where: { value in (unwrappedItem.id == value.id) })
                
                if let unwrappedIndex: Int = index { items[unwrappedIndex] = unwrappedItem }
                
            }
  
        }

    }
}
  • Related