Using array of protocol in Swift class' generics - swift

Is there any way to use array of protocol's generic?
For example,
/* I want to use protocol like below,
* but I can't because protocol is not concrete
* so cannot make array of it */
class MyClass<T where T:MyProtocol, T:[MyProtocol]> {
let result: T
}
protocol MyProtocol {
init(with: String)
}
class SpecialThing: MyProtocol {
let appleWatch: AppleWatch
init(with: String) {
self.appleWatch = AppleWatch(with)
}
}
class SampleClass {
func test {
typealias listCallback = (MyClass<[SpecialThing]>, NSError) -> ()
typealias oneCallback = (MyClass<SpecialThing>, NSError) -> ()
}
}
There can be one object or array of protocol's subclass.
I think "typealias" does not help me.
I want to find something more simple way.....

My first issue with this is that the type signature is wrong:
class MyClass<T where T:MyProtocol, T:[MyProtocol]>
That's the same type of thing as doing:
let t: String
let t: [String]
t = String("foo")
The compiler will complain because you are redefining T, once as a MyProtocol and again as an array of MyProtocol. You can't have both, you can only have one.

Answer: Use a construct like Either:
enum Either<T, U>
{
case Left(T)
case Right(U)
var leftValue: T?
{
if case .Left(let leftValue) = self
{
return leftValue
}
return nil
}
var rightValue: U?
{
if case .Right(let rightValue) = self
{
return rightValue
}
return nil
}
}
Allowing for:
class MyClass<T: MyProtocol>
{
let result: Either<T, [MyProtocol]>
}

Related

Swift: Protocol `static var foo: Self` and enums

Simplified example
Take a look at this simple protocol
protocol FooOwner {
static var foo: Self { get }
}
I would like an enum to conform to said protocol, and in my opinion this ought to work, since the static syntax SomeTypeConformingToFooOwner.foo should result in an instance of SomeTypeConformingToFooOwner and in the case where SomeTypeConformingToFooOwner is a enum.
enum Foo: FooOwner { // Type 'Foo' does not conform to protocol FooOwner
case foo
}
The workaround is this ugly thing:
protocol FooOwner {
static var fooOwner: Self { get }
}
enum Foo: FooOwner {
case foo
static var fooOwner: Foo {
return Foo.foo
}
}
Do you have a nicer workaround for enum conforming to protocols with static vars?
Real use case
protocol StringConvertibleError: Swift.Error {
static var invalidCharactersError: Self { get }
}
protocol StringConvertibleErrorOwner {
associatedtype Error: StringConvertibleError
}
protocol StringConvertible {
var value: String { get }
/// Calling this with an invalid String will result in runtime crash.
init(validated: String)
init(string value: String) throws
static func validate(_ string: String) throws -> String
}
// MARK: - Default Implementation Constrained
extension StringConvertible where Self: CharacterSetSpecifying, Self: StringConvertibleErrorOwner {
static func validate(_ string: String) throws -> String {
guard Self.allowedCharacters.isSuperset(of: CharacterSet(charactersIn: string)) else {
throw Error.invalidCharactersError
}
// Valid
return string
}
}
struct HexString: StringConvertible, CharacterSetSpecifying, StringConvertibleErrorOwner {
static var allowedCharacters = CharacterSet.hexadecimal
let value: String
init(validated unvalidated: String) {
do {
self.value = try HexString.validate(unvalidated)
} catch {
fatalError("Passed unvalid string, error: \(error)")
}
}
}
extension HexString {
enum Error: StringConvertibleError {
static var invalidCharactersError: Error {
return Error.invalidCharacters
}
case invalidCharacters
}
}
So it's the last part that I would like to change to:
extension HexString {
enum Error: StringConvertibleError {
case invalidCharacters
}
}
Since I have many similar types to HexString.
Yes of course obviously I can use one shared Error enum, but I would like to have one specific enum per type.
SE-0280 solves this - if accepted. It is currently in review.
Edit 1
Great news, SE-0280 has been accepted.
This might not fully match OP's issue, but as it seems somewhat related, I'm throwing it out there in case it helps.
In my case, I cared about some cases, but other cases could be ignored (or handled in a common way).
I admit my approach is not elegant and requires a lot of boiler plate, but it got me past a similar problem.
// Given these enums, we want a protocol that matches enums with
// 'foo' and 'bar' cases.
enum FooFriends {
case foo // conforms
case bar // conforms
case baz // don't really care
}
enum FooBar {
case foo // conforms
case bar // conforms
}
// We wish we could do this:
protocol FooBarWish {
case foo // or static var foo: Self { get }
case bar // or static var bar: Self { get }
}
// Workaround
// the boiler plate
enum FooBarProxy {
case foo
case bar
case other
}
protocol FooBarProtocol {
func getProxy() -> FooBarProxy
}
extension FooFriends: FooBarProtocol {
func getProxy() -> FooBarProxy {
switch self {
case .foo:
return .foo
case .bar:
return .bar
default:
return .other
}
}
}
extension FooBar: FooBarProtocol {
func getProxy() -> FooBarProxy {
switch self {
case .foo:
return .foo
case .bar:
return .bar
}
}
}
// Usage
// Instead of the ideal case (which won't work)
let fooBarOrFooFriend1: FooBarWish = FooFriends.foo
let fooBarOrFooFriend2: FooBarWish = FooBar.bar
// We can get by with
let fooBarProxy1 = FooFriends.foo.getProxy()
let fooBarProxy2 = FooBar.bar.getProxy()
// Verification
func checkIt(_ foobar: FooBarProxy) {
switch foobar {
case .foo:
print("it was foo")
case .bar:
print("it was bar")
case .other:
print("it was neither")
}
}
checkIt(fooBarProxy1)
checkIt(fooBarProxy2)
// =>
// it was foo
// it was bar

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 :)

