override initializer from generic class - swift

open class CheckItem<T, U: Equatable & CustomStringConvertible>: DataTableItem<T,U,Bool> {
public override init(_ data: T, getter: #escaping (T) -> U) {
super.init(data, getter: getter)
}
}
open class DataTableItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: TableItem{
let data: T
let getter: (T) -> U
public init(_ data: T, getter: #escaping (T) -> U) {
self.data = data
self.getter = getter
}
}
open class TableItem: NSObject {
public var title: String?
}
It is weird that, can't override the init in subclass CheckItem.
Compiler complains that Initializer does not override a designated initializer from its superclass. It complains that Overriding declaration requires an 'override' keyword if I remove the override keyword.
It drives me crazy anyone helps? Thanks in advance.
The more weird part is that it works in LabelItem
open class LabelItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: DataTableItem<T,U,V>{
public override init(_ data: T, getter: #escaping (T) -> U) {
super.init(data, getter: getter)
}
The full code is available here https://github.com/magerate/TableMaker
Edit
let checkItem = CheckItem<People, Bool>(people, getter: {(p: People) -> Bool in
p.isGirl
})
It compiles if don't try to create any instance of CheckItem. But complains
Cannot convert value of type 'People' to expected argument type 'Bool'
when try to create a new instance of CheckItem.
It seems that type inference is not correctly here.
Edit
It works when I deploy the code to swift framework. WTF

I am not sure what exactly goes wrong for you. When I run your code everything seems fine. Are you sure you're instantiating your instances correctly?
let checkItem = CheckItem("Check item data") { (string: String) -> String in
return string + "checkItem"
}
let labelItem = LabelItem<String, String, Int>("Label item") { (string: String) -> String in
return string + "labelItem"
}
let dataTableItem = DataTableItem<String, String, Int>("Data table item") { (string: String) -> String in
return string + "dataTableItem"
}
In LabelItem and DataTableItem you have a generic V which is not used anywhere and is not a parameter, so you need to be explicit with your types upon instantiation since you're not passing the V type in the init and the compiler can't infer the type. Thus <String, String, Int> or any other types that meet the constraints.
EDIT:
After looking into your project code (the project didn't run on my Xcode, I only copied relevant code into my project) I still see no problem - both initializers of CheckItem compile:
open class TableItem: NSObject {
public var title: String?
}
open class DataTableItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: TableItem{
let data: T
let getter: (T) -> U
public weak var host: TableItemHost?
public init(_ data: T, getter: #escaping (T) -> U) {
self.data = data
self.getter = getter
}
}
public protocol TableItemHost: class {}
open class CheckItem<T, U: Equatable & CustomStringConvertible>: DataTableItem<T,U,Bool> {
public init(_ data: T, host: TableItemHost, getter: #escaping (T) -> U) {
super.init(data, getter: getter)
self.host = host
}
public override init(_ data: T, getter: #escaping (T) -> U) {
super.init(data, getter: getter)
}
}
Creating instances:
let checkItem1 = CheckItem("Check item 1 ") { (string: String) -> String in
return string
}
class Host: TableItemHost {}
let host = Host()
let checkItem2 = CheckItem("Check item 2 ", host: host) { (string: String) -> String in
return string
}
print(checkItem1.data)
print(checkItem2.data)
Copy paste my code into a playground and see for yourself. Perhaps there is something other than the initializer causing the error.
For a test you can also try commenting out both initializers of CheckItem and instantiate it with the inherited initializer. That should work, because CheckItem will not have its own designated initializer anymore (https://stackoverflow.com/a/31459131/1433612)

Related

Create an array of protocols with constrained associated types

This is a basic example of creating an array of protocols with associated types using type erasure:
protocol ProtocolA {
associatedtype T
func doSomething() -> T
}
struct AnyProtocolA<T>: ProtocolA {
private let _doSomething: (() -> T)
init<U: ProtocolA>(someProtocolA: U) where U.T == T {
_doSomething = someProtocolA.doSomething
}
func doSomething() -> T {
return _doSomething()
}
}
Creating an array of them isn't hard:
let x: [AnyProtocolA<Any>] = []
Is there any way way I can create an array of protocols which have associated types that are constrained? This is what I have tried:
protocol Validateable {
// I removed the functions and properties here to omit unreleveant code.
}
protocol ProtocolA {
associatedtype T: Validateable
func doSomething() -> T
}
struct AnyProtocolA<T: Validateable>: ProtocolA {
private let _doSomething: (() -> T)
init<U: ProtocolA>(someProtocolA: U) where U.T == T {
_doSomething = someProtocolA.doSomething
}
func doSomething() -> T {
return _doSomething()
}
}
It compiles! But didn't it defeated the chance of creating an array of AnyProtocolA's now? Now I can't use type Any as a placeholder in my array.
How do I create an array of AnyProtocolA's which has a constrained associated type? Is it even possible? This won't work since Any ofcourse doesn't conform to Validateable:
let x: [AnyProtocolA<Any>] = []
Extending Any can't be done:
extension Any: Validateable {} // Non nominal type error
Edit:
I think I already found it, just type erasure the protocol Validateable as well:
protocol Validateable {
// I removed the functions and properties here to omit unreleveant code.
}
protocol ProtocolA {
associatedtype T: Validateable
func doSomething() -> T
}
struct AnyProtocolA<T: Validateable>: ProtocolA {
private let _doSomething: (() -> T)
init<U: ProtocolA>(someProtocolA: U) where U.T == T {
_doSomething = someProtocolA.doSomething
}
func doSomething() -> T {
return _doSomething()
}
}
struct AnyValidateable<T>: Validateable {}
Now I can use it as:
let x: [AnyProtocolA<AnyValidateable<Any>>] = []
Any answers that are better are always welcome :)

Cannot convert value of type '(T) -> Void'

Example:
struct Wrapper<T> {
var key: Int = 0
var listeners: [Int: (T) -> Void] = Dictionary()
mutating func add(_ handler:#escaping (T) -> Void) {
self.key += 1
self.listeners[self.key] = handler
}
func get(key: Int) -> (T) -> Void {
return self.listeners[key]!
}
}
Test protocol:
protocol CommonProtocol {
}
Class that create Wrapper of test class
class C {
var wrapper: Wrapper = Wrapper<CommonProtocol>()
func add<T: CommonProtocol>(_ handler: #escaping (T) -> Void) {
self.wrapper.add(handler) //Cannot convert value of type '(T) -> Void' to expected argument type '(CommonProtocol) -> Void'
}
}
Image with error
I get error:
Cannot convert value of type '(T) -> Void' to expected argument type '(CommonProtocol) -> Void'
Question:
Why (T) -> Void can't be casted to (CommonProtocol) -> Void ? The T
is explicitly declared as <T: CommonProtocol>
This is my first question, if you have some suggestions please don't hesitate to contact me
You don't need to make func add generic.
When you specify in func add<T: CommonProtocol>... you explicitly telling the compiler that your function accepts all Types that inherit CommonProtocol but your Wrapper specifies that accepts CommonProtocol not inherited types.
Solution
Either type-erase class C:
Class C<T: CommonProtocol> {
var wrapper: Wrapper<T>
....
}
or if type T doesn't actually matter to you then:
func add(_ handler: #escaping (CommonProtocol) -> Void)
but second one doesn't make sense at all. You have to downcast it every-time you'll use this method (and downcasts are very bad :D)
Note: It's actually not related to this question, but one of your options is to type-erase the CommonProtocol too.

Swift generic function saved as varaible

Given a class:
class First<T> {
}
And a method of class First:
func second<U>(closure: (value: T) -> U) {
}
How could I store the closure passed as an argument to second so that I can call it at a later date?
You would need to declare U in the class instead, so that you have a type for the storage:
class First<T,U> {
var f : ((T) -> U)! = nil
func second(closure: #escaping (T) -> U) {
self.f = closure
}
}
If having the function second work for only one kind of type is good enough for you, then Matt's answer is good.
class First<T, U> {
typealias ClosureType = (value: T) -> U
var savedClosure: ClosureType? = nil
func second(closure: ClosureType) {
savedClosure = closure
}
}
That doesn't actually answer your question as stated!
The thing is: You can't store a value of an unknown type.
But! if the type conforms to a known protocol, then you can save it.
protocol P {}
class First<T> {
typealias ClosureType = (value: T) -> P
var savedClosure: ClosureType? = nil
func second<U: P>(closure: (value: T) -> U) {
savedClosure = closure
}
}
The protocol could even be protocol<> "no protocol at all", which is typealiased to the keyword Any.
class First<T> {
typealias ClosureType = (value: T) -> Any
var savedClosure: ClosureType? = nil
func second<U>(closure: (value: T) -> U) {
savedClosure = closure
}
}
But we don't really know what you want to do, so there are multiple answers to your question… for example, maybe you wanted to store a separate closure for each type?
class First<T> {
typealias ClosureType = (value: T) -> Any
var savedClosures = [String: ClosureType]()
func second<U>(closure: (value: T) -> U) {
savedClosures[String(U)] = closure
}
}
Anyway, the real question is: "Do you really need to do this? Is there some simple change you can do that obviates this need?"

Using protocol with typealias as a property

I have a protocol with a typealias:
protocol Archivable {
typealias DataType
func save(data: DataType, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> DataType
}
and a class that conforms to that protocol:
class Archiver: Archivable {
typealias DataType = Int
func save(data: DataType, withNewName newName: String) throws {
//saving
}
func load(fromFileName fileName: String) throws -> DataType {
//loading
}
}
and I would like to use Archivable as a property in another class like:
class TestClass {
let arciver: Archivable = Archiver() //error here: Protocol 'Archivable' can only be used as a generic constraint because it has Self or associated type requiments
}
but it fails with
Protocol 'Archivable' can only be used as a generic constraint because it has Self or associated type requiments
My goal is that TestClass should only see Archiver as Archiveable, so if I want to change the saving/loading mechanism, I just have to create a new class that conforms to Archivable as set it as the property in TestClass, but I don't know if this is poosible, and if so, then how.
And I would like to avoid using AnyObject instead of DataType.
Depending on what you are actually trying to do, this can work using type erasure. If you follow the instructions in the link R Menke posted in the comments, you can achieve what you are trying to do. Since your property in TestClass seems to be a let, I'm going to assume you already know the type of DataType at compile time. First you need to setup a type erased Archivable class like so:
class AnyArchiver<T>: Archivable {
private let _save: ((T, String) throws -> Void)
private let _load: (String throws -> T)
init<U: Archivable where U.DataType == T>(_ archiver: U) {
_save = archiver.save
_load = archiver.load
}
func save(data: T, withNewName newName: String) throws {
try _save(data, newName)
}
func load(fromFileName fileName: String) throws -> T {
return try _load(fileName)
}
}
Much like Swift's AnySequence, you'll be able to wrap your Archiver in this class in your TestClass like so:
class TestClass {
let archiver = AnyArchiver(Archiver())
}
Through type inference, Swift will type TestClass' archiver let constant as an AnyArchiver<Int>. Doing it this way will make sure you don't have to create a dozen protocols to define what DataType is like StringArchiver, ArrayArchiver, IntArchiver, etc. Instead, you can opt in to defining your variables with generics like this:
let intArchiver: AnyArchiver<Int>
let stringArchiver: AnyArchiver<String>
let modelArchiver: AnyArchiver<Model>
rather than duplicating code like this:
protocol IntArchivable: Archivable {
func save(data: Int, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> Int
}
protocol StringArchivable: Archivable {
func save(data: String, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> String
}
protocol ModelArchivable: Archivable {
func save(data: Model, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> Model
}
let intArchiver: IntArchivable
let stringArchiver: StringArchivable
let modelArchiver: ModelArchivable
I wrote a post on this that goes into even more detail in case you run into any problems with this approach. I hope this helps!
When you try to declare and assign archiver:
let archiver: Archivable = Archiver()
it must have concrete type.
Archivable is not concrete type because it's protocol with associated type.
From "The Swift Programming Language (Swift 2)" book:
An associated type gives a placeholder name (or alias) to a type that
is used as part of the protocol. The actual type to use for that
associated type is not specified until the protocol is adopted.
So you need to declare protocol that inherits from Archivable and specifies associated type:
protocol IntArchivable: Archivable {
func save(data: Int, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> Int
}
And then you can adopt this protocol:
class Archiver: IntArchivable {
func save(data: Int, withNewName newName: String) throws {
//saving
}
func load(fromFileName fileName: String) throws -> Int {
//loading
}
}
There are no truly generic protocols in Swift now so you can not declare archiver like this:
let archiver: Archivable<Int> = Archiver()
But the thing is that you do not need to do so and I explain why.
From "The Swift Programming Language (Swift 2)" book:
A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.
So basically when you want to declare archiver as Archivable<Int> you mean that you don't want some piece of code using archiver to know about its concrete class and to have access to its other methods, properties, etc.
It's obvious that this piece of code should be wrapped in separate class, method or function and archiver should be passed there as parameter and this class, method or function will be generic.
In your case TestClass can be generic if you pass archivable via initializer parameter:
class TestClass<T, A: Archivable where A.DataType == T> {
private let archivable: A
init(archivable: A) {
self.archivable = archivable
}
func test(data: T) {
try? archivable.save(data, withNewName: "Hello")
}
}
or it can have generic method that accepts archivable as parameter:
class TestClass {
func test<T, A: Archivable where A.DataType == T>(data: T, archivable: A) {
try? archivable.save(data, withNewName: "Hello")
}
}
Hector gives a more complex though ultimately better solution above but I thought I'd post an alternative take on the answer anyway. It is simpler but probably less flexible in the long term.
typealias DataType = Int
protocol Archivable {
var data: DataType { get set }
func save(data: DataType, withNewName newName: String) throws
func load(fromFileName fileName: String) throws -> DataType
}
class Archiver: Archivable {
var data:DataType = 0
func save(data: DataType, withNewName newName: String) throws {
//saving
}
func load(fromFileName fileName: String) throws -> DataType {
return data
}
}
class TestClass {
let arciver: Archivable = Archiver()
}

Can't create an Array of types conforming to a Protocol in Swift

I have the following protocol and a class that conforms to it:
protocol Foo{
typealias BazType
func bar(x:BazType) ->BazType
}
class Thing: Foo {
func bar(x: Int) -> Int {
return x.successor()
}
}
When I try to create an Array of foos, I get an odd error:
var foos: Array<Foo> = [Thing()]
Protocol Foo can only be used as a generic constraint because it has
Self or associated type requirements.
OK, so it can only be used if it has an associated type requirement (which it does), but for some reason this is an error?? WTF?!
I'm not sure I fully understand what the compiler is trying to tell me...
Let's say, if we could put an instance of Thing into array foos, what will happen?
protocol Foo {
associatedtype BazType
func bar(x:BazType) -> BazType
}
class Thing: Foo {
func bar(x: Int) -> Int {
return x.successor()
}
}
class AnotherThing: Foo {
func bar(x: String) -> String {
return x
}
}
var foos: [Foo] = [Thing()]
Because AnotherThing conforms to Foo too, so we can put it into foos also.
foos.append(AnotherThing())
Now we grab a foo from foos randomly.
let foo = foos[Int(arc4random_uniform(UInt32(foos.count - 1)))]
and I'm going to call method bar, can you tell me that I should send a string or an integer to bar?
foo.bar("foo") or foo.bar(1)
Swift can't.
So it can only be used as a generic constraint.
What scenario requires a protocol like this?
Example:
class MyClass<T: Foo> {
let fooThing: T?
init(fooThing: T? = nil) {
self.fooThing = fooThing
}
func myMethod() {
let thing = fooThing as? Thing // ok
thing?.bar(1) // fine
let anotherThing = fooThing as? AnotherThing // no problem
anotherThing?.bar("foo") // you can do it
// but you can't downcast it to types which doesn't conform to Foo
let string = fooThing as? String // this is an error
}
}
I have been playing with your code trying to understand how to implement the protocol. I found that you can't use Typealias as a generic type because it is just an alias not a type by itself. So if you declare the Typealias outside your protocol and your class you can effectively use it in your code without any problem.
Note: the Typealias has the Int type in its declaration, that way you can always use the alias instead of the Int type and use all of its associated methods and functions.
Here's how I make it work:
typealias BazType = Int
protocol Foo{
func bar(x:BazType) -> BazType
}
class Thing: Foo {
func bar(x: BazType) -> BazType {
return x.successor()
}
}
let elements: Array<Foo> = [Thing(), Thing()]