What is the problem with my generic protocol in Swift? - ios

Couldn't get my head around how protocol generic types work in Swift. Same solution in Java or Kotlin would work without problems.
protocol ResultProtocol {
associatedtype T: ResultTypeProtocol
var result: T? { get set }
}
protocol ResultTypeProtocol {
func didGet()
}
protocol InteractorProtocol: ResultProtocol where T == InteractorResultProtocol {
func someLogicRelatedToInteractor()
}
protocol InteractorResultProtocol: ResultTypeProtocol {
func interactorResult()
}
class Interactor: InteractorProtocol {
typealias T = InteractorResultProtocol
var result: InteractorResultProtocol?
func someLogicRelatedToInteractor() {}
}
I get 2 errors in my code.
First one is when I put where generic constraint on another protocol
Second error is that my Interactor class doesn't conform to the protocol. When I click fix it will add another 'typealias T = type' and want me specify T again.
I want to know if there is another way to achieve this in Swift or how to fix this problem.
Idea behind this is to extend my interactor classes with generic property result which is used as delegate for other layers. Interactor is used via his protocol and it's injected in all other classes.

As #vadian comment says: "The type of the typealias must be a concrete type, not a protocol".
But if you want this class to be used with different InteractorResultProtocol then you can use generic for Interactor class also. And define T later.
class Interactor<ResultProtocolType: InteractorResultProtocol>: InteractorProtocol {
typealias T = ResultProtocolType
var result: ResultProtocolType?
func someLogicRelatedToInteractor() {}
}
Usage:
struct MyInteractorResultProtocol: InteractorResultProtocol {
func interactorResult() {}
func didGet() {}
}
let interactor = Interactor<MyInteractorResultProtocol>()

Related

Swift UI use View in a Protocol (Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements)

I want to use View in a Protocol.
protocol Test {
var view: View { get }
}
Protocol 'View' can only be used as a generic constraint because it
has Self or associated type requirements
I just want to do the same thing as with my ViewController. Any idea?
protocol Test {
var viewController: UIViewController { get }
}
If I use an associated type, I get the error in my other protocols.
protocol Test2: Test { 
//STUB
}
Any idea how to solve this problem? Thanks :)
SwiftUI.View is a protocol, and because it uses Self (for example, in its body property), you cannot directly declare a property type as a View.
You can define an associated type on Test and constrain that type to be a View:
protocol Test {
associatedtype T: View
var view: T { get }
}
You can't directly use the protocol unless you declare it as associated type, but you can use the type erased AnyView instead:
protocol Test {
var view: AnyView { get }
}
Creating an AnyView instance might add some noise in the code, however it's easy to create.
Extending Cristik's solution:
protocol ViewFactoryProtocol {
func makeView(parameter: SomeType) -> AnyView
}

Dependency injection with associated types causing arguments without type names (undescores) in Swift

