Protocol conformance of system classes using more generic types - swift

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)

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.

Swift inferring a completion handler closure to be the default #nonescaping instead of #escaping when completion handler explicitly uses #escaping

Swift 4.2, Xcode 10.1
In the order processing app I'm working on, the user may do a search for orders already processed or submitted. When that happens, it will check to see if it has a cache of orders, and if it does not, it will refill that cache using an asynchronous API request, then check the cache again.
The function that refills the cache is a private static one that accepts an escaping completion handler. Whenever I have used that completion handler in the past, all I had to do was add a closure at the end of the function call. This was before I was instructed to make a cache of all data wherever possible, and only use the API to refill that cache. Since then, the function has become private, because there will never be a need to call the API directly from anywhere but within this class.
Now, when I put the closure directly after the function call, it's giving me an error that basically says I'm passing a #nonescaping closure instead of an #escaping closure:
"Cannot invoke 'getAndCacheAPIData' with an argument list of type '(type: Codable.Type, (String?) -> Void)', Expected an argument list of type '(type: CodableClass.Type, #escaping (String?) -> Void)'"
I've never had to explicitly declare a closure to be #escaping before, nether does it seem to be possible. I suspect that because the function is both private AND static, there's some kind of issue happening with the way closures are inferred to be #escaping. I'm out of my depth. I could try converting the static class to a singleton, but I'm hesitant to refactor a bunch of working code because of one error until I'm absolutely sure that change will resolve the issue, and that what I'm trying to do isn't possible unless I change my approach.
Here's the code:
public static func fillSearchResultArray<ManagedClass: NSManagedObject>(query:String, parameters:[String], with type: ManagedClass.Type, completionHandler: #escaping (String?)->Void)
{
let codableType:Codable.Type
switch type
{
case is ClientTable.Type:
codableType = ClientData.self
case is OrderTable.Type:
codableType = OrderData.self
case is ProductTable.Type:
codableType = ProductData.self
default:
completionHandler("Unrecognized type.")
return
}
let fetchedData:[ManagedClass]
do
{
fetchedData = try PersistenceManager.shared.fetch(ManagedClass.self)
}
catch
{
completionHandler(error.localizedDescription)
return
}
if fetchedData.isEmpty
{
AppNetwork.getAndCacheAPIData(type: codableType)//error here
{(firstErrorString) in
//move search array data to the cache
if firstErrorString.exists
{
completionHandler(error)
}
else
{
AppNetwork.fillSearchResultArray(query: query, parameters: parameters, type: type)
{ errorString in
completionHandler(errorString)
}
}
}
return
}
else
{ ...
The signature of the function being called:
private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: #escaping (String?)->Void)
Why is swift inferring this closure to be the default #nonescaping when before it always inferred it to be #escaping?
The problem has nothing to do with the closure, or static, or private. It has to do with the type parameter. You cannot call this method:
private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: #escaping (String?)->Void)
with a variable of type Codable.Type. The type value you pass must be a concrete type, known at compile-time. If you want to pass a variable, you can't use a generic. It would have to be:
private static func getAndCacheAPIData(type: Codable.Type, completionHandler: #escaping (String?)->Void)
Alternately, you can call this as:
AppNetwork.getAndCacheAPIData(type: Int.self) {(firstErrorString) in ... }
or some other known-at-compile-time type.
Probably what you really want here is something like:
let completion: (String?) -> Void = {(firstErrorString) in ... }
switch ... {
case ...:
AppNetwork.getAndCacheAPIData(type: Int.self, completion: completion)
case ...:
AppNetwork.getAndCacheAPIData(type: String.self, completion: completion)
...
The basic problem is that protocols do not conform to themselves, so a variable of type Codable.Type does not satisfy the : Codable requirement. This comes down to the same reason you can't just call:
AppNetwork.getAndCacheAPIData(type: Codable.self) {...}
Alternately, you could refactor it this way:
private static func handleAPI<CodableClass: Codable>(type: CodableClass.Type) {
getAndCacheAPIData(type: type.self) { _ in ... the completion handler ..}
}
switch ... {
case ...:
AppNetwork.handleAPI(type: Int.self)
case ...:
AppNetwork.handleAPI(type: String.self)
...
Side note: Any & is meaningless here. You just meant <CodableClass: Codable>.

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

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.

Swift 3: Inheritance from non-named type

I have the following SSCIE:
protocol Foo {
associatedtype Bar
associatedtype Baz: (Self.Bar) -> Void
var currentValue: Bar { get }
}
That I want to use like this:
func call<T: Foo>(foo: T, callback: #escaping T.Baz) {
DispatchQueue.main.async {
callback(foo.currentValue)
}
}
But it fails to compile, with the error:
Inheritance from non-named type '(`Self`.Bar)'
This also fails to compile when I use (Bar) -> Void and (Foo.Bar) -> Void.
Sadly, Googling this didn't come up with any useful results.
Does anyone have any idea what this error means, what I'm doing wrong, and how to correct it?
Associated types in Swift 3 can only be "is-a"-constrained. So your Bar is required to be an Any. Which, by the way, is not much of a constraint ;). In other words, you can remove it.
However, (Self.Bar) -> Void is a function type and you can't constrain an associated type like this.
If you want to define a callback type, you can use a typealias:
protocol Foo
{
associatedtype Bar
typealias Callback = (Self.Bar) -> Void
var currentValue: Bar { get }
func f(callback: Callback) -> Void
}
Using #escaping does not currently work in a typealias (see SR-2316 and its various duplicates). This is a bug that was supposed to have a fix soon (as of August 2016). So you will have to spell it out for now:
func call<T: Foo>(foo: T, callback: #escaping (T.Bar) -> Void) {
DispatchQueue.main.async {
callback(foo.currentValue)
}
}
Update: As Hamish suggested, I filed SR-4967. I'll update this post as soon as there is any news about it.
As already mentioned, function types can't be used as associated types.
Try this instead:
func call<T: Foo>(foo: T, callback: #escaping (T.Bar) -> Void) {
...
}
and using this design you can mix-and-match function types (for the callback arg) for each specific helper function (call in your example) you come up with.