Protocol Conformance Check - swift

How can I perform conformance check against protocol with AssociatedType. Xcode shows error:
Protocol 'MyListener' can only be used as a generic constraint because
it has Self or associated type requirements
My ultimate goal is to extract "MyListener.section" from an array of weakObjects, where the handler matches the function argument.
Note. The NSPointerArray of weakObjects is suppose to capture different types of MyListeners.
public class MyHandler<O,E> {
var source = [O]()
var dest = [E]()
}
public protocol MyListener:class {
var section: Int {get}
associatedtype O
associatedtype E
var handler: MyHandler<O,E>? { get }
}
public class MyAnnouncer {
private let mapWeakObjects: NSPointerArray = NSPointerArray.weakObjects()
public func add<L: MyListener>(listener: L) {
let pointer = Unmanaged.passUnretained(listener).toOpaque()
mapWeakObjects.addPointer(pointer)
}
public func search<O, E> (h:MyHandler<O,E>) -> [Int] {
_ = mapWeakObjects.allObjects.filter { listener in
if listener is MyListener { // Compilation failed
}
if let _ = listener as? MyListener { //Compilation error
}
if listener is MyListener.Type { //Compilation failed
}
}
return [] // ultimate goal is to extract corresponding [MyListener.section].
}
}

Unfortunately, Swift doesn't support protocols with AssociatedType to conformance.
You should try to use Type Erasure. One of the way is to implement type erasure by creating new AnyType class.
Here is another way to release type erasure (example from the internet)
protocol SpecialValue { /* some code*/ }
protocol TypeErasedSpecialController {
var typeErasedCurrentValue: SpecialValue? { get }
}
protocol SpecialController : TypeErasedSpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
extension SpecialController {
var typeErasedCurrentValue: SpecialValue? { return currentValue }
}
extension String : SpecialValue {}
struct S : SpecialController {
var currentValue: String?
}
var x: Any = S(currentValue: "Hello World!")
if let sc = x as? TypeErasedSpecialController { // Now we can perform conformance
print(sc.typeErasedCurrentValue)
}

Related

How to create an instance that satisfies the generic requirements