The situation is following: I'm using a protocol to inject dependencies and the best way I found to implement this in Swift is to use the associatedtype keyword. I am also using protocol composition since some implementations of TestProtocol need more than one dependency.
protocol TestProtocol: class {
associatedtype Dependencies
func inject(_ dependency: Dependencies)
}
protocol HasSomething {
var something: Something { get set }
}
protocol HasSomethingElse {
var somethingElse: SomethingElse { get set }
}
To use this I found that I'll need to use generics like this:
class TestService<T> where T: TestProtocol, T.Dependencies == TestService {
weak var testProtocol: T?
init(with testProtocol: T) {
self.testProtocol = testProtocol
self.testProtocol?.inject(self)
}
}
Now when I want to use this service somewhere else and I'm trying to initiate it I get following problem:
The parameter is displayed as _ and not as the protocol name TestProtocol.
Let's say I would use this code in a library. How would a user know (without reading the documentation of course) what type could be used in this context when he is not even knowing what protocol he has to implement?
Is there a better way on how to use dependency injection with the type actually being displayed to the user, or am I doing something wrong in the where clause of the TestService class, or is this simply not possible in the current versions of Swift?
There is nothing wrong with your code, this is simply not possible.
class TestService<T> where T: TestProtocol
The where clause means T could be anything, with the constraint that the given object must conform to TestProtocol.
The Xcode autocomplete feature only displays the resolved type when available, but it doesn't show the constraints on a generic, and unfortunately there is nothing you can do about that.
You have the exact same issue in the swift standard library, with Dictionary for example
public struct Dictionary<Key, Value> where Key : Hashable {
public init(dictionaryLiteral elements: (Key, Value)...) {
// ..
}
}
The generic Key as a constraint to Hashable, but Xcode still shows _ in the autocomplete list.
I guess Swift developers are use to this behaviour, so it won't be a big issue, even if your code is embedded in a library.
How would a user know (without reading the documentation of course) what type could be used in this context when he is not even knowing what protocol he has to implement?
Because Xcode is pretty clear about the protocol requirement.
If I try to initialize the TestService with a String I'll get the error:
Referencing initializer 'init(with:)' on 'TestService' requires that 'String' conform to 'TestProtocol'
Which is pretty self explanatory.
Actually at the time of init(with testProtocol: T) Compiler doesn't know about T of course because it is generic
if you provide directly class it will show you in suggestion
For example
class TestService<T:Something> {
weak var testProtocol: T?
init(with testProtocol: T) {
self.testProtocol = testProtocol
}
}
Now you will see compiler know that it need SomeThing at T
For your case For TestProtocol You can replace with with something user readable world. for next time compiler will give you provided type as suggestion
For Example
class TestService<T:TestProtocol> {
weak var testProtocol: T?
init(with testProtocol: T) {
self.testProtocol = testProtocol
}
func add(t:T) {
}
}
class Test {
init() {
let t = Something()
let ts = TestService(with: t)
}
}
In Test class you can type ts.add now it knows

Factory pattern with different protocols in swift

