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
}
}
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'
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.
Hi I'm using generics a lot in my current project. However, I've come across a problem:
I need a generic function foo<T> to be able to take a parameter that conforms to a generic protocol using a specific type.
For example in Java I can do:
public interface Proto<B> {
public void SomeFunction()
}
public class SampleClass {
}
public class Conforms extends Proto<SampleClass> {
#Override
public void SomeFunction () {}
}
public class TestingClass {
public void Requires (Proto<SampleClass> param) {
// I can use param
}
}
How would I do the same Requires() function in Swift?
I know in Swift you use typealias in the protocol for generics. How do I constrain a parameter based on the typealias?
Prototypes don't seem to have generics the way classes and structs do in Swift, but you can have associated types with typealias, which are close.
You can use type constraints to make sure that the object you pass in adopts the Proto with type constraints. To make the Proto you pass into require has the right B, you use where.
Apples documentation on generics has a lot of info.
This blog post is also a good source of info on doing more complicated things with generics.
protocol Proto {
typealias B
func someFunction()
}
class SampleClass {}
class Conforms : Proto {
typealias B = SampleClass
func someFunction() { }
}
class TestingClass {
func requires<T: Proto where T.B == SampleClass>(param: T) {
param.someFunction()
}
}
You can use a where clause to specify multiple requirements for a generic. I think your example translates to this, but it crashes Xcode. Beta!
protocol Proto {
func someFunction()
}
class SampleClass {
}
class Conforms: SampleClass, Proto {
func someFunction() {
}
}
class TestingClass {
func requires<T: SampleClass >(param: T) where T: Proto {
param.someFunction() // it's this line that kills Xcode
}
}
let testingClass = TestingClass()
let conforms = Conforms()
testingClass.requires(conforms)