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.
Related
I would like to make some abstract protocol with generic types
protocol UseCase {
associatedtype P
associatedtype R
func execute(params: P) async throws -> R
}
Then I would like to make some concrete protocol with some types
protocol TestUseCase: UseCase where P == Int, R == Int { }
And then use it to declare some implementation to use it on another side
class TestUseCaseImpl: TestUseCase {
func execute(params: Int) async throws -> Int {
// some impl
}
}
When I try to use it inside of my view model I got such error:
class ViewModel {
private let testUseCase: TestUseCase // error
init(testUseCase: TestUseCase) { // errror
self.testUseCase = testUseCase
}
}
Protocol 'TestUseCase' can only be used as a generic constraint because it has Self or associated type requirements
To fix such problem I can declare generic ViewModel with some use case type like this:
class ViewModel<UseCase: TestUseCase> {
private let testUseCase: UseCase
init(testUseCase: UseCase) {
self.testUseCase = testUseCase
}
}
Problem around ViewModel - it's possible to have many use cases inside of ViewModel.
How to implement such idea without generic ViewModel?
You need to use the any keyword. This is only supported in Swift >= 5.7.
Note: it is very unlikely that you want a type called ViewModel. Nest type Model inside of View if that's what it is.
class ViewModel {
private let testUseCase: any TestUseCase
init(testUseCase: some TestUseCase) {
self.testUseCase = testUseCase
}
}
I'd like to use swift generics in a way described below:
class ResponseContainer {
}
protocol DisplayManageable {
func getModel<ModelType: ResponseContainer>() -> ModelType?
}
class DisplayBaseManager<ObtainedModelType: ResponseContainer>: NSObject, DisplayManageable {
var modelObtained: ObtainedModelType? = nil
func getModel<ObtainedModelType>() -> ObtainedModelType? {
return modelObtained
}
}
But I have a problem with this code, more precisely there is a problem in this line:
return modelObtained
And I'm getting the error:
Cannot convert return expression of type 'ObtainedModelType?' to
return type 'ObtainedModelType?'
And now my simple question, why can't I do that? What's wrong with that?
Generics in protocol's function and in class definitions are the same. Everything looks fine in my opinion and logically is ok, so why cannot I do so?
In
func getModel<ObtainedModelType>() -> ObtainedModelType? { ... }
ObtainedModelType introduces a local generic placeholder, and that
hides the ObtainedModelType from the class definition
class DisplayBaseManager<ObtainedModelType: ResponseContainer>
This causes the strange looking error message
Cannot convert return expression of type 'ObtainedModelType?' to return type 'ObtainedModelType?'
because return modelObtained has the generic type ObtainedModelType? from the
class definition, but the expected return type is ObtainedModelType?
from the method definition.
What you probably want is a protocol with an associated type
protocol DisplayManageable {
associatedtype ModelType: ResponseContainer
func getModel() -> ModelType?
}
and a class adopting this protocol with ModelType == ObtainedModelType:
class DisplayBaseManager<ObtainedModelType: ResponseContainer>: NSObject, DisplayManageable {
var modelObtained: ObtainedModelType? = nil
func getModel() -> ObtainedModelType? {
return modelObtained
}
}
Ćukasz, I don't now why it doesn't compile but I found the way to compile it. Just change the return statement:
return modelObtained as? ObtainedModelType
But I'm still waiting for someone to explain the reason for error in the original code.
I want to be able to have the classes which have a static property (field) which is either inherited from the base class or "mixed" from a protocol. And every class should have it's own implementation of that property. Is it possible? Preferably, it to be immutable.
class C1 {
static let stProperty = "my prorepty1"
}
class C2 {
static let stProperty = "my prorepty2"
}
It's possible, but it's really hard to make this useful in Swift. How do you plan to refer to this property? Let's start with a super-simple implementation:
protocol SomeProtocol {
static var prop: String { get }
}
class C1: SomeProtocol {
static let prop = "This is One"
}
Great. So now I want a function that uses this:
func useProp(x: SomeProtocol) -> String {
return x.prop
// 'SomeProtocol' does not have a member named 'prop'
}
That doesn't work. x is an instance, but I want the type.
// Accessing members of protocol type value 'SomeProtocol.Type' is unimplemented
func useProp(x: SomeProtocol.Type) -> String {
return x.prop
}
This is probably how it will work some day given the word "unimplemented." But it doesn't work today.
func useProp(x: SomeProtocol) -> String {
// Accessing members of protocol type value 'SomeProtocol.Type' is unimplemented
return x.dynamicType.prop
}
Same thing.
Today, you really have to hang this on the object itself and not use static or class:
protocol SomeProtocol {
var prop: String { get }
}
class C1: SomeProtocol {
let prop = "This is One"
}
func useProp(x: SomeProtocol) -> String {
return x.prop
}
That's not so terrible in many cases, since the value for the class is probably also the value for any given instance of the class. And it's really all we can do today.
Of course your problem might be that you don't have an instance yet and you need this information to build an instance. That's really hard today and you should probably rethink your design. You'll generally have to use some other pattern like a Builder. See Generic Types Collection for more.
Now you also said:
or "mixed" from a protocol
I wouldn't say "mixed" here. If you really mean this like a Ruby "mixin", there is no such thing in Swift today. Swift folks often refer to this feature as "default implementation," and it's not currently possible (though I do expect it to come eventually). The only thing you can do in the protocol is say that the implementor has to provide this method somehow. You can't provide it for them.
Sure you can do that with a protocol:
protocol SomeProtocol {
static var foo: String { get }
}
class One: SomeProtocol {
class var foo: String {
get {
return "This is One"
}
}
}
Btw I agree with Rob Napier below that this is a bit off a oddball feature. I do think there are probably use-cases for it, but I also think those can be better implemented with other language features
protocol P {
class var stProperty: String { get }
}
class C1 {
class var stProperty: String {
return = "my property1"
}
}
class C2 {
class var stProperty: String {
return = "my property2"
}
}
Usage:
C2.prop //"my property2"
If you try:
C2.prop = "new value" //"cannot assign to the result of this expression"
In my application written in Swift, I have the following class structure. Class A has a static method which does some stuff, but in a very simple form it looks like the code below.
class A {
class func create<T: A>() -> T? {
println(NSStringFromClass(T));
return nil;
}
}
Class B is subclassed from class A.
class B : A {
}
Now, when I execute the following code, the println command outputs A instead of B.
var myVar:B? = B.create();
I am not sure what I am doing wrong here, but I would expect it to output B.
When debugging and putting a breakpoint in the create method, the value $swift.type.T is defined as a Builtin.RawPointer MyApp.A instead of B.
Your generic class method on A doesn't make sense to me. Instead I would actually use something like the code below. This way it creates an instance of Self, which is whatever class you call it on. No need for generics in this case.
class A {
required init() {}
class func create() -> Self {
return self()
}
func test() -> String {
return "A"
}
}
class B : A {
override func test() -> String {
return "B"
}
}
let b = B.create() // "{A}" according to the playground, but it is a "B" instance!
b.test() // "B"
Please note that A needs a required initializer because the use of Self. When doing it in playground, the created instance is shown as {A} on the right. This is an error in Xcode I believe, the actual type is correct.
Edit:
I believe the code above isn't what you were looking for exactly, now I do get what you're trying to do. I would suggest not doing that by depending on the actual class name, but using a generic class to create the instances for you:
protocol Entity {
init()
class func entityName() -> String
}
class EntityFactory<T : Entity> {
class func newEntity() -> T? {
var entity: T?
// ... create entity here using T.entityName()
return entity
}
}
class Person : Entity {
required init() {}
class func entityName() -> String {
return "Person"
}
}
let person = EntityFactory<Person>.newEntity()
Think this is a more elegant solution, which moves the responsibility of creating an entity to a separate generic class. This results in code that is maintainable and testable. You can even abstract it out further for i.e. unit testing purposes, but that seems a bit out of scope here.
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.