I'm trying to use Swift
(which I'm very new at) to create a clean interface for my data models. These models are backed by Firebase
(though this is ultimately irrelevant to the actual question). What I want to avoid is writing tons of setter/accessor boiler plate code over and over.
For example:
class MyData {
let connection: Firebase!
private var _name: String? // internal storage, like an iVar in ObjC
init(connection: Firebase!) {
self.connection = connection
self.connection.observeEventType(.Value, withBlock: { snapshot in
_name = snapshot["name"]
}
}
var name: {
get {
return _name
}
set(name) {
// When the client sets the name, write it to Firebase
_name = name
self.connection.childByAppendingPath("name").setValue(name)
}
}
}
I'm sure I'm making a lot of mistakes in there. The idea is that the data is first loaded from the server when the instance is instantiated. Subsequently, we could call my_data_instance.name
to get that name, or my_data_instance.name = "foo"
and the name would be automatically written to the server.
This requires ~10 lines of code for a single attribute (of which there will be many). Nuts! There must be a better way!
EDIT: to be clear, I want to obviate the need to write as MUCH boiler plate code as possible. Consider a library like Mantle
, where merely defining a @property
is sufficient to do everything you want. In my opinion, anything more than one single line of code to say I have an attribute called "name", handle it via Firebase
is overly verbose.
You can use Key-Value Observing to monitor changes in your properties. More info in Adopting Cocoa Design Patterns in Swift .
import Foundation
private var KVOContext = 0
// Your class must inherit from NSObject
class MyData : NSObject {
private let propertiesToObserve = ["name", "location"]
dynamic var name: String
dynamic var location: String
init(name: String, location: String) {
self.name = name
self.location = location
super.init()
// Add the properties that you want to observe
for property in self.propertiesToObserve {
self.addObserver(self, forKeyPath: property, options: [.New, .Old], context: &KVOContext)
}
}
// This method is called whenever an observed property is changed
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if let property = keyPath,
newValue = change![NSKeyValueChangeNewKey],
oldValue = change![NSKeyValueChangeOldKey] {
print("\(property) changed from \(oldValue) to \(newValue)")
// If oldValue != newValue, write back to Firebase
}
}
// Remove self as observer of self
deinit {
for property in self.propertiesToObserve {
self.removeObserver(self, forKeyPath: property)
}
}
}
let data = MyData(name: "John", location: "Chicago")
data.name = "David" // print: name changed from John to David
data.location = "New York" // print: location changed from Chicago to New York
Swift provides that functionality called property observer
var name: String {
didSet {
self.connection.childByAppendingPath("name").setValue(name)
}
}
There is a second observer willSet
which is called before the value is changed.
Note (from the documentation):
When you assign a default value to a stored property, or set its initial value within an initializer, the value of that property is set directly, without calling any property observers.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.