I have created the following code and everything works fine except the definition of TestDBAPI.
When I want to create a type that conforms to the DBAPIProtocol protocol, it is always impossible to generate a type instance that satisfies the generic constraints
Please, how can I define TestNoteFetcher to satisfy the protocol requirement of DBAPIProtocol.
ps: I hope the flexibility of generic definitions can be maintained in DBAPIProtocol
thanks
import Combine
// For Value
public enum WrappedID: Equatable, Identifiable, Sendable, Hashable {
case string(String)
case integer(Int)
public var id: Self {
self
}
}
public protocol BaseValueProtocol: Equatable, Identifiable, Sendable {
var id: WrappedID { get }
}
public struct Note: BaseValueProtocol {
public var id: WrappedID
public var index: Int
public init(id: WrappedID, index: Int) {
self.id = id
self.index = index
}
}
// For Object
public protocol ConvertibleValueObservableObject<Value>: ObservableObject, Equatable, Identifiable where ID == WrappedID {
associatedtype Value: BaseValueProtocol
func convertToValueType() -> Value
}
public final class TestNote: ConvertibleValueObservableObject {
public static func == (lhs: TestNote, rhs: TestNote) -> Bool {
true
}
public var id: WrappedID {
.integer(1)
}
public func convertToValueType() -> Note {
.init(id: .integer(1), index: 0)
}
}
// For Fetcher
public protocol ObjectFetcherProtocol<Object,ConvertValue> {
associatedtype ConvertValue: BaseValueProtocol
associatedtype Object: ConvertibleValueObservableObject<ConvertValue>
var stream: AsyncPublisher<AnyPublisher<[Object], Never>> { get }
}
public final class TestNoteFetcher: ObjectFetcherProtocol {
public typealias ConvertValue = Note
public typealias Object = TestNote
public var stream: AsyncPublisher<AnyPublisher<[TestNote], Never>> {
sender.eraseToAnyPublisher().values
}
public var sender: CurrentValueSubject<[TestNote], Never>
public init(_ notes: [TestNote] = []) {
sender = .init(notes)
}
}
// For API
public protocol DBAPIProtocol {
var notesFetcher: () async -> any ObjectFetcherProtocol<any ConvertibleValueObservableObject<Note>, Note> { get set }
}
// get error in here . Cannot convert value of type 'TestNoteFetcher.Object' (aka 'TestNote') to closure result type 'any ConvertibleValueObservableObject<Note>'
public final class TestDBAPI: DBAPIProtocol {
public var notesFetcher: () async -> any ObjectFetcherProtocol<any ConvertibleValueObservableObject<Note>, Note> = {
TestNoteFetcher([])
}
}
Since your closure returns too many anys, the compiler got confused & is telling you that TestNoteFetcher does not conform any ObjectFetcherProtocol. Generics are your friend, you can use associatedtype to skip all this code & fix the issue:
public protocol DBAPIProtocol {
associatedtype Fetcher: ObjectFetcherProtocol
var notesFetcher: () async -> Fetcher { get set }
}
public final class TestDBAPI: DBAPIProtocol {
public var notesFetcher: () async -> TestNoteFetcher = {
TestNoteFetcher([])
}
}
Sometimes the answer is right in front of me, but I do turn a blind eye to it. follow the Xcode warning, I made a change to this part of the code and it works now.
public protocol ObjectFetcherProtocol<ConvertValue> {
associatedtype ConvertValue: BaseValueProtocol
var stream: AsyncPublisher<AnyPublisher<[any ConvertibleValueObservableObject<ConvertValue>], Never>> { get }
}
public final class TestNoteFetcher: ObjectFetcherProtocol {
public var stream: AsyncPublisher<AnyPublisher<[any ConvertibleValueObservableObject<Note>], Never>> {
sender.eraseToAnyPublisher().values
}
public var sender: CurrentValueSubject<[any ConvertibleValueObservableObject<Note>], Never>
public init(_ notes: [any ConvertibleValueObservableObject<Note>] = []) {
sender = .init(notes)
}
}
This way, I can still maintain the flexibility defined in DBAPIProtocol without having to introduce association types

How to correct implement provider logic using generics in Swift

I try to implement logic where some different objects can receive their unique configs.
I have a lot of objects with different types, which can request their own configuration object.
//Provider
protocol ConfigProvider {
func config<R: ConfigReciever>(for reciever: R) -> R.ConfigType
}
class Factory {
}
extension Factory: ConfigProvider {
func config<R: ConfigReciever>(for reciever: R) -> R.ConfigType {
//How switch?
return Config1(info: "hey") as! R.ConfigType
}
}
//Reciever
protocol ConfigReciever: class {
associatedtype ConfigType
var dataSource: ConfigProvider? { get set }
}
struct Config1 {
let info: String
}
class Object1: ConfigReciever {
typealias ConfigType = Config1
var dataSource: ConfigProvider?
func execute() {
let config = dataSource?.config(for: self)
print("\(config!.info)")
}
}
But have some problems with correct implement Provider logic.
I don't know how switch reciever to create correct Config type.
Is this any options?
I know, that i can make this without generics (for example with enum of configTypes), but i don't want to make unnecessary casts.
I would suggest using an intermediate protocol
// Wrapper container
protocol Container { }
extension String: Container { }
extension Int: Container { }
// Implementation
//Provider
protocol ConfigProvider {
func config<R>(for reciever: R) -> Configuration where R : ConfigReciever, R.ConfigType: Configuration
}
class Factory { }
extension Factory: ConfigProvider {
func config<R>(for reciever: R) -> Configuration where R : ConfigReciever, R.ConfigType: Configuration {
return Config1(info: "hey")
}
}
//Reciever
protocol ConfigReciever: class {
associatedtype ConfigType
var dataSource: ConfigProvider? { get set }
}
protocol Configuration {
var info: Container { get set }
}
struct Config1: Configuration {
var info: String
}
class Object1: ConfigReciever {
typealias ConfigType = Config1
var dataSource: ConfigProvider?
func execute() {
let config = dataSource?.config(for: self)
// here you should case into wanted structure:
if let stringInfo = config?.info as? String {
print("\(stringInfo)")
}
}
}
let factory = Factory()
let obj = Object1()
obj.dataSource = factory
obj.execute()

