You could write the following protocols:
protocol Request {
associatedtype Provider
var provider: Provider { get }
func execute()
}
protocol BLERequest: Request {
typealias Provider = BLEProvider
var code: UInt { get }
var characteristic: CBCharacteristic { get }
var service: CBService { get }
}
protocol HTTPRequest: Request {
typealias Provider = NetworkProvider
var data: Data { get }
var accessToken: String { get }
var method: HTTPMethod { get }
var params: [String:Any]? { get }
}
Then comes the magic; you can write generic extensions on your Request protocol, for each specific Request type.
extension Request where Self: BLERequest {
func execute() {
// provider is available since it is a get-able requirement,
// and we know it is a BLEProvider instance
// same goes for all the other get-able vars
provider.writeValue(data: code, characteristic: characteristic, service: service)
}
}
extension Request where Self: HTTPRequest {
func execute() {
// Again, same thing, we have all vars we need
provider.performRequest(data: data, token: accessToken, method: method, params: params)
}
}
Then, when you are implementing a new Request, all you need to do is implement the requirements for your chosen protocol, and everything else will be handled for you :)
Example:
enum MyBLERequest: BLERequest {
case getStatus
case download
// MARK: BLE Request conformance
var provider: Provider {
return BLEProvider(... whatever init is needed)
}
var code: UInt {
switch self {
case .getStatus: return 0x01
case .download: return 0x02
// Any further new case will require you to implement the code here, so you have compile-time safety :)
}
}
var characteristic: CBCharacteristic {
return CBCharacteristic(.. init)
}
var service: CBService {
return CBService(... more init)
}
}
Then you can simply do MyBLERequest.getStatus.execute(), and it's all done through the protocol extension code.
And since Swift Enums allow associated values, you can always pass in additional parameters, whenever needed (you could have another case case getAuthToken(byUserID: String), which are then available to you in your implementation)