I am trying to use factory pattern in swift, and give is my code
I have two protocols
protocol MyProtocol1{
func callLoginAPI()
}
protocol MyProtocol2{
func callPaymentAPI()
}
I have two structures which conform to these protocols
struct MyManager1: MyProtocol1{
func callLoginAPI()
{
debugPrint("Inisde--MyManager1 and ready to call an Login API")
}
}
struct MyManager2: MyProtocol2{
func callPaymentAPI()
{
debugPrint("Inisde--MyManager2 and ready to call Payment API")
}
}
I want to use the factory pattern to create the instance of Manager by passing the Protocol and returning a concrete object of the struct that conforms to that protocol
Example: ManagerFactory.create(MyProtocol1) --> should give me instance of MyManager1 and doing ManagerFactory.create(MyProtocol2) --> should give me instance of MyManager2
I assume that this may not work as I am asking for a concrete type at runtime, so I ended up doing something like this
protocol IManagerFactory{
func getMyManager1() -> MyProtocol1
func getMyManager2() -> MyProtocol2
}
struct ManagerFactory: IManagerFactory
{
func getMyManager1() -> MyProtocol1 {
return MyManager1()
}
func getMyManager2() -> MyProtocol2 {
return MyManager2()
}
}
But I am curious if what I am trying to do in the example is achievable, I am using swift 4.2.
I have seen other examples but all of them have the same protocol which they conform to like rectangle, square and circle conform to same protocol shape.
In my case, I have two separate protocols which do completely different things so is that even possible what I am trying to do in the example? OR the way I ended up with is the only way to go about it.
Please suggest what is the best possible approach.
First, I'm very suspicious of a "manager" that is a value (a struct) rather than an instance (a class). Do you really mean to be using structs and protocols here at all? Structs have no identity; two structs with the same properties must be completely interchangeable for each other, and things like that don't usually get named "manager."
What you're describing is certainly writeable. It's just kind of useless. Here's how you write it:
struct ManagerFactory {
static func create(_ type: MyProtocol1.Protocol) -> MyProtocol1 {
return MyManager1()
}
static func create(_ type: MyProtocol2.Protocol) -> MyProtocol2 {
return MyManager2()
}
}
let mgr1 = ManagerFactory.create(MyProtocol1.self)
let mgr2 = ManagerFactory.create(MyProtocol2.self)
But this is just an elaborate way to use method overloading to replace names.
What you seem to want is a single method, but what would its return type be? Even in C#, I don't think this is writable without adding nasty downcasts. (This is the point you and paulw11 discuss in the comments.) This isn't a limitation of Swift; it's a fundamental characteristic of types. Even in JavaScript, you'd need to know what you expect to get back or else you won't know what methods you can call on it (it's just you track that expectation in your head and docs rather than in the compiler and code).
You seem to have created a lot of protocols here, and I'm betting they mostly have exactly one implementation. That's bad Swift. Don't create protocols until you have a specific need for one. Don't create attractions for fun. They will burn you very quickly.
If your protocols really look like this, and you really have different implementations of these methods (i.e. don't do this if there is only one implementation of MyProtocol1 "just in case"), what you really want are functions:
struct API {
let login: () -> Void
let payment: () -> Void
}
let myAPI = API(login: myLoginFunction, payment: myPaymentFunction)
This will let you create different APIs if you need them. But again, only if you need this flexibility. If not, just use classes for instances, and structs for values, and avoid protocols until you have at least 2, and better 3, implementations in your program.
Suggest a swifty option:
protocol Manager {
// a generic protocol for all managers
}
protocol BadManager: Manager {
// properties and behaviour of BadManager
mutating func noBonus()
}
protocol GoodManager: Manager {
// properties and behaviour of GoodManager
mutating func bigBonus()
}
protocol ManagerFactory {
associatedtype Manager
mutating func createManager() -> Manager
}
struct gm: GoodManager {
mutating func bigBonus() {
print("tons of cookies & coffee")
}
}
struct bm: BadManager {
mutating func noBonus() {
print("all fired")
}
}
enum ManagerCreator<Manager>: ManagerFactory {
func createManager() -> Manager {
switch self {
case .goodManager:
return gm() as! Manager
case .badManager:
return bm() as! Manager
}
}
case goodManager
case badManager
}

How can I create a Set of delegate protocol items in Swift?

Let's assume I have five UIView objects which all conform to a particular protocol. I have an object which should maintain a list of these objects, and message them all when necessary.
protocol MyProtocol: AnyObject {
func doSomething()
}
The problem is, when I go to add these UIViews to a Set variable, the compiler produces an error because MyProtocol does not conform to Hashable. I can understand the reasoning for this, can anyone think of good ways to overcome this? In the meantime I considered using NSHashTable instead, but you lose the nice enumeration features of Sets.
Updating answer to post some sample code (this is still not working)
protocol MyProtocol: class, AnyObject {
func doSomething()
}
class MyClass {
var observers: Set<MyProtocol> = Set<MyProtocol>()
}
As you are defining protocol for class so you need to write 'class' keyword before inheriting any other protocol:
protocol MyProtocol: AnyObject, Hashable{
func doSomething()
}
class MyClass<T: MyProtocol> {
var observers: Set<T> = Set<T>()
}
Change your protocol to this and it will work fine.
You can refer Apple Documentation for further details.

Override var conforming to a protocol with a var conforming to a child of the overridden var protocol

This is my inheritance structure
Protocols
protocol BaseProtocol {
}
protocol ChildProtocol: BaseProtocol {
}
Classes
class BaseClass: NSObject {
var myVar: BaseProtocol!
}
class ChildClass: BaseClass {
override var myVar: ChildProtocol!
}
I'm receiving a compiler error:
Property 'myVar' with type 'ChildProtocol!' cannot override a property with type 'BaseProtocol!'
What is the best approach to achieve this?
UPDATE
I updated the question trying to implement the solution with generics but it does not work :( This is my code (now the real one, without examples)
Protocols
protocol TPLPileInteractorOutput {
}
protocol TPLAddInteractorOutput: TPLPileInteractorOutput {
func errorReceived(error: String)
}
Classes
class TPLPileInteractor<T: TPLPileInteractorOutput>: NSObject, TPLPileInteractorInput {
var output: T!
}
And my children
class TPLAddInteractor<T: TPLAddInteractorOutput>: TPLPileInteractor<TPLPileInteractorOutput>, TPLAddInteractorInput {
}
Well, inside my TPLAddInteractor I can't access self.output, it throws a compiler error, for example
'TPLPileInteractorOutput' does not have a member named 'errorReceived'
Besides that, when I create the instance of TPLAddInteractor
let addInteractor: TPLAddInteractor<TPLAddInteractorOutput> = TPLAddInteractor()
I receive this other error
Generic parameter 'T' cannot be bound to non-#objc protocol type 'TPLAddInteractorOutput'
Any thoughts?
#tskulbru is correct: it can't be done, and this has nothing to do with your protocols. Consider the example below, which also fails…this time with Cannot override with a stored property 'myVar':
class Foo {
}
class Goo: Foo {
}
class BaseClass: NSObject {
var myVar: Foo!
}
class ChildClass: BaseClass {
override var myVar: Foo!
}
To understand why, let's reexamine the docs:
Overriding Properties
You can override an inherited instance or class property to provide
your own custom getter and setter for that property, or to add
property observers to enable the overriding property to observe when
the underlying property value changes.
The implication is that if you are going to override a property, you must write your own getter/setter, or else you must add property observers. Simply replacing one variable type with another is not allowed.
Now for some rampant speculation: why is this the case? Well, consider on the one hand that Swift is intended to be optimized for speed. Having to do runtime type checks in order to determine whether your var is in fact a Foo or a Bar slows things down. Then consider that the language designers likely have a preference for composition over inheritance. If both of these are true, it's not surprising that you cannot override a property's type.
All that said, if you needed to get an equivalent behavior, #tskulbru's solution looks quite elegant, assuming you can get it to compile. :)
I don't think you can do that with protocols
The way i would solve the problem you are having is with the use of generics. This means that you essentially have the classes like this (Updated to a working example).
Protocols
protocol BaseProtocol {
func didSomething()
}
protocol ChildProtocol: BaseProtocol {
func didSomethingElse()
}
Classes
class BaseClass<T: BaseProtocol> {
var myProtocol: T?
func doCallBack() {
myProtocol?.didSomething()
}
}
class ChildClass<T: ChildProtocol> : BaseClass<T> {
override func doCallBack() {
super.doCallBack()
myProtocol?.didSomethingElse()
}
}
Implementation/Example use
class DoesSomethingClass : ChildProtocol {
func doSomething() {
var s = ChildClass<DoesSomethingClass>()
s.myProtocol = self
s.doCallBack()
}
func didSomething() {
println("doSomething()")
}
func didSomethingElse() {
println("doSomethingElse()")
}
}
let foo = DoesSomethingClass()
foo.doSomething()
Remember, you need a class which actually implements the protocol, and its THAT class you actually define as the generic type to the BaseClass/ChildClass. Since the code expects the type to be a type which conforms to the protocol.
There are two ways you can go with your code, depending what you want to achieve with your code (you didn't tell us).
The simple case: you just want to be able to assign an object that confirms to ChildProtocol to myVar.
Solution: don't override myVar. Just use it in ChildClass. You can do this by design of the language Swift. It is one of the basics of object oriented languages.
Second case: you not only want to enable assigning instances of ChildProtocol, you also want to disable to be able to assign instances of BaseProtocol.
If you want to do this, use the Generics solution, provided here in the answers section.
If you are unsure, the simple case is correct for you.
Gerd

Resources