Why doesn't this behave the way I expect (hope) it would? - swift

I have a several protocols set up in my framework to deal with resources. In one of the protocols, I have set up an extension to provide a default implementation for a decode function. It's simpler to show the code and what happens (see calls to fatalError). There's a lot more code in the actual implementation, but this illustrates the issue:
This is the "base" protocol:
public protocol Resourceful {
associatedtype AssociatedResource
typealias ResourceCompletionHandler = (AssociatedResource?, Error?) -> Void
func fetch(_ completion: #escaping ResourceCompletionHandler)
}
This is a generic, concrete implementaion of Resourceful:
open class WebResourceApiCall<Resource>: Resourceful {
public typealias AssociatedResource = Resource
public typealias FetchedResponse = (data: Data?, urlResponse: URLResponse?)
public init() {
}
public func fetch(_ completion: #escaping ResourceCompletionHandler) {
try! decode(fetched: (data: nil, urlResponse: nil))
}
public func decode(fetched: FetchedResponse) throws -> Resource {
fatalError("It ends up here, but I don't want it to!")
}
}
extension WebResourceApiCall where Resource: Decodable {
public func decode(fetched: FetchedResponse) throws -> Resource {
fatalError("This is where I want it to go...")
}
}
This is how I'm attempting to use it:
public struct Something: Decodable { }
var apiCall = WebResourceApiCall<Something>()
apiCall.fetch { _, _ in } // Implictly calls decode... but not the decode I expected it to! See fatalError() calls...
Instead of calling decode on the extension, like I hoped it would, the "default" decode method with no constraints is always called.
Why doesn't this work the way I expect it to?
Thanks in advance!

Swift is a statically dispatched language, thus the address of the decode() function to be called is computed at compile time, and because the call happens inside the base definition of the class, the compiler picks the original implementation.
Now, if you call the method from a place where the compiler has enough information to pick the implementation you need, it will work:
var apiCall = WebResourceApiCall<Something>()
try apiCall.decode(fetched: (nil, nil))
The above code will call the method from the specialized extension, as at this point the compiler is a better position to know that it has a more specialized implementation to call.
It should be possible to achieve the behaviour you need if you move the decode() method in the dynamic dispatch world - i.e. at the protocol level.

Related

Use generic protocols as variable type

I would like to create a generic protocol for a data fetching service like this:
protocol FetchDataDelegate: AnyObject {
associatedtype ResultData
func didStartFetchingData()
func didFinishFetchingData(with data: ResultData)
func didFinishFetchingData(with error: Error)
}
class Controller: UIViewController, FetchDataDelegate {
func didStartFetchingData() {
//...
}
func didFinishFetchingData(with data: ResultData) {
//...
}
func didFinishFetchingData(with error: Error) {
//...
}
}
class NetworkManager {
weak var delegate: FetchDataDelegate?
//...
}
The Controller class would have a reference to a NetworkManager instance, and through this, I would like to start the network operations. When the operation is ended I would like to call the appropriate delegate function to pass the result back to the controller. But with this setup I got the following error:
Protocol 'FetchDataDelegate' can only be used as a generic constraint because it has Self or associated type requirements
The question is what should I do to use this generic protocol as a variable type? Or if is not possible what would be the correct way? Thanks!
While this is closely related to the question David Smith linked (and you should read that as well), it's worth answering separately because it's a different concrete use case, and so we can talk about it.
First, imagine you could store this variable. What would you do with it? What function in NetworkManager could call delegate.didFinishFetchingData? How would you generate the ResultData when you don't know what it is?
The point is that this isn't what PATs (protocols with associated types) are for. It's not their goal. Their goal is to help you add extensions to other types, or to restrict which kinds of types can be passed to generic algorithms. For those purposes, they're incredibly powerful. But what you want are generics, not protocols.
Instead of creating delegates, you should use generic functions to handle the result of a specific call, rather than trying to nail down each view controller to a specific result type (which isn't very flexible anyway). For example, in the simplest, and least flexible way that still gives you progress reporting:
struct APIClient {
func fetch<Model: Decodable>(_: Model.Type,
with urlRequest: URLRequest,
completion: #escaping (Result<Model, Error>) -> Void)
-> Progress {
let session = URLSession.shared
let task = session.dataTask(with: urlRequest) { (data, _, error) in
if let error = error {
completion(.failure(error))
}
else if let data = data {
let decoder = JSONDecoder()
completion(Result {
try decoder.decode(Model.self, from: data)
})
}
}
task.resume()
return task.progress
}
}
let progress = APIClient().fetch(User.self, with: urlRequest) { user in ... }
This structure is the basic approach to this entire class of problem. It can be made much, much more flexible depending on your specific needs.
(If your didStartFetchingData method is very important, in ways that Progress doesn't solve, leave a comment and I'll show how to implement that kind of thing. It's not difficult, but this answer is pretty long already.)
Possible duplicate of this
In summary, you cannot use generic protocols as variable types, it would have to be leveraged as a generic constraint since you wouldn't know the type of ResultData at compilation time

enums with Associated Values + generics + protocol with associatedtype

I'm trying to make my API Service as generic as possible:
API Service Class
class ApiService {
func send<T>(request: RestRequest) -> T {
return request.parse()
}
}
So that the compiler can infer the response type from the request categories .auth and .data:
let apiService = ApiService()
// String
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")))
// Int
let intResponse = apiService.send(request: .data(.content(id: "123")))
I tried to come up with a solution using generics and a protocol with associated type to handle the parsing in a clean way. However I'm having trouble associating the request cases with the different response types in a way that it's simple and type-safe:
protocol Parseable {
associatedtype ResponseType
func parse() -> ResponseType
}
Endpoints
enum RestRequest {
case auth(_ request: AuthRequest)
case data(_ request: DataRequest)
// COMPILER ERROR HERE: Generic parameter 'T' is not used in function signature
func parse<T: Parseable>() -> T.ResponseType {
switch self {
case .auth(let request): return (request as T).parse()
case .data(let request): return (request as T).parse()
}
}
enum AuthRequest: Parseable {
case login(email: String, password: String)
case signupWithFacebook(token: String)
typealias ResponseType = String
func parse() -> ResponseType {
return "String!!!"
}
}
enum DataRequest: Parseable {
case content(id: String?)
case package(id: String?)
typealias ResponseType = Int
func parse() -> ResponseType {
return 16
}
}
}
How is T not used in function signature even though I'm using T.ResponseType as function return?
Is there a better still clean way to achieve this?
I'm trying to make my API Service as generic as possible:
First, and most importantly, this should never be a goal. Instead, you should start with use cases, and make sure that your API Service meets them. "As generic as possible" doesn't mean anything, and only will get you into type nightmares as you add "generic features" to things, which is not the same thing as being generally useful to many use cases. What callers require this flexibility? Start with the callers, and the protocols will follow.
func send<T>(request: RestRequest) -> T
Next, this is a very bad signature. You don't want type inference on return types. It's a nightmare to manage. Instead, the standard way to do this in Swift is:
func send<ResultType>(request: RestRequest, returning: ResultType.type) -> ResultType
By passing the expected result type as a parameter, you get rid of the type inference headaches. The headache looks like this:
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")))
How is the compiler to know that stringResponse is supposed to be a String? Nothing here says "String." So instead you have to do this:
let stringResponse: String = ...
And that's very ugly Swift. Instead you probably want (but not really):
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")),
returning: String.self)
"But not really" because there's no way to implement this well. How can send know how to translate "whatever response I get" into "an unknown type that happens to be called String?" What would that do?
protocol Parseable {
associatedtype ResponseType
func parse() -> ResponseType
}
This PAT (protocol w/ associated type) doesn't really make sense. It says something is parseable if an instance of it can return a ResponseType. But that would be a parser not "something that can be parsed."
For something that can be parsed, you want an init that can take some input and create itself. The best for that is Codable usually, but you could make your own, such as:
protocol Parseable {
init(parsing data: Data) throws
}
But I'd lean towards Codable, or just passing the parsing function (see below).
enum RestRequest {}
This is probably a bad use of enum, especially if what you're looking for is general usability. Every new RestRequest will require updating parse, which is the wrong place for this kind of code. Enums make it easy to add new "things that all instances implement" but hard to add "new kinds of instances." Structs (+ protocols) are the opposite. They make it easy to add new kinds of the protocol, but hard to add new protocol requirements. Requests, especially in a generic system, are the latter kind. You want to add new requests all the time. Enums make that hard.
Is there a better still clean way to achieve this?
It depends on what "this" is. What does your calling code look like? Where does your current system create code duplication that you want to eliminate? What are your use cases? There is no such thing as "as generic as possible." There are just systems that can adapt to use cases along axes they were prepared to handle. Different configuration axes lead to different kinds of polymorphism, and have different trade-offs.
What do you want your calling code to look like?
Just to provide an example of what this might look like, though, it'd be something like this.
final class ApiService {
let urlSession: URLSession
init(urlSession: URLSession = .shared) {
self.urlSession = urlSession
}
func send<Response: Decodable>(request: URLRequest,
returning: Response.Type,
completion: #escaping (Response?) -> Void) {
urlSession.dataTask(with: request) { (data, response, error) in
if let error = error {
// Log your error
completion(nil)
return
}
if let data = data {
let result = try? JSONDecoder().decode(Response.self, from: data)
// Probably check for nil here and log an error
completion(result)
return
}
// Probably log an error
completion(nil)
}
}
}
This is very generic, and can apply to numerous kinds of use cases (though this particular form is very primitive). You may find it doesn't apply to all your use cases, so you'd begin to expand on it. For example, maybe you don't like using Decodable here. You want a more generic parser. That's fine, make the parser configurable:
func send<Response>(request: URLRequest,
returning: Response.Type,
parsedBy: #escaping (Data) -> Response?,
completion: #escaping (Response?) -> Void) {
urlSession.dataTask(with: request) { (data, response, error) in
if let error = error {
// Log your error
completion(nil)
return
}
if let data = data {
let result = parsedBy(data)
// Probably check for nil here and log an error
completion(result)
return
}
// Probably log an error
completion(nil)
}
}
Maybe you want both approaches. That's fine, build one on top of the other:
func send<Response: Decodable>(request: URLRequest,
returning: Response.Type,
completion: #escaping (Response?) -> Void) {
send(request: request,
returning: returning,
parsedBy: { try? JSONDecoder().decode(Response.self, from: $0) },
completion: completion)
}
If you're looking for even more on this topic, you may be interested in "Beyond Crusty" which includes a worked-out example of tying together parsers of the kind you're discussing. It's a bit dated, and Swift protocols are more powerful now, but the basic message is unchanged and the foundation of things like parsedBy in this example.

Protocol conformance of system classes using more generic types

To mock objects in Swift for test, I generally follow a pattern of authoring a Protocol describing the behaviour of the object I'd like, and then using Cuckoo to generate mocks for it for test.
Usually, these protocols map directly onto existing types, and this works fine, until I need to make the existing type work with my new protocol types.
public typealias RequestCompletionHandler = (Request, Error?) -> Swift.Void
public protocol Request {
var results: [Any]? { get }
var completionHandler: RequestCompletionHandler? { get }
}
extension VNRequest: Request {}
Here, VNRequest already has a member called completionHandler that returns the following type:
public typealias VNRequestCompletionHandler = (VNRequest, Error?) -> Swift.Void
Technically, all of these types should match up, but obviously it's not a very easy scenario for the type solver to solve, so the compiler isn't too cheerful about it.
At first I thought I'd be able to refer to the original completionBlock implementation by doing the following:
extension VNRequest: Request {
public var completionHandler: RequestCompletionHandler? {
return (self as VNRequest).completionHandler
}
}
But it's not too happy about that either.
Any advice about how best to do this? I've thought about using a different name in the protocol (e.g: completionBlock_ or completionBlock$), which works, but it's a bit scrappy.
The problem occurs due to the fact that Swift is covariant in respect to closure return type, and contra-variant in respect to its arguments. Which means that (VNRequest, Error?) -> Void can't be used where (Request, Error?) -> Void is needed (the other way around is possible).
You can solve your problem by using an associated type in the Request protocol:
public protocol Request {
associatedtype RequestType = Self
var results: [Any]? { get }
var completionHandler: ((RequestType, Error?) -> Void)? { get }
}
The above protocol definition will make the VNRequest class compile, as the compiler will detect the match for RequestType.
The downside, though, is that protocols with associated types have some limitations regarding where they can be used, and also passing them as function arguments will require some where clauses to make them work.
Another alternative would be to use Self as a parameter to the completion handler:
public typealias RequestCompletionHandler<T> = (T, Error?) -> Swift.Void
public protocol Request {
var results: [Any]? { get }
var completionHandler: RequestCompletionHandler<Self>? { get }
}
This will also solve the conformance issue, but also comes with some constraints: VNRequest must be final, the functions using Request must be generic:
func send<R: Request>(_ request: R)

Wrapping a callback type with Swift protocols

Let's say I'm dealing with a library I don't control and I'm trying to wrap a class that defines a callback type to decouple my code for testing purposes. Here's the class, inside module AXSwift:
public class Application {
public typealias Callback = (element: UIElement) -> ()
public func createObserver(callback: Callback) -> Observer? {
// ...
}
}
Here's the wrapper protocol for testing:
protocol UIElementProtocol {}
extension AXSwift.UIElement: UIElementProtocol {}
protocol ApplicationProtocol {
func createObserver(callback: (element: UIElementProtocol) -> ()) -> Observer?
}
extension AXSwift.Application: ApplicationProtocol {}
I get Type 'Application' does not conform to protocol 'ApplicationProtocol'. If I change UIElementProtocol inside the ApplicationProtocol callback back to UIElement, it works. But UIElement conforms to UIElementProtocol, so why doesn't this work?
Second question: Is there a better way to design the library API to allow for this sort of thing?
The solution was really simple, just implement the protocol in the extension with a method that calls the original method:
extension AXSwift.Application: ApplicationProtocol {
func createObserver(callback: (element: UIElementProtocol) -> ()) -> Observer? {
return createObserver(callback as Callback)
}
}
Where Callback is the expected type. Apparently you have to be explicit about passing a general function type as a more specific type.

Call Class Methods From Protocol As Parameter

I want to be able to pass a class (not an initialized object) of a certain protocol type to a method, then call the class functions of that class in the method. Code below.
I am using Swift and have an protocol defined like this
//Protocol for any object to be used with an FAUAPIConnection
protocol FAUAPIModel{
//Used to parse the object from a given dictionary to an object
class func parseFromJSON(JSON:AnyObject) -> Self
//Required default init
init()
}
What I would like to do is have a method like this
func getSomeParsingDone<T:FAUAPIModel>(model:T.Type? = nil, getPath:path, callingObj:CallingClass) -> Void
{
//GetIt is inconsequential, just logic to get an object from a certain path
var returnObj:AnyObject = GetIt.get(path)
if(model != nil){
returnObj = model!.parseFromJSON() <<<<<< Type 'T' does not conform to protocol 'AnyObject'
}
callingObj.done(returnObj)
}
Object that implements the protocol
import Foundation
class MyObj: FAUAPIModel{
var neededVal:String
var nonneededVal:String
required convenience init(){
self.init(neededVal:"VALUE")
}
init(neededVal:String, nonneededVal:String = ""){
self.neededVal = neededVal
self.nonneededVal = nonneededVal
}
class func parseFromJSON(JSON:AnyObject) -> WGMPart
{
return WGMPart() <<<<<<<< Method 'parseFromJSON' in non-final class 'WGMPart' must return 'Self' to conform to protocol 'FAUAPIModel'
}
}
However, I keep getting two errors. I have indicated these above with '<<<<<<<<<<<<'
compile error.
Lots of little things to consider here, but let's get to the heart of your question. The signature you want looks like this:
func getSomeParsingDone<T:FAUAPIModel>(model:T.Type, path:String) -> T?
I'm making the return optional beause there are a lot of things that could fail here, and you really shouldn't turn all of those into crashes.
I'd recommend your protocol look like this:
protocol FAUAPIModel {
class func parseFromJSON(JSON:AnyObject) -> Self
}
That way, you're promising that your return your own class, not just anything that is parseable. That does tend to mean that you need to make your classes final. If you don't want them to be final, you'll need to promise some init method in order to construct it. See Protocol func returning Self for more details on how to deal with that if you need it.
So putting it together, it might look something like this in practice:
protocol FAUAPIModel {
class func parseFromJSON(JSON:AnyObject) -> Self
}
func createObjectOfClass<T: FAUAPIModel>(model: T.Type, path: String) -> T? {
if let json: AnyObject = GetJSON(path) {
return model.parseFromJSON(json)
}
return nil
}
// Bogus JSON reader
func GetJSON(path: String) -> AnyObject? {
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(path.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!, options: NSJSONReadingOptions(0), error: nil)
return json
}
// Bogus model class that returns trivial version of itself
final class Something: FAUAPIModel {
class func parseFromJSON(JSON:AnyObject) -> Something {
return Something()
}
}
// Using it
let something = createObjectOfClass(Something.self, "/path/to/file")
I just want to note that the answer to your exact question would be to declare your function like this:
func getSomeParsingDone(model:FAUAPIModel.Type? = nil, getPath:path) -> FAUAPIModel