Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcpalmer/88a6caaddcdcb07a014c701de3c54aef to your computer and use it in GitHub Desktop.
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
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