Can one Swift typealias be constrained to another in a protocol definition? If not, how else can I achieve something like component registration? - swift

I am writing a small inversion of control container for my own little framework in swift (mainly so I can learn more) and I have stumbled across a problem - well, several problems of which this is just the latest.
I had hoped that swift would be flexible enough for me to port the core patterns of Castle.Core in C#. My first disappointment was in the weak reflection capabilities provided by Apple, which led me to do this ugliness...
public protocol ISupportInjection {
}
public protocol IConstructorArguments {
func toArgumentsDictionary() -> [String:AnyObject?]
}
public protocol ISupportConstructorInjection: ISupportInjection {
init(parameters:IConstructorArguments)
}
...the idea being that someday (soon) I could deal with it and remove any reference to these constraints in my services/components.
Now I want to write IIocRegistration with two typealias: one for the TService and the other for the TComponent, ideally where TService is a protocol and TComponent is a concrete struct or class that implements TService:
public protocol IIocRegistration {
typealias TService: Any
typealias TComponent: TService, ISupportConstructorInjection
}
But it seems that TComponent: TService is totally invalid according to the compiler, which says:
Inheritance from non-protocol, non-class type '`Self`.TService'
So I am wondering how to make a typealias derive another typealias if it possible.

First of all what typealias do inside a swift protocol is not to define a type alias. The keyword typealias inside swift protocol is used to define associated type about which you can check out the swift programming book.
Back to your situation the possible solution I can come up with is move the typealias outside protocol like such
public typealias TService = Any
public struct component: TService, ISupportConstructorInjection {
public init(parameters: IConstructorArguments) {
//
}
}
public typealias TComponent = component
public protocol IIocRegistration {
var service: TService {get set}
var component: TComponent {get set}
}

Related

What's the point of conforming to an empty protocol in Swift

I'm just working on a codebase where the most classes conform to an empty protocol. Is there any point of doing so? Actually conforming that protocol does nothing.
Edit as reaction to #vacawama's helpful comment : the protocol is only used once
import Foundation
protocol myUrlRequestConfig {}
extension myUrlRequest: myUrlRequestConfig {
public static func configGetAppConfig() -> URLRequest {
let request = ....
return request
}
}
An empty protocol can be useful if you want to create an easy way to add static functions or variables to several different classes. Of course, this only makes sense if the static functionality is the same for all classes conforming to the protocol.
Here is an example: I have a protocol sizeable. Every class that conforms to Sizeable is extended with the static variable stride and size.
protocol Sizeable { }
extension Sizeable {
static var size: Int {
return MemoryLayout<Self>.size
}
static var stride: Int {
return MemoryLayout<Self>.stride
}
}
class Foo: Sizeable {}
class Baa: Sizeable {}
Foo.size
Baa.size
Our app has three perfectly legitimate uses for an empty protocol:
The protocol has an extension with method definitions. The implementation in the extension is automatically injected into any type that adopts the protocol.
The protocol has other protocols that adopt it. This allows multiple protocols to be unified under a single head.
The protocol marks the adopter as wanting some other type to behave in a certain way. Code can always is whether an object is ThisEmptyProtocol and if it is, can behave differently than if it isn't.

Swift Generics - Attempting to make a generic protocol concrete fails when attempting to use specialised sub-protocol as variable

