Last active
May 17, 2019 09:38
-
-
Save marcpalmer/88a6caaddcdcb07a014c701de3c54aef to your computer and use it in GitHub Desktop.
Example of trying to avoid sub-protocol typealias defaulting compiler warning using associatedtype to re-state the type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// **************************************************************************************** | |
// | |
// Example of trying to supply a default type for an associated type in a sub-protocol | |
// and helper functions that use this default type in an extention on that sub-protocol. | |
// The natural way to achieve this by defining the typealias in the sub-protocol produces | |
// a compiler warning and fix-it suggesting use of protocol constraint. | |
// | |
// This approach: Re-stating the associated type as a more specific type | |
// | |
// Outcome: Fail (?). The call to `Action.perform` appears to not see the refined PresenterType, | |
// so the compile fails | |
// | |
// **************************************************************************************** | |
// The base type with an associated type we want to constrain/default to later | |
protocol Action { | |
associatedtype PresenterType | |
static func perform(presenter: PresenterType) | |
} | |
// Types specific to Intent-implementing Action(s) | |
class IntentResponse { | |
} | |
// A presenter that IntentAction's use to relay their strongly typed response to the callback | |
class IntentPresenter<ResponseType> where ResponseType: IntentResponse { | |
let completion: (ResponseType) -> Void | |
public init(completion: @escaping (ResponseType) -> Void) { | |
self.completion = completion | |
} | |
/// Call to pass the response to Siri | |
public func showResponse(_ response: IntentResponse) { | |
// Ugly but we're at the intersection of static typing and the liberal arts | |
guard let safeResponse = response as? ResponseType else { | |
fatalError("Wrong response type, expected \(ResponseType.self) but got \(type(of: response))") | |
} | |
completion(safeResponse) | |
} | |
} | |
// The subtype of Action that is for Siri Intent actions to conform, to get default behaviours | |
// This uses a re-stating of the associated type constraint approach suggested by Slava | |
protocol IntentAction: Action { | |
associatedtype IntentResponseType: IntentResponse | |
// THIS IS WHAT IS DIFFERENT | |
associatedtype PresenterType = IntentPresenter<IntentResponseType> | |
} | |
// Provide the default implementations we want conforming types to receive without effort | |
extension IntentAction { | |
static func perform(withCallback callback: @escaping (IntentResponseType) -> Void) { | |
// Create the correct type of presenter automatically | |
let presenter = IntentPresenter(completion: callback) | |
// Now pass it to the underlying Action.perform call | |
perform(presenter: presenter) | |
} | |
} | |
// Use case, an intent action | |
// This will be our response type that we pass to the presenter | |
class MyIntentResponse: IntentResponse { | |
let status: Int | |
init(status: Int) { | |
self.status = status | |
} | |
} | |
final class DoSomethingWithSiriAction: IntentAction { | |
typealias IntentResponseType = MyIntentResponse | |
// This is required, unsurprisingly. However the point of doing all this using a `typealias` | |
// (which WORKS!) produces a Fix-It warning | |
// Comment this line out to get the error about PresenterType not being defined | |
typealias PresenterType = IntentPresenter<IntentResponseType> | |
// Provide Action.perform impl here, and set the response on the IntentPresenter | |
// that is our PresenterType | |
static func perform(presenter: PresenterType) { | |
presenter.showResponse(MyIntentResponse(status: 42)) | |
} | |
} | |
let intentCallback = { (response: MyIntentResponse) in | |
print("Response is: \(response)") | |
} | |
DoSomethingWithSiriAction.perform(withCallback: intentCallback) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment