0

I've created a property wrapper that I want to insert some logic into and the "set" value is doing the right thing, but the textfield isn't updating with all uppercase text. Shouldn't the text field be showing all uppercase text or am I misunderstanding how this is working?

Also this is a contrived example, my end goal is to insert a lot more logic into a property wrapper, I'm just using the uppercase example to get it working. I've searched all over the internet and haven't found a working solution.

import SwiftUI
import Combine

struct ContentView: View {
    @StateObject var vm = FormDataViewModel()

    var body: some View {
        Form {
            TextField("Name", text: $vm.name)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

class FormDataViewModel: ObservableObject {
    @Capitalized var name: String = ""
}

@propertyWrapper
public class Capitalized {
    @Published var value: String

    public var wrappedValue: String {
        get { value }
        set { value = newValue.uppercased() } //Printing this shows all caps
    }

    public var projectedValue: AnyPublisher<String, Never> {
        return $value
            .eraseToAnyPublisher()
    }

    public init(wrappedValue: String) {
        value = wrappedValue
    }
}
SN81
  • 323
  • 3
  • 13

2 Answers2

1

SwiftUI watches @Published properties in @StateObject or @ObservedObject and triggers UI update on changes of them.

But it does not go deep inside the ObservableObject. Your FormDataViewModel does not have any @Published properties.


One thing you can do is that simulating what @Published will do on value changes.

class FormDataViewModel: ObservableObject {
    @Capitalized var name: String = ""
    private var nameObserver: AnyCancellable?

    init() {
        nameObserver = _name.$value.sink {_ in
            self.objectWillChange.send()
        }
    }
}

Please try.

OOPer
  • 47,149
  • 6
  • 107
  • 142
  • Thanks so much! This worked. I was under the impression that property wrappers may have been the answer to encapsulating reusable logic but it seems like I'll still need to use backing variables to get publishing to happen. – SN81 Aug 18 '20 at 14:11
1

This can be done with standard @Published that seems simpler and more reliable.

Here is a solution. Tested with Xcode 12 / iOS 14.

class FormDataViewModel: ObservableObject {
    @Published var name: String = "" {
        didSet {
            let capitalized = name.uppercased()
            if name != capitalized {
                name = capitalized
                objectWillChange.send()
            }
        }
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690