Saturday, July 08, 2017

Type Erasure

Say that I have a view model that has a reference to a data source object generated by another object like a factory. In my view model I would have a property that is typed as AnyDataSource. I also have a property that is of type FRCDataSource, this is the object that will be wrapped in the AnyDataSource object.

class AddToDoViewModel {
    var dataSource : AnyDataSource<Item>!
    var frcDataSource : FRCDataSource<Item>!

FRCDataSource is defined as :

class FRCDataSource<T: NSManagedObject> : DataSourceProtocol {

    var fetchedResultsController : NSFetchedResultsController<T>!

    init(fetchedResultsController: NSFetchedResultsController<T>) {
    self.fetchedResultsController = fetchedResultsController
    }
}

The FRCDataSource class is a wrapper around an NSFetchedResultsController type. The generic type constraint on FRCDataSource limits the type of T to be an object that derives from NSManagedObject. Also note that FRCDataSource is specialized by the same type as NSFetchedResultsController. To create an FRCDataSource we do the following in reloadData()

func reloadData() {
    self.frcDataSource = FRCDataSource(fetchedResultsController:self.fetchedResultsController)
}

We create an FRCDataSource, passing in the NSFetchedResultsController class property. Now our frcDataSource property is set to a FRCDataSource instance that internally stores a NSFetchedResultsController instance. In this case, I am creating an instance of FRCDataSource directly, although I could easily delegate this responsibility to another object which would create and return an FRCDataSource.

Once I’ve created an FRCDataSource object from the NSFetchedResultsController, I can now add a method that will create an AnyDataSource derived object and store it in the dataSource property of the view model:

func createDataSource<T: DataSourceProtocol>(dataSource: T) where T.DataSourceItem == Item 
{
    self.dataSource = AnyDataSource(dataSource)
}

The createDataSource() takes an object that adopts the DataSourceProtocol, creates an AnyDataSource object and sets the dataSource property. There are a few things to take note of here:

Once the data source is created we will have an AnyDataSource typed object that can be used in place of a DataSourceProtocol type.

Basically what we’ve done is create wrappers for two types


Saturday, July 01, 2017

ProtocolOrientedProgramming

Type Erasure


Say that I have a view model that has a reference to a data source object generated by another object like a factory.  In my view model I would have a property that is typed as AnyDataSource<Item>.  I also have a property that is of type FRCDataSource<Item>, this is the object that will be wrapped in the AnyDataSource<Item> object.


class AddToDoViewModel {

var dataSource : AnyDataSource<Item>!

var frcDataSource : FRCDataSource<Item>!



FRCDataSource<Item> is defined as :  


class FRCDataSource<T: NSManagedObject> : DataSourceProtocol {


var fetchedResultsController : NSFetchedResultsController<T>!


init(fetchedResultsController: NSFetchedResultsController<T>) {

        self.fetchedResultsController = fetchedResultsController

}

}


The FRCDataSource<T> class is a wrapper around an NSFetchedResultsController<T> type.  The generic type constraint on FRCDataSource limits the type of T to be an object that derives from NSManagedObject. Also note that FRCDataSource<T> is specialized by the same type as NSFetchedResultsController<T>.  To create an FRCDataSource we do the following in reloadData()


func reloadData() {

self.frcDataSource = FRCDataSource(fetchedResultsController:self.fetchedResultsController)

}


We create an FRCDataSource, passing in the NSFetchedResultsController<T> class property.  Now our frcDataSource property is set to a FRCDataSource<Item> instance that internally stores a NSFetchedResultsController<Item> instance.  In this case, I am creating an instance of FRCDataSource directly, although I could easily delegate this responsibility to another object which would create and return an FRCDataSource.


Once I’ve created an FRCDataSource object from the NSFetchedResultsController, I can now add a method that will create an AnyDataSource<Item> derived object and store it in the dataSource property of the view model:


func createDataSource<T: DataSourceProtocol>(dataSource: T) 

where T.DataSourceItem == Item 

{

self.dataSource = AnyDataSource(dataSource)

}


The createDataSource<T>() takes an object that adopts the DataSourceProtocol, creates an AnyDataSource object and sets the dataSource property.  There are a few things to take note of here:


  • The createDataSource method is a generic method and is specialized by an object that must adopt DataSourceProtocol.  The input parameter dataSource is of the specialized type.
  • A generic constraint is specified which constrains the DataSourceItem associated type of the input parameter to be of type Item.  This is done to make sure that the type of the dataSource property matches the type returned by the AnyDataSource(dataSource) init call.


Now we just have to pass in the frcDataSource property to the createDataSource() method.


createDataSource(dataSource: self.frcDataSource)


Once the data source is created we will have an AnyDataSource typed object that can be used in place of a DataSourceProtocol type.  


Basically what we’ve done is create wrappers for two types of objects:

  1. An FRCDataSource<T: NSManagedObject> wrapper around a NSFetchedResultsController<T> type
  2. An AnyDataSource<T: DataSourceProtocol> wrapper around the DataSourceProtocol adopter FRCDataSource<T: NSManagedObject>


This allows external clients of AddToDoViewModel (e.g. UITableViewDataSource delegate objects) access to the AnyDataSource<Item> object without leaking the internal implementation of the data source of AddToDoViewModel (e.g. NSFetchedResultsController<Item>).  Knowing this, we can replace frcDataSource with another object that adopts DataSourceProtocol.  If I have a type that models data within sections called a DataSource,


class SectionController<T: NSManagedObject> : DataSourceProtocol {

    var sections : [SectionInfo<T>] = [] 

}


This class is used to represent objects stored in an array of SectionInfo<T> objects.  This enables us to create a custom data source that is not reliant on CoreData and relationships between CoreData entities.  I can create an array of SectionInfo<T> types like SectionInfo<Item> types and store them in the sections property.  This gives me a flexible way of storing data than what an NSFetchedResultsController provides since the fetchedObjects property of the NSFetchedResultsController are immutable.  Basically, the NSFetchedResultsController provides an immutable in-memory representation of the entities and relationships in a CoreData store.  If your desire is to re-order sections easily, you would need to create a new data store that structures the data the way that you want.


Let’s say that I would like a data source that models the following sections along with their respective items:


  • Errands 
  • Groceries
  • Post Office
  • Career
  • Presentation
  • Multiple Selections


I first create an array of SectionInfo<T> objects.  The SectionInfo<T> struct is defined as:


class SectionInfo<T : NSManagedObject> {

    var name : String = ""

    var rows : [T] = []

}


Each SectionInfo<T> has a name as well as an array of T types.  Lets create a method to create a section within the SectionController class:


extension SectionController {

    func createSection(name: String, items: [T]) {

        let section = SectionInfo<T>()

        section.name = name

        

        for item in items {

            section.rows.append(item)

        }

        

        self.sections.append(section)

    }

}


Now I can do the following:

  1. Create a property in AddToDoViewModel of type SectionController<Item>! called sectionDataSource
  2. Intitalize the sectionDataSource property with data by calling createSection() for each section
  3. Call createDataSource() which sets the dataSource property.


First add a property to AddToDoViewModel

class AddToDoViewModel {

var sectionDataSource : SectionController<Item>!


Create the SectionController<Item> instance in reloadData()

func reloadData() {

self.sectionDataSource = SectionController()

}


Create the sections in an initSectionData()

func initSectionData() {

self.sectionDataSource.createSection(name:”Errands”

, items: [Item(name:“Groceries”), Item(name:”Post Office”)])


self.sectionDataSource.createSection(name:”Career”,

items:[Item(name:“Presentation”), Item(name:”Multiple Selections”)]

}


Set the dataSource property

createDataSource(dataSource: self.sectionDataSource)


what we’ve done is create wrappers for two types of objects:

  1. A SectionController<T: NSManagedObject> wrapper around a SectionInfo<T> type
  2. An AnyDataSource<T: DataSourceProtocol> wrapper around the DataSourceProtocol adopter SectionController<T:NSManagedObject> type


Now that we’ve created the new data source, external clients (e.g. UITableViewDataSource delegate objects) can access this data store without any changes


This page is powered by Blogger. Isn't yours?