Reputation: 326
I added a protocol extension for a protocol with an associated type called PickerType
. I wrote a reimplementation of a function, refresh(:,completion:)
that’s defined in the protocol and implemented in a different protocol extension.
But the function inside my new extension isn’t called when I call refresh(:,completion:)
unless the compiler knows what type PickerType
is. I wrote the following:
extension PickerItemProvider where PickerType: Equatable & SyncableEntity {
func refresh(_ sender: Any, completion: (() -> Void)?) {
print("we're trying to have this implementation called")
PickerType.startSync()
}
}
It gets called as I would expect if I call refresh(:,completion:)
on a PickerSectionProvider<ObservationType>
(see the code from my Playground below) but not when I call refresh(:,completion:)
on an ItemProvider
, which is a generic type that must conform to PickerItemProvider
(again see my code below).
// We are trying to get our pickers to sync their provided type when you pull to refresh. But the implementation of refresh(:, completion:) that we added doesn’t get called.
import Foundation
protocol PickerItemProvider: class {
associatedtype PickerType
func findItem(by identifier: NSNumber) -> PickerType?
func itemAt(_ indexPath: IndexPath) -> PickerType?
func refresh(_ sender: Any, completion: (() -> Void)?)
}
extension PickerItemProvider {
func findItem(by identifier: NSNumber) -> PickerType? {
return nil
}
public func refresh(_ sender: Any, completion: (() -> Void)?) {
print("the default refresh implementation")
}
}
public class PickerSectionProvider<ProvidedType: Equatable> : PickerItemProvider {
func itemAt(_ indexPath: IndexPath) -> ProvidedType? {
return nil
}
}
extension PickerItemProvider where PickerType: Equatable & SyncableEntity {
func refresh(_ sender: Any, completion: (() -> Void)?) {
print("we’re trying to have this implementation called instead of the above implementation of refresh")
PickerType.startSync()
}
}
protocol SyncableEntity {
static func startSync()
}
extension SyncableEntity {
static func startSync() {
}
}
class ObservationType: Equatable, SyncableEntity {
}
func ==(lhs: ObservationType, rhs: ObservationType) -> Bool {
return false
}
class GenericPickerViewController<PickerType: Equatable, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == PickerType {
var itemProvider: ItemProvider?
init() {
}
func foo() {
// Why doesn’t the implementation of refresh(:,completion:) we added get called here?
itemProvider?.refresh("dummy sender") {
}
}
}
class PopupPickerRow<T: Equatable, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == T {
var pickerController = GenericPickerViewController<T, ItemProvider>()
}
let pickerSectionProvider = PickerSectionProvider<ObservationType>()
let row = PopupPickerRow<ObservationType, PickerSectionProvider<ObservationType>>()
row.pickerController.itemProvider = pickerSectionProvider
row.pickerController.foo()
Upvotes: 3
Views: 125
Reputation: 326
I added two functions on GenericPickerViewController
, one for setting it up with a PickerItemProvider
whose PickerType
simply conforms to Equatable
, and another to set it up with a PickerItemProvider
whose PickerType
conforms to SyncableEntity
. This way the compiler knows which refresh(:,completion:)
to call. Here is the Playground code:
import Foundation
import CoreData
protocol PickerItemProvider: class {
associatedtype PickerType
func itemAt(_ indexPath: IndexPath) -> PickerType?
}
extension PickerItemProvider {
public func refresh(_ sender: Any, completion: (() -> Void)?) {
print("the default refresh implementation")
}
}
public class PickerSectionProvider<ProvidedType: Equatable> : PickerItemProvider {
func itemAt(_ indexPath: IndexPath) -> ProvidedType? {
return nil
}
}
extension PickerItemProvider where PickerType: Equatable & SyncableEntity {
func refresh(_ sender: Any, completion: (() -> Void)?) {
print("we’re trying to have this implementation called instead of the above implementation of refresh")
PickerType.startSync()
completion?()
}
}
protocol SyncableEntity {
associatedtype EntityType
static func startSync()
}
extension SyncableEntity {
static func startSync() {
print("starting sync")
}
}
class ObservationType: Equatable, SyncableEntity {
typealias EntityType = NSManagedObject
}
func ==(lhs: ObservationType, rhs: ObservationType) -> Bool {
return false
}
class GeneralPickerViewController<PickerType: Equatable, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == PickerType {
private var itemProvider: ItemProvider?
private var refresher: ((Any, (() -> ())?) -> ())?
func setup<T: PickerItemProvider>(with itemProvider: T) where T.PickerType == PickerType {
refresher = { sender, completion in
itemProvider.refresh(self, completion: {
completion?()
})
}
self.itemProvider = (itemProvider as! ItemProvider)
}
func setup<T: PickerItemProvider>(with itemProvider: T) where T.PickerType == PickerType, PickerType: SyncableEntity {
refresher = { sender, completion in
itemProvider.refresh(self, completion: {
completion?()
})
}
self.itemProvider = (itemProvider as! ItemProvider)
}
func foo() {
refresher?(self, {
print("finished")
})
}
}
class PopupPickerRow<T: Equatable, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == T {
var pickerController = GeneralPickerViewController<T, ItemProvider>()
}
let pickerSectionProvider = PickerSectionProvider<ObservationType>()
let row = PopupPickerRow<ObservationType, PickerSectionProvider<ObservationType>>()
row.pickerController.setup(with: pickerSectionProvider)
row.pickerController.foo()
Upvotes: 2
Reputation: 15258
See the corrected implementation below,
import Foundation
protocol PickerItemProvider: class {
associatedtype PickerType
func findItem(by identifier: NSNumber) -> PickerType?
func itemAt(_ indexPath: IndexPath) -> PickerType?
}
extension PickerItemProvider {
func findItem(by identifier: NSNumber) -> PickerType? {
return nil
}
func refresh(_ sender: Any, completion: (() -> Void)?) {
print("the default refresh implementation")
}
}
public class PickerSectionProvider<ProvidedType: Equatable> : PickerItemProvider {
func itemAt(_ indexPath: IndexPath) -> ProvidedType? {
return nil
}
}
extension PickerItemProvider where PickerType: Equatable & SyncableEntity {
func refresh(_ sender: Any, completion: (() -> Void)?) {
print("we’re trying to have this implementation called instead of the above implementation of refresh")
PickerType.startSync()
}
}
protocol SyncableEntity {
static func startSync()
}
extension SyncableEntity {
static func startSync() {
}
}
class ObservationType: Equatable, SyncableEntity {
}
func ==(lhs: ObservationType, rhs: ObservationType) -> Bool {
return false
}
class GenericPickerViewController<PickerType: Equatable & SyncableEntity, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == PickerType {
var itemProvider: ItemProvider?
init() {
}
func foo() {
// Why doesn’t the implementation of refresh(:,completion:) we added get called here?
itemProvider?.refresh("dummy sender") {
}
}
}
class PopupPickerRow<T: Equatable & SyncableEntity, ItemProvider: PickerItemProvider> where ItemProvider.PickerType == T {
var pickerController = GenericPickerViewController<T, ItemProvider>()
}
let pickerSectionProvider = PickerSectionProvider<ObservationType>()
let row = PopupPickerRow<ObservationType, PickerSectionProvider<ObservationType>>()
row.pickerController.itemProvider = pickerSectionProvider
row.pickerController.foo()
First of all, when you want to override
a method
implementation declared as a requirement in the protocol
and provided some default implementation in the protocol
extension
it will always call the method implemented in the extension
discarding the where
clause requirements. You can see similar issues in this question and this.
So to make the protocol
look for the method
fulfilling the constraints in where
clause you need to remove the method
signature from the protocol
and keep it only inside the extension
as i did above.
Secondly you were missed the PickerType
requirement to be Equatable & SyncableEntity
while defining GenericPickerViewController
and PopupPickerRow
so i updated that also.
Upvotes: 2