Unable to create a Entity with some parameters - swift

I try to init a class in a generic way with parameters but it doesn't work...
protocol EntityCreator: class {
func createEntity<EntityType: EntityClass & EntityCreatorInitializable>(type: EntityType.Type, _ initialize: #escaping (EntityType) -> Void)
}
protocol EntityCreatorInitializable {
init(entityCreator: EntityCreator)
}
class EntityClass: NSObject { }
class MyClass: EntityClass, EntityCreatorInitializable {
required init(entityCreator: EntityCreator) {
super.init()
// use the entityCreator
}
}
// On the entity creator implementation :
class EntityCreatorImplementation: EntityCreator {
var toAdd = Set<EntityClass>()
func createEntity<EntityType: EntityClass & EntityCreatorInitializable>(type: EntityType.Type, _ initialize: #escaping (EntityType) -> Void) {
// This creation doesn't work...
let newEntity = EntityType(entityCreator: self)
initialize(newEntity)
self.toAdd.insert(newEntity)
}
}
The error I have at compilation is
Non-nominal type 'EntityType' does not support explicit initialization
Any Ideas on how to achieve this ?
Thanks !!
EDIT : I added the definition of EntityClass. In my project it is a GKEntity, but the problem is the same with just NSObject
EDIT : added the toAdd Set, forgot to declare it (but used it in the createEntity method)

Change
EntityType: EntityClass & EntityCreatorInitializable
to
EntityType: EntityCreatorInitializable

if you want to be able to initialize a lot of different types, you should define a new protocol, check this answer
protocol EntityCreator: class {
func createEntity<EntityType: EntityClass & EntityCreatorInitializable>(type: EntityType.Type, _ initialize: #escaping (EntityType) -> Void)
}
protocol EntityCreatorInitializable {
init(entityCreator: EntityCreator)
}
class EntityClass: NSObject { }
class MyClass: EntityClass, EntityCreatorInitializable {
public required init(entityCreator: EntityCreator) {
super.init()
// use the entityCreator
}
}
// On the entity creator implementation :
class EntityCreatorImplementation: EntityCreator {
func createEntity<EntityType: EntityCreatorInitializable>(type: EntityType.Type, _ initialize: #escaping (EntityType) -> Void) {
// This creation doesn't work...
let newEntity = EntityType.init(entityCreator:self)
// initialize(newEntity)
// self.toAdd.insert(newEntity)
}

Related

Property must be declared public because it matches a requirement in public protocol?

public protocol CDViewModel: class {
var cancellableList: [AnyCancellable] { get set }
}
public extension CDViewModel {
func subscribe(_ callback: #escaping (Project) -> Void) {
cancellableList.append( /*...*/)
}
}
I have a protocol that will be used outside of the module. So I have to declare it public.
But I want the class conforming to that to implement cancellableList with private access.
class MyClass: CDViewModel {
private var cancellableList: [AnyCancellable] = [AnyCancellable]()
}
Property 'cancellableList' must be declared public because it matches a requirement in public protocol 'CDViewModel'
Is this possible?
One workaround to this is to create an intermediate object class that can protect access to its variables using fileprivate. You declare that 'wrapper object type' in your MyClass instance thereby conforming to MyClass's required interface.
Now you have full access to the wrapped object (wrappedList) properties from within the protocol extension, but not from outside the module.
CDViewModel.swift
import Combine
class CDViewModelList {
fileprivate var cancellableList: [AnyCancellable] = [AnyCancellable]()
}
protocol CDViewModelProtocol: AnyObject {
var wrappedList: CDViewModelList { get }
}
extension CDViewModelProtocol {
func subscribe(_ callback: Int) {
self.wrappedList.cancellableList.append(/****/)
}
}
MyClass.swift
class MyClass: CDViewModelProtocol {
let wrappedList = CDViewModelList()
func doStuff () {
self.subscribe(24)
self.wrappedList.cancellableList // 'cancellableList' is inaccessible due to 'fileprivate' protection level
}
}
Thanks for the question it brought me to a good read here:
FULL CREDIT
You cannot have some requirements of a protocol being publicly satisfied and hide other ones, you have to have it all or nothing. More, since the protocol extension needs to work with cancellableList, then this property needs to be public on the type that conforms to the protocol.
If you want to hide the cancellableList property, to avoid extrinsic factors messing with it, one approach I can think of is to split the protocol in two, move the cancellableList to the new protocol, and privately declare a property in MyClass.
CDViewModel.swift
public protocol CDViewModel: class {
// other view model requirements
}
public protocol CDViewModelSource {
var cancellableList: [AnyCancellable] { get set }
}
public extension CDViewModelSource {
func subscribe(_ callback: #escaping (Project) -> Void) {
//cancellableList.append( /*...*/)
}
}
MyClass.swift
class MyClass {
private var source = MyClassSource()
func subscribe(_ callback: #escaping (Project) -> Void) {
source.subscribe(callback)
}
}
fileprivate class MyClassSource: CDViewModelSource {
var cancellableList: [AnyCancellable] = []
}
The downside would be that you'd have to talk to the source every time you need to access the list of cancellables.

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.

Convenience constructor with generics callback will crash in Swift 3.1

When creating a child class object which extends the generic base class, if it is created using convenience constructor, a callback contains the generic object will crash in the runtime because of the incorrect/corrupted object being passed back.
It is new in Swift3.1. There is no such problem in Swift 3.0.
It only happens when creating an object using convenience constructor.
protocol Initializable {
init()
}
class MyClass: Initializable {
required init() {}
func log() {
print("Hello")
}
}
class Parent<T: Initializable> {
typealias Callback = (T) -> Void
init(callback: Callback) {
callback(T())
}
}
class Child: Parent<MyClass> {
convenience init(value: Int, callback: Callback) {
self.init(callback: callback)
}
override init(callback: Callback) {
super.init(callback: callback)
}
}
let _ = Child { data in
data.log() //This is OK
}
let _ = Child(value: 0) { data in
data.log() //This will crash under swift3.1
}
Is it a language bug? Or am I doing something not supposed to be?

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

Implementing Swift protocol with a constrained type parameter

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
}