Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
//: Playground - noun: a place where people can play
import Cocoa
// When you’re downloading objects from the web, it’s common to need to merge changes
// from the server to already-existing local objects. (If your data model allows for
// mutable objects, as with Core Data, that is.)
//
// The below is a Swift translation of how I’ve done this in Objective-C.
// Note that it works just fine in Swift — though it does require NSObject subclasses.
//
// The part I like is that updateLocalObjectWithServerObject is reusable
// (and I have reused my equivalent Objective-C code).
// It doesn’t care what the objects are, or whether they’re of the same type,
// and it doesn’t care what the types of the values are.
//
// An argument against is that it uses strings — see mergeablePropertyNames.
// Type one of those wrong, and you get a runtime exception.
// My counter to that is that you’ll find that bug very, very quickly.
// Even your testers won’t see it.
//
// My wish is that I could do this same thing — or something very much like it —
// in pure Swift (minus NSObject), and that it would work with structs as well as classes.
class LocalObject: NSObject {
var dog = "Fido"
var cat = "Hunter"
var zebra = "Mr. Stripes"
var numberOfAnimals = 17
var fedTheTigers = false
var attendingDoctors: [String]?
let zoo = "On My Mac"
static let mergeablePropertyNames = ["dog", "cat", "zebra", "numberOfAnimals", "fedTheTigers", "attendingDoctors"]
}
class ServerObject: NSObject {
var dog = "Rex"
var cat = "Hunter"
var zebra = "Mr. Stripes"
var numberOfAnimals = 33
var fedTheTigers = true
var attendingDoctors = ["Smith", "Tennant", "Capaldi"]
let dateDownloaded = NSDate()
}
func valuesAreEqual(value1: AnyObject?, _ value2: AnyObject?) -> Bool {
if let value1 = value1, value2 = value2 {
return value1.isEqual(value2)
}
if value1 == nil && value2 == nil {
return true
}
return false
}
func updateLocalObjectWithServerObject(localObject: AnyObject, serverObject: AnyObject, propertyNames: [String]) -> NSDictionary {
// Returns a dictionary with the changes. The changes can then be used for notifications, updating the database, etc.
let changeDictionary = NSMutableDictionary()
propertyNames.forEach { (onePropertyName) in
let localValue = localObject.valueForKey(onePropertyName)
let serverValue = serverObject.valueForKey(onePropertyName)
if valuesAreEqual(localValue, serverValue) {
return
}
localObject.setValue(serverValue, forKey: onePropertyName)
changeDictionary[onePropertyName] = serverValue
}
return changeDictionary.copy() as! NSDictionary
}
let x = LocalObject()
let y = ServerObject()
let changes = updateLocalObjectWithServerObject(x, serverObject: y, propertyNames: LocalObject.mergeablePropertyNames)
print(changes)
// Prints:
//{
// attendingDoctors = (
// Smith,
// Tennant,
// Capaldi
// );
// dog = Rex;
// fedTheTigers = 1;
// numberOfAnimals = 33;
//}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment