如何使自定义属性包装器可绑定(可能使用@State)?

Yul*_*lky 1 state properties swift swiftui property-wrapper

我创建了一个自定义属性包装器,它作为其 setter 和 getter 从文件中写入和读取。

@propertyWrapper struct Specifier<Value> {
    let key: String
    let defaultValue: Value
    let plistPath: String
    
    var prefs: NSDictionary {
        NSDictionary(contentsOfFile: plistPath) ?? NSDictionary()
    }

    var wrappedValue: Value {
        get {
            return prefs.value(forKey: key) as? Value ?? defaultValue
        }
        set {
            prefs.setValue(newValue, forKey: key)
            prefs.write(toFile: plistPath, atomically: true)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我现在想在设置视图中使用它,如下所示:

struct PrefsView: View {
    @Specifier<Bool>(key: "enable", defaultValue: true, plistPath: "/Library/path/to/Prefs.plist") private var enable

    var body: some View {
        Form {
            Toggle("Enable", isOn: $enable)
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

Cannot find '$enable' in scope
Run Code Online (Sandbox Code Playgroud)

我如何才能实现写入和读取文件以设置和获取变量的效果,同时又允许它绑定到一个值?

注意:我的项目不需要“App Store Legal”

paw*_*222 6

您的属性包装器返回Bool,而Binding<Bool>不是Toggle.

您可以指定projectedValue(如 NewDev 建议) - 这将允许您使用Binding以下命令访问$

@propertyWrapper struct Specifier<Value> {
    ...

    var wrappedValue: Value {
        get {
            projectedValue.wrappedValue
        }
        set {
            projectedValue.wrappedValue = newValue
        }
    }
    
    var projectedValue: Binding<Value> {
        .init(get: {
            prefs.value(forKey: key) as? Value ?? defaultValue
        }, set: {
            prefs.setValue($0, forKey: key)
            prefs.write(toFile: plistPath, atomically: true)
        })
    }
}

struct ContentView: View {
    @Specifier(key: "enable", defaultValue: true, plistPath: "/Library/path/to/Prefs.plist") private var enable

    var body: some View {
        Form {
            Toggle("Enable", isOn: $enable)
            Text(String(enable))
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以将 的返回类型更改wrappedValueBinding<Bool>

@propertyWrapper struct Specifier<Value> {
    ...

    var wrappedValue: Binding<Value> {
        .init(get: {
            prefs.value(forKey: key) as? Value ?? defaultValue
        }, set: {
            prefs.setValue($0, forKey: key)
            prefs.write(toFile: plistPath, atomically: true)
        })
    }
}

struct ContentView: View {
    @Specifier(key: "enable", defaultValue: true, plistPath: "/Library/path/to/Prefs.plist") private var enable

    var body: some View {
        Form {
            Toggle("Enable", isOn: enable) // access as a Binding
            Text(String(enable.wrappedValue)) // access as a Bool
        }
    }
}
Run Code Online (Sandbox Code Playgroud)