Statically typed properties in Swift protocols - swift

I'm trying to use Protocol-Oriented Pgrogramming for model layer in my application.
I've started with defining two protocols:
protocol ParseConvertible {
func toParseObject() -> PFObject?
}
protocol HealthKitInitializable {
init?(sample: HKSample)
}
And after implementing first model which conforms to both I've noticed that another model will be basically similar so I wanted to create protocol inheritance with new one:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
var value: AnyObject { get set }
}
A you can see this protocol has one additional thing which is value but I want this value to be type independent... Right now I have models which use Double but who knows what may show up in future. If I leave this with AnyObject I'm sentenced to casting everything I want to use it and if I declare it as Double there's no sense in calling this BasicModel but rather BasicDoubleModel or similar.
Do you have some hints how to achieve this? Or maybe I'm trying to solve this the wrong way?

You probably want to define a protocol with an "associated type",
this is roughly similar to generic types.
From "Associated Types" in the Swift book:
When defining a protocol, it is sometimes useful to declare one or
more associated types as part of the protocol’s definition. An
associated type gives a placeholder name (or alias) to a type that is
used as part of the protocol. The actual type to use for that
associated type is not specified until the protocol is adopted.
Associated types are specified with the typealias keyword.
In your case:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
typealias ValueType
var value: ValueType { get set }
}
Then classes with different types for the value property can
conform to the protocol:
class A : BasicModel {
var value : Int
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
class B : BasicModel {
var value : Double
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
For Swift 2.2/Xcode 7.3 and later, replace typealias in the
protocol definition by associatedtype.

Related

How to define a protocol that specializes a generic protocol, so that it can be used in type declarations?

I'm an Android developer learning iOS development and I'm facing this issue that is trivial with Kotlin/Java interfaces, but I can't get through it with Swift protocols.
Say we have this protocol:
protocol ValueStore {
associatedtype Value
var value: Value? { get set }
}
In Kotlin/Java, if I want to use a generic abstraction to define a variable type, I just use a generic interface with type parameter:
val stringStore: ValueStore<String>
Since this is not possible in Swift, I tried to create a specialized sub-protocol that defines the associated type:
protocol StringStore: ValueStore where Value == String { }
with the intent to use the latter like this:
let stringStore: StringStore
The above declaration is what I'm trying to achieve. Yet the compiler tells me Protocol 'StringStore' can only be used as a generic constraint because it has Self or associated type requirements.
Although in type declarations I can use a specialized generic concrete implementation, i.e. UserDefaultsValueStore<String>, this is against the dependency inversion principle.
Is it possible to specialize a protocol with associated type and still maintaining the level of abstraction?
If I understand the question correctly... Try this:
protocol ValueStoreType {
associatedtype Value
var value: Value? { get set }
}
struct ValueStore<T>: ValueStoreType {
var value: T?
}
Then you will be able to do:
var stringStore: ValueStore<String>

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>
}

How to solve the specification problem for Swift Protocol

protocol Specification {
associatedtype T
func isSatisfied(item : T) -> Bool
}
protocol AndSpecification {
associatedtype T
var arrayOfSpecs : [Specification] {set}
}
The aforementioned code gives the following error
Protocol 'Specification' can only be used as a generic constraint
because it has Self or associated type requirements
I understand that there is this problem because of associated types, which can be solved if I add a where clause. But I am at loss to understand where and how should I use this where clause
You think you need a where clause because you want the array to contain Specifications all with the same T, right? Well, protocols with associated types can't do that either!
What you can do is to have the array contain specifications all of the same type:
protocol AndSpecification {
associatedtype SpecificationType : Specification
var arrayOfSpecs : [SpecificationType] { get }
}
If you really like your T associated type, you can add one nonetheless:
protocol AndSpecification {
associatedtype T
associatedtype SpecificationType : Specification where SpecificationType.T == T
var arrayOfSpecs : [SpecificationType] { get }
}
But this is quite redundant, because you could just say AndSpecification.SpecificationType.T instead.

How to define a Swift protocol that enforces its adopters themselves to conform to an associated type?

I need to work with objects that not only conform to a protocol, but also expose the type of a second protocol which they conform to. (This is for use with NSXPCConnection, where you must configurable not only an object to proxy, but also tell it which protocol should be exposed on that proxied object.)
I tried something like:
protocol Conformer where Self : Conformer.P {
associatedtype P : Protocol
static var interface : P {get}
init(info: String)
}
func exposeOverXPC<T:Conformer>(_ _: T.Type) {
let c : NSXPCConnection = …
c.exportedInterface = NSXPCInterface(with: T.interface)
c.exportedObject = T(info:"foo")
}
But it results in an error:
Associated type 'P' can only be used with a concrete type or generic parameter base
Specifically I want exposeOverXPC to only accept objects that:
Are initializeable in a particular way
Have a static property interface which references a protocol
Are themselves conformant to said interface
It's the last step that I'm getting stuck on, is there any way I can accomplish it?
You cannot restrict who conforms to a protocol, that goes against the concept of having protocols in the first place if you think about it. However you can use composed types, Swift4 feature, in your generic parameter in exposeOverXPC.
protocol Interface {
}
protocol XPCExposable {
associatedtype P: Interface
init(info: String)
static var interface: P { get }
}
func exposeOverXPC<T: XPCExposable & Interface>(_ : T.Type) {
// 1: T is initializeable in a particular way
// 2: Has a static property interface which references a protocol
// 3: Are themselves conformant to said interface
}
Yes this constraints T to conform to Interface and not P, your best bet is to make exposeOverXPC private/internal and provide APIs that expect Interface subtype. wherever you have access to the Interface subtype expose that api. e.g:
Solution 1
protocol InterfaceSubType: Interface {
fun test()
}
/// Create as many `API`s as the number of `Interface` subtypes you have.
func exposeOverXPC<T: XPCExposable & InterfaceSubType>(_ : T.Type) {
exposeOverXPC(T.self)
}
/// set to private, you only want to expose the APIs with `Interface` subtype.
private func exposeOverXPC<T: XPCExposable & Interface>(_ : T.Type) {
// Impl.
}
Solution 2
An alternative solution to have a function with parameters whose type is the associated type is to add that api (as static function if you wish) by extending the protocol. You must know all the expected subtypes of Interface in this extension.
extension XPCExposable {
static func exposeOverXPC<T>(_ interface: P, _ xpcType: T.Type) where T: XPCExposable {
// Expected subtype Interface
if let subInterface = interface as? InterfaceSubType {
subInterface.test()
}
// Other subtypes here.
}
}
Can be called as:
let impl = Impl(info: "")
Impl.exposeOverXPC(Impl.interface, Impl.self)
Its an extension on XPCExposable so you constrain the caller to be a conformer and the parameter requires XPCExposable.P so you're all set.
Downsides of this solution are:
You have two parameters instead of one.
It uses if conditions, I don't know if thats worth mentioning as downside other than that I'd like to push the first solution as favourite.

Define a protocol that holds Collection as a property

I want to define a protocol that defines a variable "sequence" that is of type "Collection". I want that, because i have several conforming types, that will all hold an array of different types. Example:
protocol BioSequence {
associatedtype T
var sequence: Collection { get }
// Will also implement conformance to Collection and redirect all Collection functions to the property "sequence". This is because my types that conform to BioSequence are just wrappers for an array of Nucleotide or AminoAcid with some extra functionality
}
struct DNASequence: BioSequence {
// Will hold "sequence" of type [Nucleotide]
}
struct AminoSequence: BioSequence {
// Will hold "sequence" of type [AminoAcid]
}
Why do i want this? Because i need to implement the conformance to "Collection" only once in BioSequence and all conforming type inherit it automatically. Plus i can freely add extra functionality on the conforming types.
Now, when i try this like the code above, the compiler says: "Protocol Collection can only be used as a generic constraint". Yes, i googled what this error means, but how can i actually fix it to make my code work, like i want. Or is it not even possible to do what i want?
Thank you.
You can easily achieve this by using an associatedtype in your protocol, which can be constrained to Collection, allowing conforming types to satisfy the requirement with a concrete type when they adopt the protocol.
For example:
protocol CollectionWrapper : Collection {
associatedtype Base : Collection
var base: Base { get }
}
extension CollectionWrapper {
var startIndex: Base.Index {
return base.startIndex
}
var endIndex: Base.Index {
return base.endIndex
}
func index(after i: Base.Index) -> Base.Index {
return base.index(after: i)
}
subscript(index: Base.Index) -> Base.Iterator.Element {
return base[index]
}
// Note that Collection has default implementations for the rest of the
// requirements. You may want to explicitly implement them and forward them to
// the base collection though, as it's possible the base collection implements
// them in a more efficient manner (e.g being non random access and having
// a stored count).
}
// S adopts CollectionWrapper and satisfies the 'Base' associatedtype with [String].
struct S: CollectionWrapper {
var base: [String]
}
let s = S(base: ["foo", "bar", "baz"])
print(s[1]) // "bar"
Regarding your comment:
If I want to use this like that: let a: CollectionWrapper = S() [...] this leaves me with "Protocol can only be used as a generic constraint" again.
The problem is that you cannot currently talk in terms of a protocol with associated type requirements, as the types which are used to satisfy those requirements are unknown to the compiler (this isn't a technical limitation though). You can solve this by using Swift's AnyCollection type eraser to wrap an arbitrary Collection with a given element type.
For example:
let a = AnyCollection(S(base: ["foo", "bar", "baz"]))
If you need to work with additional protocol requirements from CollectionWrapper, you'll have to implement your own type eraser to do so. See Rob's answer here for an idea of how to go about this.