How do you implement a generic class factory in Swift?

I want to "make" a number of class instances similar to a base class but different in underlying type. (Not quite the same as the typical "Animal" class factory examples seen all over the net!)
The code below is close to working but it requires the user to "upcast" the make result, as in:
var f1 = FOO.make(version: FOO.Ver.f1) as! FOO1_32
I do not want the user to know about the specific class type other than it is a FOO. I have seen other proposals and they all indicate that the solution is to define the make with a generic type that conforms to the protocol as in:
make<T: FOOProtocol>(version: Ver = .f1) -> T
However this gives me the error "generic parameter 'T' could not be inferred" on the call to FOO.make(version: FOO.Ver.f1)
Anyone know how to do this? My Playground code follows.
protocol FOOProtocol
{
associatedtype FOOtype
var value: FOOtype {get set}
}
class FOO
{
enum Ver
{
case f1
case f2
}
class func make(version: Ver = .f1) -> FOO
{
print("FOO make")
switch version
{
case .f1:
return FOO1_32()
case .f2:
return FOO2_64()
}
}
}
class FOO1_32: FOO, FOOProtocol
{
typealias FOOtype = UInt32
private var fooVal: UInt32 = 0
var value: UInt32
{
get { return self.fooVal }
set { self.fooVal = newValue }
}
override init()
{
print("FOO1_32 init")
self.fooVal = 132
}
}
class FOO2_64: FOO, FOOProtocol
{
typealias FOOtype = UInt64
private var fooVal: UInt64 = 0
var value: UInt64
{
get { return self.fooVal }
set { self.fooVal = newValue }
}
override init()
{
print("FOO2_64 init")
self.fooVal = 264
}
}
var f1 = FOO.make(version: FOO.Ver.f1) // requires: as! FOO1_32
let f1v = f1.value
print("\(f1v)")
var f2 = FOO.make(version: FOO.Ver.f2) // requires: as! FOO2_64
let f2v = f2.value
print("\(f2v)")

Swift: Abstract base class/protocol with private members

