Implementing Swift protocol with a constrained type parameter - swift

I have a couple of Swift protocols that describe a general interface that I'm trying to implement in multiple ways:
protocol Identifiable
{
var identifier:String { get }
}
protocol ItemWithReference
{
var resolveReference<T:Identifiable>(callback:(T) -> ())
}
Now I want to implement the ItemWithReference protocol using CloudKit as the back end (this will eventually work with an alternate back-end as well, at which time I expect to provide an alternative implementation of the ItemWithReference protocol.
In my CloudKit implementation, I have something like this:
class CloudKitIdentifiable : Identifiable
{
...
}
class CloudKitItemWithReference : ItemWithReference
{
func resolveReference<T:Identifiable>(callback:(T) -> ())
{
// In this implementation, I want to only proceed if `T` is a CloudKitIdentifiable subtype
// But not sure how to enforce that
}
}
What I would like to do is to constrain T to be a CloudKitIdentifiable rather than just a simple Identifiable. I can't do that directly in the resolveReference declaration because then the function wouldn't conform to the ItemWithReference protocol. So instead, I am hoping to confirm that T is indeed a CloudKitIdentifiable and then invoke it's initializer to create a new instance of the class being resolved.
Is there any way in Swift to use T's metatype T.Type and determine if it is a subtype of another type? Furthermore, is there any way to invoke a required initializer that has been declared on that subtype?

try:
class CloudKitIdentifiable : Identifiable {
var identifier:String = ...
required init() {}
// you need `required`.
}
class CloudKitItemWithReference : ItemWithReference {
func resolveReference<T:Identifiable>(callback:(T) -> ()) {
if T.self is CloudKitIdentifiable.Type {
// do work..
let obj = (T.self as CloudKitIdentifiable.Type)()
callback(obj as T)
}
}
}
OR:
class CloudKitItemWithReference : ItemWithReference {
func resolveReference<T:Identifiable>(callback:(T) -> ()) {
if let CKT = T.self as? CloudKitIdentifiable.Type {
// do work..
let obj = CKT()
callback(obj as T)
}
}
}
But, In this case, you have to call resolveReference like this:
let ref = CloudKitItemWithReference()
ref.resolveReference { (obj: CloudKitIdentifiable) -> () in
// ^^^^^^^^^^^^^^^^^^^^ explicit type is necessary.
println(obj.identifier)
return
}
Rathar than that, I would recommend to use Associated Type:
protocol Identifiable {
var identifier:String { get }
}
protocol ItemWithReference {
typealias Item: Identifiable // <-- HERE is associated type
func resolveReference(callback:(Item) -> ())
}
class CloudKitIdentifiable : Identifiable {
var identifier:String
init(identifier: String) {
self.identifier = identifier
}
}
class CloudKitItemWithReference : ItemWithReference {
// `Item` associated type can be inferred from
// the parameter type of `resolveReference()`
//
// typealias Item = CloudKitIdentifiable
func resolveReference(callback:(CloudKitIdentifiable) -> ()) {
let obj = CloudKitIdentifiable(identifier: "test")
callback(obj)
}
}
let ref = CloudKitItemWithReference()
ref.resolveReference { obj in
println(obj.identifier)
return
}

Related

Why in Swift generics factory method does compiler warn Generic class 'X' requires that 'Object' conforms to 'Y'

As an overview, I'm trying to implement a data abstraction layer in Swift. I'm using two database SDKs but I'm trying to be able to isolate their specific APIs from the rest of the system.
I'm trying to implement a factory pattern that will return the correct object based on a protocol conformance of the supplied concrete type. But the compiler is giving me red flags that I can't wrap my head around.
The Thing class is a first class entity that moves freely through the logic and UI layers of the app, and certain objects persist on a Realm store (which shouldn't matter) and have a specific type that is a subclass of the Realm object. That type is returned by a static function that has to be there to conform with the protocol FromDataSourceA.
protocol FromDataSourceA {
static func A_objectType () -> A_Object.Type
}
class MyObject {
...
}
class Thing: MyObject, FromDataSourceA {
static func A_objectType () -> A_Thing.Type
...
}
// Example usage
let target = DataTarget<Thing>(url: "url")
let dataSource = target.dataSourceBuilder()
As you can see, the concrete type Thing conforms to FromDataSourceA and is passed into the DataTarget initializer. DataSourceBuilder needs to be able to check that Thing conforms to FromDataSourceA to be able to return the correct DataSource.
The DataSourceTypeA class looks like
class DataSourceTypeA<T: MyObject & FromDataSourceA>: DataSource<T> {
let db: DatabaseTypeA // All db-specific APIs contained here
override init (target: DataTarget<T>) {
self.db = try! DatabaseTypeA()
super.init(target: target)
}
override func create<T: MyObject & FromDataSourceA> (object: T) {
db.beginWrite()
db.create(T.A_objectType(), value: object.dict()) // <-- T.A_objectType() is what the conformance to FromDataSourceA is needed for
try! db.commitWrite()
}
override func get<T: MyObject & FromDataSourceA>(id: Int) -> T {
...
}
}
class DataSource<T>: AnyDataSource {
let target: DataTarget<T>
init (target: DataTarget<T>) {
self.target = target
}
func create<T> (object: T) { }
func get<T>(id: Int) -> T { fatalError("get(id:) has not been implemented") }
}
protocol AnyDataSource {
func create<T> (object: T)
func get<T> (id: Int) -> T
}
The problem I'm facing right now is when I check that the metatype conforms to FromDataSourceA, the compiler gives me this warning
class DataTarget<T: MyObject> {
...
func dataSourceBuilder () -> DataSource<T> {
if T.self is FromDataSourceA { // <-- Thing.self conforms to FromDataSourceA
// The Problem:
return DataSourceTypeA(target: self) // Generic class 'DataSourceTypeA' requires that 'MyObject' conform to 'FromDataSourceA'
} else {
...
}
}
}
Why won't the compiler let me return the DataSourceTypeA instance with argument self if the generic T passes the conditional statement and is proven as being in conformance with FromDataSourceA?
The problem is that the call
return DataSourceTypeA(target: self)
is resolved at compile time, therefore it does not help to check
if T.self is FromDataSourceA { }
at runtime. A possible solution is to make the conformance check a compile-time check by defining a restricted extension:
extension DataTarget where T: FromDataSourceA {
func dataSourceBuilder() -> DataSource<T> {
return DataSourceTypeA(target: self)
}
}
If necessary, you can add more implementations for other restrictions:
extension DataTarget where T: FromDataSourceB {
func dataSourceBuilder() -> DataSource<T> {
// ...
}
}
or add a default implementation:
extension DataTarget {
func dataSourceBuilder() -> DataSource<T> {
// ...
}
}
For a call
let dataSource = target.dataSourceBuilder()
the compiler will pick the most specific implementation, depending on the static (compile-time) type information of target.

Protocol Conformance Check

How can I perform conformance check against protocol with AssociatedType. Xcode shows error:
Protocol 'MyListener' can only be used as a generic constraint because
it has Self or associated type requirements
My ultimate goal is to extract "MyListener.section" from an array of weakObjects, where the handler matches the function argument.
Note. The NSPointerArray of weakObjects is suppose to capture different types of MyListeners.
public class MyHandler<O,E> {
var source = [O]()
var dest = [E]()
}
public protocol MyListener:class {
var section: Int {get}
associatedtype O
associatedtype E
var handler: MyHandler<O,E>? { get }
}
public class MyAnnouncer {
private let mapWeakObjects: NSPointerArray = NSPointerArray.weakObjects()
public func add<L: MyListener>(listener: L) {
let pointer = Unmanaged.passUnretained(listener).toOpaque()
mapWeakObjects.addPointer(pointer)
}
public func search<O, E> (h:MyHandler<O,E>) -> [Int] {
_ = mapWeakObjects.allObjects.filter { listener in
if listener is MyListener { // Compilation failed
}
if let _ = listener as? MyListener { //Compilation error
}
if listener is MyListener.Type { //Compilation failed
}
}
return [] // ultimate goal is to extract corresponding [MyListener.section].
}
}
Unfortunately, Swift doesn't support protocols with AssociatedType to conformance.
You should try to use Type Erasure. One of the way is to implement type erasure by creating new AnyType class.
Here is another way to release type erasure (example from the internet)
protocol SpecialValue { /* some code*/ }
protocol TypeErasedSpecialController {
var typeErasedCurrentValue: SpecialValue? { get }
}
protocol SpecialController : TypeErasedSpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
extension SpecialController {
var typeErasedCurrentValue: SpecialValue? { return currentValue }
}
extension String : SpecialValue {}
struct S : SpecialController {
var currentValue: String?
}
var x: Any = S(currentValue: "Hello World!")
if let sc = x as? TypeErasedSpecialController { // Now we can perform conformance
print(sc.typeErasedCurrentValue)
}

Returning a nil from an optional generic extension

Here's something I'm playing with. The problem is that I have a container class that has a generic argument which defines the type returned from a closure. I want to add a function that is only available if they generic type is optional and have that function return a instance containing a nil.
Here's the code I'm currently playing with (which won't compile):
open class Result<T>: Resolvable {
private let valueFactory: () -> T
fileprivate init(valueFactory: #escaping () -> T) {
self.valueFactory = valueFactory
}
func resolve() -> T {
return valueFactory()
}
}
public protocol OptionalType {}
extension Optional: OptionalType {}
public extension Result where T: OptionalType {
public static var `nil`: Result<T> {
return Result<T> { nil } // error: expression type 'Result<T>' is ambiguous without more context
}
}
Which I'd like to use like this:
let x: Result<Int?> = .nil
XCTAssertNil(x.resolve())
Any idea how to make this work?
I don't think you can achieve this with a static property, however you can achieve it with a static function:
extension Result {
static func `nil`<U>() -> Result where T == U? {
return .init { nil }
}
}
let x: Result<Int?> = .nil()
Functions are way more powerful than properties when it comes to generics.
Update After some consideration, you can have the static property, you only need to add an associated type to OptionalType, so that you'd know what kind of optional to have for the generic argument:
protocol OptionalType {
associatedtype Wrapped
}
extension Optional: OptionalType { }
extension Result where T: OptionalType {
static var `nil`: Result<T.Wrapped?> {
return Result<T.Wrapped?> { nil }
}
}
let x: Result<Int?> = .nil
One small downside is that theoretically it enables any kind of type to add conformance to OptionalType.

swift - Pass generic type to method with more specific extension requirements

So the title is a little weirdly worded, but here is the basis of what I am looking to do. I want to make a function that can determine if the generic type given extends from a specific protocol and then pass through the type to the more specific method for processing. This would be using the swift programming language to do so.
Psuedo code of what I want to achieve below:
func doStuff<T>(callback: Callback<T>) {
// Pseudo code of what I want to achieve as I'm not sure the syntax
// nor if it's even possible
if T extends Protocol {
let tExtendsProtocolType = T.Type as Protocol
mapStuffSpecific<tExtendsProtocolType>(callback: callback)
} else {
// Standard Use Case
}
}
func doStuffSpecific<T: Protocol>(callback: Callback<T> {
}
Thanks in advance
EDIT 1
typealias Callback<T> = (T) -> Void
protocol Protocol {}
struct A {}
struct B: Protocol {}
// I want to be able to use this to do some common set up then call into either doStuff<T> or doStuff<T: Protocol>
func tryDoStuff<T>(callback: Callback<T>) {
// Do some common setup then call this
doStuff(callback: callback)
}
func doStuff<T>(callback: Callback<T>) {
print("doStuff")
}
func doStuff<T: Protocol>(callback: Callback<T>) {
print("doStuffSpecific")
}
let callbackA: Callback<A> = { _ in } // Just an empty closure
let callbackB: Callback<B> = { _ in }
tryDoStuff(callback: callbackA) // prints doStuff
tryDoStuff(callback: callbackB) // prints doStuffSpecific
Swift's overload resolution algorithm already prioritizes the most specific overload available. Here's an example:
typealias Callback<T> = (T) -> Void
protocol Protocol {}
struct A {}
struct B: Protocol {}
func doStuff<T>(callback: Callback<T>) {
print("doStuff")
}
func doStuff<T: Protocol>(callback: Callback<T>) {
print("doStuffSpecific")
}
let callbackA: Callback<A> = { _ in } // Just an empty closure
let callbackB: Callback<B> = { _ in }
doStuff(callback: callbackA) // prints doStuff
doStuff(callback: callbackB) // prints doStuffSpecific

Specify relationships between protocols in Swift

I want to specify a protocol that manages some type objects that conform to another protocol. Like this:
// Specify protocol
protocol ElementGenerator {
func getElements() -> [Element]
}
protocol Element {
// ...
}
// Implement
class FooElementGenerator: ElementGenerator {
func getElements() -> [FooElement] {
// Generate elements here
return [FooElement()]
}
}
class FooElement {
// ...
}
When trying to compile this, I get an error:
Type 'FooElementGenerator' does not conform to protocol 'ElementGenerator'
hinting that candidate func getElements() -> [FooElement] has non-matching type of () -> [FooElement], but instead it expects () -> [Element].
How this kind of an error can be fixed?
UPDATE:
This solution seems to be working:
protocol ElementGenerator {
typealias T:Element
func getElements() -> [T]
}
protocol Element {
// ...
}
class FooElementGenerator: ElementGenerator {
typealias T = FooElement
func getElements() -> [T] {
return [T()]
}
}
class FooElement: Element {
// ...
}
But when I try to create a variable like this:
let a: ElementGenerator = FooElementGenerator()
a new error appears:
Protocol 'ElementGenerator' can only be used as a generic constraint because it has Self or associated type requirements
When implementing protocol methods, the return type must be same but you may return child class object like this;
protocol ElementGenerator {
func getElements() -> [Element]
}
//#objc for bridging in objective C
#objc protocol Element {
// ...
}
// Implement
class FooElementGenerator: NSObject,ElementGenerator {
override init() {
super.init();
//--
let fooElements:[FooElement] = self.getElements() as! [FooElement]
}
func getElements() -> [Element] {
// Generate elements here
return [FooElement()]
}
}
class FooElement:NSObject, Element {
// ...
override init() {
super.init();
//--
NSLog("FooElement init");
}
}
The error message in the second case is given since you have defined ElementGenerator with an “Associated Type”, and this means that you can only use it in giving constraints for types.
For instance, if you need to have a function defined for generic ElementGenerator values, you could write something like this:
func f<T1:ElementGenerator>(elemGenerator:T1) -> Element {
return elemGenerator.getElements()[0]
}
var a : Element = FooElementGenerator()
var b : Element = BarElementGenerator()
var x : Element = f(a)
var y : Element = f(b)
var z : FooElement = f(a) as! FooElement