Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save stephancasas/38cc9d5aaf0dc990078629a1a36a30b6 to your computer and use it in GitHub Desktop.
Save stephancasas/38cc9d5aaf0dc990078629a1a36a30b6 to your computer and use it in GitHub Desktop.
Syntactically-clean and portable SwiftUI bindings for optional properties on CoreData entities.
//
// NSManagedObject+DynamicBindings.swift
//
// Created by Stephan Casas on 3/31/24.
//
import SwiftUI;
import CoreData;
protocol DynamicBindings: NSManagedObject { }
extension DynamicBindings {
/// Create a SwiftUI binding for a managed object's optional property
/// - Parameters:
/// - key: The keypath to the binding's mutable property.
/// - defaultValue: The default value provided to the binding if no value is present.
func binding<T>(
for key: ReferenceWritableKeyPath<Self, T?>,
defaultValue: T? = nil
) -> Binding<T> {
let defaultValue = defaultValue ?? {
switch T.self {
case is String.Type:
return "" as! T;
case is Int.Type:
return 0 as! T;
case is Bool.Type:
return false as! T;
case is Float.Type:
return Float(0.0) as! T;
case is Double.Type:
return Double(0.0) as! T;
default:
fatalError("No inferred default value declared for type \(T.self). Provide a value to `defaultValue`.");
break
}
}();
return .init(get: {
self[keyPath: key] ?? defaultValue
}, set: {
self[keyPath: key] = $0;
});
}
}
@stephancasas
Copy link
Author

Apply the protocol to your CoreData entity using an extension:

extension Item: DynamicBindings { }

Then use the utility in your SwiftUI views:

struct ItemEditorView: View {

  @ObservedObject private var item: Item;
  
  init(_ item: Item) {
    self.item = item;
  }

  var body: some View {
    TextField("Title", text: self.item.binding(for: \.title))
  }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment