Generic Translation Layer Design

So here's the goal: I have a framework that deals in subclasses of a base `Object`. While I want to use this framework, I do not want to expose it to the rest of the app. Instead, I'm writing a layer around the framework that uses `Model` objects. In order to do this, I need to add a translation layer. I want the translation layer to be a protocol that simply translates from an `Object` subtype to a `Model` subtype. Ex:



public protocol Translator {
  typealias T:Model
  typealias U:Object

  var objectType:U.Type { get }
  var modelType:T.Type { get }

  func translate(from: U) -> T
  func translate(from: T) -> U
}


I can't store the protocol this way though, so I tried to make it more generic:



public protocol Translator {
  var objectType:Object.Type { get }
  var modelType:Model.Type { get }

  func translateObject(from: Object) -> Model
  func translateModel(from: Model) -> Object
}


I would then have a wrapper class that stores the Translators and uses them to convert from `Object` to `Model`:


public class Wrapper {
  let translators:[Translator]

  init(translators:[Translator]) {
    self.translators = translators
  }

  private func translatorFor<T:Model>(type: T.Type) -> Translator? {
    for translator in translators {
      if translator.modelType === type {
        return translator
      }
    }
    return nil
  }

  private func getObject<U:Object>(type: U.Type) -> U {
    // Use framework to return object
  }

  func getModel<T:Model>(type:T.Type) -> T? {
    if let translator = translatorFor(type) {
      let object = getObject(translator.objectType)
      return translator.translateObject(object) as? T
    }
    return nil
  }
}


The idea is simple, the Wrapper takes a Model type, uses it to find a Translator for that type. It uses the Translator to retrieve the correct Object, and the convert that object into a Model.


With a more generic Translator Protocol this almost works, except I can't seem to check the provided type in the provided in `getModel` to the stored type in translator. On line 10 in the Wrapper I get the error `Binary operator '===' cannot be applied to operands of type 'Model.Type' and 'T.Type'`. But I know that `T` conforms to `Model`.


Does anyone have an idea of how I can accomplish this?

Regarding the particular question about making this work:

if translator.modelType === type { ... }


try this:

if ObjectIdentifier(translator.modelType) == ObjectIdentifier(type) { .. }

In Xcode 7 beta 2, just using the == operator seems to work to compare meta-types in a playground


private func translatorFor<T:Model>(type: T.Type) -> Translator? {
    for translator in translators {
        if translator.modelType == type {
            return translator
        }
    }
    return nil
}

That does not seem to work. I get the error: `Binary operator '==' cannot be applied to two ObjectIdentifier operands`.


Likely, this is because the types in question are generics conforming to a Protocol. While `Object` is a base class, Model is simply a Protocol. I want my model objects to have value semantics, so defining a base Protocol seemed the best way to transform a class type to a struct type. However, I checked and this actually works:


  private func translatorForObjectType<T:Object>(type: T.Type) -> Translator? {
    for translator in translators {
      if translator.objectType === type {
        return translator
      }
    }
  }


So I can compare types when they're based on a class, but not when they're based on a protocol. I'm going to update my original post to reflect this.

UPDATE:


I neglected to point out that `Model` is simply a protocol, and that seems to be the source of the problem. I cannot compare a generic type when it is based on a protocol. `Object` (from the framework I'm wrapping) is a class, so this works:


  private func translatorForObjectType<T:Object>(type: T.Type) -> Translator? {
    for translator in translators {
      if translator.objectType === type {
        return translator
      }
    }
  }


The point of using a protocol was to allow me to convert `Object` objects into value-based structs (to be used by the rest of the app). Defining a protocol was the only way I could think to do that. Perhaps there's a better way? (Although I still want to avoid Classes for my Model)

If true, this is good news. Unfortunately, I'm writing this code in a production app so I'm not able to take advantage of Swift 2 (no matter how much I'd like to)

Well, I don't know if I would recommend doing the following in production code, but I'm pretty sure it still worked back in 1.2...


private func translatorFor<T:Model>(type: T.Type) -> Translator? {
    for translator in translators {
        if "\(translator.modelType)" == "\(type)" {
            return translator
        }
    }
    return nil
}

Wow... +1 for ingenuity. You're right that I probably souldn't use this in production, but clearly it's a clever idea.


I'm starting to think that I'll be forced to use Classes instead of Structs for my Model objects at least until Swift 2 comes out.

Generic Translation Layer Design
 
 
Q