Generics and Protocols in Swift: Why does my $0 have no expectation member?

In the code below, when I try to let expectations = stuffToExpect.map({ $0.expectation }) the compiler says Value of tuple type '(key: _, value: HasExpectations)' has no member 'expectation'.
What is the correct way to use map in with a generic type?
import XCTest
import Foundation
protocol HasExpectations {
var expectation: XCTestExpectation { get }
}
public class A: HasExpectations {
var expectation: XCTestExpectation
init(expectation: XCTestExpectation) {
self.expectation = expectation
}
}
public class B: HasExpectations {
var expectation: XCTestExpectation
init(expectation: XCTestExpectation) {
self.expectation = expectation
}
}
func doit<T>(stuffToExpect: [T: HasExpectations]) {
let expectations = stuffToExpect.map({ $0.expectation })
}
</pre>
In your function
func doit<T>(stuffToExpect: [T: HasExpectations]) {
let expectations = stuffToExpect.map({ $0.expectation })
}
stuffToExpect is of type [T: HasExpectations] aka Dictionary<T: HasExpectations>. When you map over a dictionary it gives you a tuple of type (key: T, value: HasExpectations) back which is why you are seeing that error.
I think you instead wanted to constrain T instead and have stuffToExpect as an array, in which case the syntax is either of these (pick which you think looks best):
func doit<T: HasExpectations>(stuffToExpect: [T]) {
let expectations = stuffToExpect.map({ $0.expectation })
}
// or
func doit<T>(stuffToExpect: [T]) where T: HasExpectations {
let expectations = stuffToExpect.map({ $0.expectation })
}
You described Tas generic. That is correct. Now you want to say I am happy to accept any type T which conforms to protocol HasExpectations. That smeans <T: HasExpectation>.
So your function going to look like below
func doit<T: HasExpectations>(stuffToExpect: [T]) {
let expectations = stuffToExpect.map({ $0.expectation })
}
You got a compilation error because when you specify [T: HasExpections]. The compiler treats as a dictionary, which is not the case here.
Call mapValues over the expectation,
func doit<T>(stuffToExpect: [T: HasExpectations]) {
let expectations = stuffToExpect.mapValues { $0.expectation }
// here your expectations is a new dictionary of [Hashable: XCTestExpectation]
// also note that T is turned to Hashable since you cannot have stuffToExpect key with T, which is not actually Hashable.
// you can get all test expectations using values
let testExpectations = Array(expectations.values)
// here testExpectations is [XCTestExpectation]
}
My real problem was my function declaration:
func doit<T>(stuffToExpect: [T: HasExpectations]) {
What I really wanted to do was say that T conformed to HasExpectations:
func doit<T: HasExpectations>(stuffToExpect: [T: HasExpectations]) {

Different return types from protocol method

I would like to have a protocol that defines some methods and properties. However, property types and method return types may vary between the different classes that conform to said protocol. For example: A.getContent() may return a value of type String, but B.getContent() may return a value of type Int. In my example below, I used the type Any. Is this possible in Swift or is this a totally wrong approach? Maybe with generics?
protocol Content {
func getContent() -> any
}
class A: Content {
func getContent() -> String {
return "Im a String"
}
}
class B: Content {
func getContent() -> Int {
return 1234
}
}
I think you are looking about Generics in Protocol.
You can associate a type dynamically with associatedtype, for example
protocol Content{
associatedtype T
func getContent()-> T
}
class A: Content {
func getContent() -> String {
return "Hello World"
}
}
class B: Content {
func getContent() -> Int {
return 42
}
}
A().getContent() //"Hello World"
B().getContent() //42
If you look at this example when you put the Type after the function in the class sun of Content, the protocol Content will be this one type
Update
I am putting another example using "swiftly" syntax instead of traditional getContent.
protocol Content{
associatedtype T
var content:T { get }
}
class A: Content {
var content:String{
return "Hello World"
}
}
class B: Content {
var content:Int{
return 42
}
}
A().content //"Hello World"
B().content //42
You can use generics and meta-types:
protocol Content {
func getContent<T>(ofType: T.Type) -> T?
}
class A: Content {
func getContent<T>(ofType: T.Type) -> T? {
return "Im a String" as? T ?? nil
}
}
class B: Content {
func getContent<T>(ofType: T.Type) -> T? {
return 1234 as? T ?? nil
}
}
let aClass = A()
let aValue = aClass.getContent(ofType: String.self) // "Im a String"
let bClass = B()
let bValue = bClass.getContent(ofType: Int.self) // 1234

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()]