Lets say i have a protocol:
protocol Router {
associatedtype Answer
typealias AnswerCallback = (Answer) -> Void
}
At some point I want to store a variable of type AnswerCallback
var answerCallback: Router.AnswerCallback ...
But I need to specify which concrete type i'm using since i get this error:
Type alias 'AnswerCallback' can only be used with a concrete type or
generic parameter base
How I can specify a type like Router.AnswerCallback ... where String is "Answer" type?
Instead the only way to work is to use var answerCallback: ((String) -> Void)
You need a class/struct that conforms to Router, which has an Answer of String:
class StringRouter : Router {
typealias Answer = String
}
let callback: StringRouter.AnswerCallback? = nil
If all you want is such a type alias, you don't need a protocol:
typealias AnswerCallback<T> = (T) -> Void
Related
The following code use to work for me at Swift 3.2, but with the latest release of Swift 4 I am getting an strange error I can not get to understand.
I am trying to create a generic protocol like so:
public protocol FactoryComponent {
associatedtype Argument
associatedtype Service
static var factory: (Resolver) -> (Argument) -> Service { get }
}
public extension FactoryComponent {
public typealias Factory = (Argument) -> Service
}
And using it here:
public extension Container {
#discardableResult
public func register<Component: FactoryComponent>(
factory componentType: Component.Type
) -> ServiceEntry<Component.Factory> { // On this line the error shows
return self.register(componentType.Factory.self) { resolver in
componentType.factory(resolver)
}
}
}
Error:
'Component' does not have a member type named 'Factory'; did you mean 'Factory'?
And of course, the auto-fix does not help since the error is useless...
I checked out Swift 4 breaking changes and did not see anything involving generic protocols.
Can somone please help me understand what does this mean?
It seems that a concrete class of FactoryComponentis missing. The Factory type alias can be called only by protocol's concrete classes.
Try create an FactoryComponent concrete class that implements its protocol
The Argument and Service still generic on your implementation and needs an concrete type. My english is very bad, but I hope I've been help you. Check the code bellow if I'm wasn't clear on my answer.
```
class Resolver {}
protocol FactoryComponent {
associatedtype Argument
associatedtype Service
static var factory: (Resolver) -> (Argument) -> Service { get }
}
extension FactoryComponent {
typealias Factory = (Argument) -> Service
}
class ConcreteFactoryComponent: FactoryComponent {
static var factory: (Resolver) -> Factory {
return foo
}
static func foo(_ resolver: Resolver) -> Factory {
let factory: Factory = { argument in return "" }
return factory
}
typealias Argument = String
typealias Service = String
}
let factory: ConcreteFactoryComponent.Factory = { argument in return "" }
```
I believe the error is not really on the line with the return type. The processing of generic signature resolution has changed slightly in Swift 4 due to the new default for #escaping. The fact that the function is calling a self.register function with an identical signature as the function itself is likely the source of the issue.
From other similar instances, I would suggest looking at the signature of the Container.register. If it happens to have an #escaping closure, then the signature of the generic function in the extension should also have one so that it is recognized as an overload.
See this post: https://stackoverflow.com/a/43081214/5237560
[EDIT] I just realized this change was between Swift 2 and 3. Leaving the answer here in case it can provide some inspiration.
Here's the simplified code that I have:
class MyClass {
func returnSomething(argument: Protocol2) {}
}
protocol Protocol2: Protocol1 where E == Int {
}
protocol Protocol1 {
associatedtype E
func doSomething(_ value: E)
}
Compiler gives me the following error: Protocol 'Protocol2' can only be used as a generic constraint because it has Self or associated type requirements.
I understand that associated type E needs to be resolved before the protocol can be used as an argument in a function, but given that Protocol2 provides that information, why I still cannot compile that code?
Since I am learning about Swift generics , so I want to give it a go:
If you want to use Protocol2 as the type of argument in the function returnSomething
As suggested by #Nicolas Miari
class MyClass {
func returnSomething<T: Protocol2>(argument: T) {}
}
Now as the name suggests this function should return something
so
class MyClass {
func returnSomething<T: Protocol2>(argument: T) -> T {
return argument
}
}
Another issue that I see in the original problem is the use of where clause
I think you want to say the associated type of Protocol1 is Int
you should do it like this
protocol Protocol2: Protocol1 {
typealias E = Int
}
"How to use associated type protocol as argument's type to a function?"
I might not be following what you're asking here, but if a protocol of yours have an associated type, e.g.
protocol DummyNumericTypeWrapper {
associatedtype U: Numeric
static func magic(_ bar: U) -> U
}
You can access this type e.g. in a generic context where the generic placeholder has been constrained to your protocol. To follow up the dummy wrapper above with an entirely dummy example, e.g.:
extension StupidNumericTypeWrapper where U == Int {
static func magic(_ bar: Int) -> Int {
return bar + 42
}
}
func foo<T: StupidNumericTypeWrapper>(_ bar: T.U, _ _ : T.Type) -> T.U {
return T.magic(bar) * T.magic(bar)
}
struct DefaultMagic: StupidNumericTypeWrapper { }
let num = foo(1, DefaultMagic.self)
print(num) // 1849 (43*43)
Swift compiler converted my typealias to the where clause. (Typealias overriding associated type 'E' from protocol 'Protocol1' is better expressed as same-type constraint on the protocol)
Passing argument indeed works with aforementioned syntax, thank you all!
But how would you return an instance that conforms to Protocol2?
class ClassConformsToProto2: Protocol2 {
func doSomething(_ value: Int) {
}
}
class MyClass {
func returnSomething<T: Protocol2>() -> T {
return ClassConformsToProto2()
}
}
This code doesn't work.
Cannot convert return expression of type 'ClasConformsToProto2' to return type 'T'
I am looking to write a protocol which will be used with a variety of measurement structs. Each of these structs has its own Units enumeration which is used to define the unit types for comparison:
public enum TestUnits: Double {
case foo = 100.0
case var = 1000.0
}
All the enumerations are of type Double and conform to RawRepresentable. I am looking for a way to create a generic protocol property which each Struct can set their own unit enumeration for comparison and formatting:
protocol UnitMeasuable {
var measurementType : SOMETHING<RawRepresentable> { get}
func someFormattingFunc(type: measurementType) -> String
}
I am just unclear how I can declare the measurementType so that it will be set by the individual struct.
Thanks
In this case I would you have two options.
# Use associatedtype on your protocol
protocol UnitMeasuable {
associatedtype Something where Something: RawRepresentable
var measurementType: Something { get}
func someFormattingFunc(type: Something) -> String
}
or go forget about the variable (I see no reason you should have that property, but I really don't know what's your plan) and use a generic in the function.
protocol UnitMeasuable {
func someFormattingFunc<Type: RawRepresentable>(type: Type) -> String
}
Hope this helps!
I'm using Swift and trying to make some collection objects. These collection objects have a backing Dictionary to hold custom objects. For example, an object might be of type Cat and the collection object would be of type Cats. Cats would have a private dictionary containing values of type Cat. I have other types that also need respective collections (each collection type has specific logic for the type it holds).
I created a protocol to ensure each collection has a few common characteristics. These common functions and subscripts are generally passthroughs to the backing dictionary. Here is the protocol:
protocol ObjectDictionaryProtocol {
// These are necessary for generics to work in protocols
typealias Key: Hashable
typealias Value
// MARK: - Properties
var count: Int { get }
var isEmpty: Bool { get }
var keys: LazyMapCollection<Dictionary<Key, Value>, Key> { get }
var values: LazyMapCollection<Dictionary<Key, Value>, Value> { get }
// MARK: - Subscripts
subscript(key: Key) -> Value? { get set }
}
When I go to actually use the protocol as a type, for instance:
var objects: ObjectDictionaryProtocol
or
init(objs: ObjectDictionaryProtocol) {
...
}
I get the error:
Protocol 'ObjectDictionaryProtocol' can only be used as a generic constraint because it has Self or associated type requirements
I've searched around and it looks like the Hashable protocol I'm conforming to for my Key typealias is causing this. What is the best way to get around this? Is there a way to change the protocol such that I don't need the Hashable, or do I need to do something in the class that is using ObjectDictionaryProtocol? Or maybe there's a better way to effectively 'subclass' a Swift Dictionary (quotes because I realize the Dictionary struct cannot be subclassed)?
A protocol with associated types is less a type and more a template for a type. The actual type exists when the protocol's associated types are specified. Thus ObjectDictionaryProtocol 'becomes' a type when you specify:
ObjectDictionaryProtocol<String,Cat>
but the above is not valid Swift...
So you asked... '[is there] a better way to effectively 'subclass' a Swift Dictionary'. You might get by with an extension with something like:
class Cat {}
extension Dictionary where Value : Cat {
func voice (name: Key) {
if let _ = self[name] {
print ("\(name): Meow")
}
}
}
var someCats = Dictionary<String,Cat>()
someCats["Spot"] = Cat()
someCats.voice("Spot")
// Spot: Meow
Or you may need to actually implement the protocol.
class ObjectDiciontary<Key:Hashable, Value> : ObjectDictionaryProtocol {
var backingDictionary = Dictionary<Key,Value>()
// implement protocol
}
var object : ObjectDictionary<String,Cat> = ...
// ...
The reason is because when you're using typealias you're effectivly making your Protocol into a generic protocol as Protocol<T>. Bear with me here, I'll explain why and how to fix it.
The issue here is that Apple has decided to make typealias the keyword for defining associated types. This is fixed in Swift 2.2 (Xcode 7.3)
You can read more about it on https://github.com/apple/swift-evolution/blob/master/proposals/0011-replace-typealias-associated.md
It's being renamed to associatedtype which makes more sense.
This means your protocol has to be adopted, and there define its associated types.
In your case it would look something like
protocol ObjectDictionaryProtocol {
associatedtype Value
}
extension String : ObjectDictionaryProtocol {
associatedtype = Double
}
The init could look like
init<T : ObjectDictionaryProtocol>(objs: ObjectDictionaryProtocol)
or
init<T : ObjectDictionaryProtocol
where T.Value == Double>(objs: ObjectDictionaryProtocol)
Now for typealias Key: Hashable it means that whatever type that is assigned to Key has to conform or be Hashable
This will give you an error that String isn't conforming to ObjectDictionaryProtocol as it can't fulfil the requirements.
protocol ObjectDictionaryProtocol {
associatedtype Value : FloatingPointType
}
extension String : ObjectDictionaryProtocol {
associatedtype = Int
}
Int isn't a FloatingPointType (but Double is)
I want to typealias a dictionary of String keys and values of objects/structs that implements the Equatable protocol. So I wrote this line of code but it gave me error that I didn't know how to go on to fix.
typealias Storage = [String: Equatable]
I want to use the type [String: Equatable] as a variable in a protocol, e.g:
protocol StorageModel {
var storage: Storage { get set }
init(storage: Storage)
}
Error:
Protocol 'Equatable' can only be used as a generic constraint because
it has Self or associated type requirements
Can anyone suggest a solution?
Generally speaking, the protocol tag isn't required, protocol names are first-class type names and can be used directly:
typealias Storage = [String:Equatable]
In this case, what the error is telling you is that because Equatable includes func == (lhs:Self, rhs:Self) -> Bool and specifically lhs:Self, Equatable can't be used except as a constraint on a generic:
class Generic<T:Equatable> { ... }
Without more details about what you're trying to achieve and how you're trying to use StorageModel, the best I can come up with is:
protocol Matches {
typealias T
func matches(t:T) -> Bool
}
protocol StorageModel {
typealias T
var storage: [String:T] { get set }
init(storage:[String:T])
}
extension Int : Matches {
func matches(target:Int) -> Bool {
return self == target
}
}
class MyClass <T:Matches> {
var model = [String:T]()
}
Another possibility is to use a generic instead of a protocol:
class StorageModel <T:Equatable> {
var storage: [String:T]
init(storage:[String:T]) {
self.storage = storage
}
}
From there you'll need to do some research, dig into the Swift manual, do some googling and see what solves your problem.