26 August 2015

Techniques after using Swift for a month

At work, I was put on an iOS/iPad app project a little while ago. I've never written anything in the iOS ecosystem so I spent about a week to research before digging in.The transition was smooth primarily because I'm using Swift--a much more succinct and modern language than Objective-C--and my somewhat dated Android experience and very dated Windows dev experience provided me with all of the patterns I needed to understand iOS. Also, coming off of Groovy development, Swift's syntactic sugar was immediately understandable.

I came up with two small techniques for the project. One helps with data persistence using Core Data with models and entity managers, a la JPA. The other technique simplifies updating multiple views using the Notification Center with type safe additions. They've helped to speed development and--the thought of every developer who thinks they've discovered something new--some aspects aren't commonly known (which could be good or other).

My Core Data code starts with the NSManagedObject model generation. In a separate file, I create an extension class so that the model can always be re-created without wiping out my custom methods. I also create a class derived from a DAO utility class to implement all CRUD methods using generics. The DAO util:

class DaoUtil {
    
    var managedObjectContext: NSManagedObjectContext
    
    var managedObjectModel: NSManagedObjectModel
    
    var entityName: String
    
    init(managedObjectContext: NSManagedObjectContext, managedObjectModel: NSManagedObjectModel, entityName: String) {
        self.managedObjectContext = managedObjectContext
        self.managedObjectModel = managedObjectModel
        self.entityName = entityName
    }

    // ... create, find, count, and delete methods

}

The find, count, and delete methods come in an *All() version and a *(fetchRequestName: String) version. The implementation for a User model would be as follows:

extension User {

    // ...

}

class UserDao: DaoUtil {
    
    let EntityName = "User"
    
    init(managedObjectContext: NSManagedObjectContext, managedObjectModel: NSManagedObjectModel) {
        super.init(managedObjectContext: managedObjectContext, managedObjectModel: managedObjectModel, entityName: EntityName)
    }
    
    // ... custom methods

}

This design favors the data mapper pattern over the active record pattern, which I think is more common in iOS. I prefer a separation of data object and persistence.

The other technique is a type-safe wrapper around the notification center. iOS's notification center calls reminded me of Windows SendMessage (and, with dispatch queues, PostMessage). One potential issue with these is the lack of type safety since you're effectively passing around a void pointer. Using Swift's generics again allows us to create small, typed versions based on a named string constant, an event (e,g, BeforeUpdate or AfterDelete), and an object type.

The core of the implementation:

    private func observe(id: String, event: Event?, notify: (observable: T?) -> Void) -> NSObjectProtocol {
        var observer = notificationCenter.addObserverForName(id, object: event, queue: NSOperationQueue.mainQueue()) { notification in
            if let userInfo = notification.userInfo, observable = userInfo[id] as? T {
                notify(observable: observable)
            } else {
                notify(observable: nil)
            }
        }
        return observer
    }
    
    private func notify(id: String, event: Event?, observable: T?) {
        if let observable = observable {
            notificationCenter.postNotificationName(id, object: event, userInfo: [id: observable])
        } else {
            notificationCenter.postNotificationName(id, object: event)
        }
    }
    
    func observeUser(event: Event?, notify: (equipment: User?) -> Void) -> NSObjectProtocol {
        return observe(NotificationService.Name.User, event: event, notify: notify)
    }
    
    func notifyUser(event: Event?, user: User?) {
        notify(NotificationService.Name.User, event: event, observable: user)
    }

With this, I can trace publishers and subscribers by type and event, but still take advantage of the built-in message queue. Still much to learn ...

[ posted by sstrader on 26 August 2015 at 11:41:51 PM in Programming ]