I've created an abstract base class-like structure in Swift, using protocol extensions, as per this answer. This is a simplified example:
protocol AbstractBase {
var _constant: Int { get }
func _operation(_ val: Int) -> Int
}
public class ConcreteSub: AbstractBase {
let _constant: Int = 42
func _operation(_ val: Int) -> Int {
return val + 2
}
}
extension AbstractBase {
func mainOperation(_ val: Int) -> Int {
return _operation(val + _constant)
}
}
So basically, ConcreteSub provides the implementation details needed by AbstractBase, namely _constant and _operation.
I would like to hide those details from clients, and only expose mainOperation. However, Swift does not allow me to make the members fileprivate on the protocol -- if I do the following
protocol AbstractBase {
fileprivate var _constant: Int { get }
// etc
I get "error: 'fileprivate' modifier cannot be used in protocols".
Nor can I apply the modifier on the subclass -- when I try
public class ConcreteSub: AbstractBase {
fileprivate let _constant: Int = 42
// etc
I get "error: property '_constant' must be declared internal because it matches a requirement in internal protocol 'AbstractBase'".
Lastly, when I make the whole protocol fileprivate, I get no compile errors, but I consistently run into Linking errors, which I guess is because the protocol is private, but the subclass is public.
Is there another way I'm missing?
When I need an abstract base with some properties/functions hidden I use class with some additional fatalErrors and asserts to crash whenever someone is trying to use Base instead of implementation.
public class AbstractBase {
init() {
assert(type(of: self) != AbstractBase.self, "Abstract class")
}
fileprivate var _constant: Int {
fatalError("Abstract class")
}
fileprivate func _operation(_ val: Int) -> Int {
fatalError("Abstract class")
}
func mainOperation(_ val: Int) -> Int {
return _operation(val + _constant)
}
}
public class ConcreteSub: AbstractBase {
fileprivate override var _constant: Int {
return 42
}
fileprivate override func _operation(_ val: Int) -> Int {
return val + 2
}
}
I actually just ran into this issue. As of Swift 5.1, you can do this instead:
protocol MyProtocol {
var someVisibleVar: String { get }
func someVisibleFunc()
}
fileprivate extension MyProtocol {
var someFilePrivateVar: String {
"whatever"
}
func someFilePrivateFunc() {
print("someFilePrivateFunc() was called with \(someVisibleVar)")
}
}
class SomeClass: MyProtocol {
var someVisibleVar: String { "whatever" }
func someVisibleFunc() {
if someFilePrivateVar == someVisibleVar {
someFilePrivateFunc()
}
}
}
class SomeOtherClass: MyProtocol {
var someVisibleVar: String { "something else" }
func someVisibleFunc() {
if someFilePrivateVar == someVisibleVar {
someFilePrivateFunc()
}
}
}

Implementing Swift protocol with a constrained type parameter

I have a couple of Swift protocols that describe a general interface that I'm trying to implement in multiple ways:
protocol Identifiable
{
var identifier:String { get }
}
protocol ItemWithReference
{
var resolveReference<T:Identifiable>(callback:(T) -> ())
}
Now I want to implement the ItemWithReference protocol using CloudKit as the back end (this will eventually work with an alternate back-end as well, at which time I expect to provide an alternative implementation of the ItemWithReference protocol.
In my CloudKit implementation, I have something like this:
class CloudKitIdentifiable : Identifiable
{
...
}
class CloudKitItemWithReference : ItemWithReference
{
func resolveReference<T:Identifiable>(callback:(T) -> ())
{
// In this implementation, I want to only proceed if `T` is a CloudKitIdentifiable subtype
// But not sure how to enforce that
}
}
What I would like to do is to constrain T to be a CloudKitIdentifiable rather than just a simple Identifiable. I can't do that directly in the resolveReference declaration because then the function wouldn't conform to the ItemWithReference protocol. So instead, I am hoping to confirm that T is indeed a CloudKitIdentifiable and then invoke it's initializer to create a new instance of the class being resolved.
Is there any way in Swift to use T's metatype T.Type and determine if it is a subtype of another type? Furthermore, is there any way to invoke a required initializer that has been declared on that subtype?
try:
class CloudKitIdentifiable : Identifiable {
var identifier:String = ...
required init() {}
// you need `required`.
}
class CloudKitItemWithReference : ItemWithReference {
func resolveReference<T:Identifiable>(callback:(T) -> ()) {
if T.self is CloudKitIdentifiable.Type {
// do work..
let obj = (T.self as CloudKitIdentifiable.Type)()
callback(obj as T)
}
}
}
OR:
class CloudKitItemWithReference : ItemWithReference {
func resolveReference<T:Identifiable>(callback:(T) -> ()) {
if let CKT = T.self as? CloudKitIdentifiable.Type {
// do work..
let obj = CKT()
callback(obj as T)
}
}
}
But, In this case, you have to call resolveReference like this:
let ref = CloudKitItemWithReference()
ref.resolveReference { (obj: CloudKitIdentifiable) -> () in
// ^^^^^^^^^^^^^^^^^^^^ explicit type is necessary.
println(obj.identifier)
return
}
Rathar than that, I would recommend to use Associated Type:
protocol Identifiable {
var identifier:String { get }
}
protocol ItemWithReference {
typealias Item: Identifiable // <-- HERE is associated type
func resolveReference(callback:(Item) -> ())
}
class CloudKitIdentifiable : Identifiable {
var identifier:String
init(identifier: String) {
self.identifier = identifier
}
}
class CloudKitItemWithReference : ItemWithReference {
// `Item` associated type can be inferred from
// the parameter type of `resolveReference()`
//
// typealias Item = CloudKitIdentifiable
func resolveReference(callback:(CloudKitIdentifiable) -> ()) {
let obj = CloudKitIdentifiable(identifier: "test")
callback(obj)
}
}
let ref = CloudKitItemWithReference()
ref.resolveReference { obj in
println(obj.identifier)
return
}