I want to know why my SomeResourceRepository is still generic, even though it is only defined in one case only, which is when I set ResourceType = SomeResource, which XCode formats as below with the where clause. Code below which shows the exact setup I'm trying to achieve, written in a Playground.
I am trying to define a generic protocol for any given ResourceType such that the ResourceTypeRepository protocol then automatically requires the same set of functions, without having to copy-paste most of GenericRepository only to manually fill in the ResourceType for each Repository I make. The reason I need this as a protocol is because I want to be able to mock this for testing purposes later. So I'll provide an implementation of said protocol somewhere else in the actual app.
My interpretation of the code below is that it should work, because both SomeResourceLocalRepository and SomeResourceRemoteRepository are concrete, as I have eliminated the associated type by defining them "on top of" SomeResourceRepository, which is only defined where ResourceType == SomeResource.
import Foundation
struct SomeResource: Identifiable {
let id: String
let name: String
}
struct WhateverResource: Identifiable {
let id: UUID
let count: UInt
}
protocol GenericRepository: class where ResourceType: Identifiable {
associatedtype ResourceType
func index() -> Array<ResourceType>
func show(id: ResourceType.ID) -> ResourceType?
func update(resource: ResourceType)
func delete(id: ResourceType.ID)
}
protocol SomeResourceRepository: GenericRepository where ResourceType == SomeResource {}
protocol SomeResourceLocalRepository: SomeResourceRepository {}
protocol SomeResourceRemoteRepository: SomeResourceRepository {}
class SomeResourceLocalRepositoryImplementation: SomeResourceLocalRepository {
func index() -> Array<SomeResource> {
return []
}
func show(id: String) -> SomeResource? {
return nil
}
func update(resource: SomeResource) {
}
func delete(id: String) {
}
}
class SomeResourceService {
let local: SomeResourceLocalRepository
init(local: SomeResourceLocalRepository) {
self.local = local
}
}
// Some Dip code somewhere
// container.register(.singleton) { SomeResourceLocalRepositoryImplementation() as SomeResourceLocalRepository }
Errors:
error: Generic Protocols.xcplaygroundpage:45:16: error: protocol 'SomeResourceLocalRepository' can only be used as a generic constraint because it has Self or associated type requirements
let local: SomeResourceLocalRepository
^
error: Generic Protocols.xcplaygroundpage:47:17: error: protocol 'SomeResourceLocalRepository' can only be used as a generic constraint because it has Self or associated type requirements
init(local: SomeResourceLocalRepository) {
I will probably have to find another way to accomplish this, but it is tedious and quite annoying as we will produce a lot of duplicate code, and when we decide to change the API of our repositories, we will have to manually change it for all the protocols as we don't follow a generic "parent" protocol in this work-around.
I have read How to pass protocol with associated type as parameter in Swift and the related question found in an answer to this question, as well as Specializing Generic Protocol and others.
I feel like this should work, but it does not. The end goal is a concrete protocol that can be used for dependency injection, e.g. container.register(.singleton) { ProtocolImplementation() as Protocol } as per Dip - A simple Dependency Injection Container, BUT without copy-pasting when the protocol's interface clearly can be made generic, like in the above.
As swift provides a way to declare generic protocols (using associatedtype keyword) it's impossible to declare a generic protocol property without another generic constraint. So the easiest way would be to declare resource service class generic - class SomeResourceService<Repository: GenericRepository>.
But this solution has a big downside - you need to constraint generics everywhere this service would be involved.
You can drop generic constraint from the service declaration by declaring local as a concrete generic type. But how to transit from generic protocol to the concrete generic class?
There's a way. You can define a wrapper generic class which conforms to GenericRepository. It does not really implements its methods but rather passes to an object (which is real GenericRepository) it wraps.
class AnyGenericRepository<ResourceType: Identifiable>: GenericRepository {
// any usage of GenericRepository must be a generic argument
init<Base: GenericRepository>(_ base: Base) where Base.ResourceType == ResourceType {
// we cannot store Base as a class property without putting it in generics list
// but we can store closures instead
indexGetter = { base.index() }
// and same for other methods or properties
// if GenericRepository contained a generic method it would be impossible to make
}
private let indexGetter: () -> [ResourceType] {
indexGetter()
}
// ... other GenericRepository methods
}
So now we have a concrete type which wraps real GenericRepository. You can adopt it in SomeResourceService without any alarm.
class SomeResourceService {
let local: AnyGenericRepository<SomeResource>
}

Associated protocol in Swift

I'm trying to abstract views that are configured from a view model. I've been using associated types so far:
public protocol ViewModelProtocol: Equatable {}
public protocol ModeledView: class {
/// The type of the view model
associatedtype ViewModel: ViewModelProtocol
var viewModel: ViewModel? { get }
/// Sets the view model. A nil value describes a default state.
func set(newViewModel: ViewModel?)
}
Which I can use like:
struct MyViewModel: ViewModelProtocol {
let foo: String
static public func == (lhs: MyViewModel, rhs: MyViewModel) -> Bool {
return lhs.foo == rhs.foo
}
}
class MyView: UIView, ModeledView {
typealias ViewModel = MyViewModel
private(set) var viewModel: MyViewModel?
public func set(newViewModel: MyViewModel?) {
print(newViewModel?.foo)
}
}
However I'd like to specify a protocol for my view models, and not a concretized type. The reason is that one struct / class could comply to several such view model protocols. I don't want to either convert this object to another type just to pass it to the view, or have the view have an associated type with more requirements than it needs. So I think I'd like to do something like:
protocol MyViewModelProtocol: ViewModelProtocol {
var foo: String { get }
}
class MyView: UIView, ModeledView {
typealias ViewModel = MyViewModelProtocol
private(set) var viewModel: MyViewModelProtocol?
public func set(newViewModel: MyViewModelProtocol?) {
print(newViewModel?.foo)
}
}
struct DataModel: MyViewModelProtocol {
let foo: String
let bar: String
static public func == (lhs: MyViewModel, rhs: MyViewModel) -> Bool {
return lhs.foo == rhs.foo && lhs.bar == rhs.bar
}
}
let dataModel = DataModel(foo: "foo", bar: "bar")
let view = MyView()
view.set(newViewModel: dataModel)
This doesn't work. The compiler says that MyView doesn't conform to the ModeledView protocol, and hints that
Possibly intended match 'MyView.ViewModel' (aka 'MyViewModelProtocol') does not conform to 'ViewModelProtocol'
I don't really get what's bothering the compiler as MyViewModelProtocol is defined as extending ViewModelProtocol
MyViewModelProtocol is defined as extending ViewModelProtocol
Correct. MyViewModelProtocol extends ViewModelProtocol. It doesn't conform to ViewModelProtocol. This is a classic case of "protocols do not conform to themselves." Your associated type requires that ViewModel be a concrete type that conforms to ViewModelProtocol, and MyViewModelProtocol is not a concrete type and it does not conform to anything (protocols do not conform to protocols).
The way to attack this problem is to start with the calling code, and then construct protocols that support what you want the calling code to look like. In the code you've given, the correct solution is to get rid of ModeledView entirely. It isn't doing anything; nothing relies on it, and it doesn't provide any extensions. I assume in your "real" code, something does rely on it. That's the key piece of code to focus on. The best way to achieve that is to write a few concrete examples of the kinds of conforming types you'd like to exist. (If you have trouble writing several concrete implementations, then reconsider whether there's anything to abstract.)
Just to elaborate a bit more why this won't work in your specific case (it won't work in more general cases either, but your case layers additional restrictions that are important). You've required that types that conform to ViewModelProtocol also conform to Equatable. That means they must implement:
static public func == (lhs: Self, rhs: Self) -> Bool {
This does not mean that some type conforming to ViewModelProtocol is Equatable to some other type conforming to ViewModelProtocol. It means Self, the specific, concrete type implementing the protocol. ViewModelProtocol is not itself Equatable. If it were, which code above would you expect dataModel == myViewModel to call? Would it check bar or not? Remember, == is just a function. Swift doesn't know that you're asking "are these equal." It can't inject things like "if they're different concrete types they're not equal." That's something the function itself has to do. And so Swift needs to know which function to call.
Making this possible in Swift will be a major addition to the language. It's been discussed quite a lot (see the link I posted in the earlier comments), but it's at least several releases away.
If you remove the Equatable requirement, it still won't work today, but the language change required is much smaller (and may come sooner).

How to do?I want a protocol that can only be followed by two classes

I want a protocol that can only be followed by two classes。(ClassA or ClassB).
protocol MyProtocol where Self: ClassA || ClassB {
}
This requirement almost certainly indicates a design problem. If any piece of your system cares what is implementing the protocol, then the protocol does not capture the whole interface. For example, if at any point you use as? ClassA, then this is a dangerous use of protocols.
It is impossible in Swift to have a protocol that I can see but that I cannot implement. There are languages like Go where it's possible (though still not in exactly the way you describe), but it's impossible in Swift today. As #MartinR notes, the answer by J. Doe doesn't actually change anything. Outside parties can still just implement both protocols.
With that said, it's possible to achieve things very similar to what you're describing.
First, and most obviously: use access controls.
private protocol MyProtocol {}
public class ClassA: MyProtocol {}
public class ClassB: MyProtocol {}
This works if all the classes and protocol are in one file. If not, put them all in a framework and use internal rather than private. Now nothing outside this file/module can implement MyProtocol. The limitation is that MyProtocol is also can't be seen outside the file/module.
This can be resolved by lifting a struct:
public struct MyStruct: MyProtocol {
private let value: MyProtocol
public init(classA: ClassA) { value = classA }
public init(classB: ClassB) { value = classB }
// Methods to conform to MyProtocol by forwarding to value
}
With this, it's impossible to generate a MyStruct that is initialized with anything but a ClassA or ClassB.
Alternately, if you really literally mean "ClassA or ClassB" that's not a struct or a protocol. That's a enum:
enum MyEnum {
case classA(ClassA)
case classB(ClassB)
}

Generics Protocol as Type in Swift

How I can create a protocol variable. My goal is a protocol will be have a function with a generic type and I'm using associatedtype which is accessing to Class and the function will be return generic type. Example declaration below:
public protocol ComponentFactory {
associatedtype T // A class and that class can be inherit from another so I need define generic type here
func create() -> T
}
I want to declare a variable for this protocol like that:
fileprivate var mComponentFactoryMap = Dictionary<String, ComponentFactory>()
At this line I receive a error:
Protocol 'ComponentFactory' can only be used as a generic constraint because it has Self or associated type requirements
I see from Android, actually from kotlin they have a declare for interface like:
private val mComponentFactoryMap = mutableMapOf<String, ComponentFactory<*>>()
Any guys can help me, how I can declare this from Swift?
I've solved this from few months ago with descriptions below. Please check it and give me another solution if you have.
Firstly, make some change for Protocol. At associatedtype T should be change to associatedtype Component and Component is a class which will be inherited from another class (important step).
public protocol ProComponentFactory {
associatedtype Component
func create() -> Component?
}
Second, I will make a Generic Struct with inheritance from ProComponentFactory:
public struct ComponentFactory<T>: ProComponentFactory {
public typealias Component = T
public func create() -> T? { return T.self as? T }
}
Well done, for now you can define a variable as I example in my question above:
fileprivate var mComponentFactoryMap = Dictionary<String, ComponentFactory<Component>>()
As well for any class was inherit from Component and variable mComponentFactoryMap can using extension inside.