Use Generic Protocol as Interface - ios

I want to create a UseCase protocol, using generic protocol. Then I want to create an interface for all implementation, in order to create mocks for tests.
Here is what I have done so far:
struct Product {}
protocol UseCase {
associatedtype ReturnType
associatedtype Param
func execute(_ params: Param, completion: ((ReturnType) -> Void))
}
protocol FetchProductsUseCase: UseCase {
associatedtype ReturnType = [Product]
associatedtype Param = Void
}
struct FetchProductsUseCaseImpl: FetchProductsUseCase {
func execute(_ params: Param , completion: ((ReturnType) -> Void)) {
completion([])
}
}
//ERROR: Protocol 'FetchProductsUseCase' can only be used as a generic constraint because it has Self or associated type requirements
var useCase: FetchProductsUseCase!
Can someone help me to fix that?
I've search over SOF, and I found multiple of topics about generics, but none of them are helpful for my case.

So there is no way to "constraint" the FetchProductUse case to only accept one couple of Generics ? ( ie: Void/[Product] ) ?
Yes, but your FetchProductsUseCase is not how you do it. Do this instead:
struct AnyUseCase<P, R>: UseCase {
typealias ReturnType = R
typealias Param = P
init<U>(useCase: U) where U: UseCase, U.ReturnType == ReturnType, U.Param == Param {
_execute = useCase.execute
}
func execute(_ params: P, completion: ((R) -> Void)) {
_execute(params, completion)
}
let _execute: (P, (R) -> Void) -> Void
}
var useCase: AnyUseCase<Void, [Product]>!
Then you can do something like:
useCase = AnyUseCase(useCase: FetchProductsUseCaseImpl())
I think your next error is going to be that completion is not escaping. It probably needs to be.

Related

Defining type of optional generic closure parameter

I want to create an interface, that can be invoked with a generic and a non generic parameter used in the Result type.
The API would look like the following:
struct NonGenericParameter {}
func taskPreparation<T: Decodable>(onTypedComplete: ((Result<T, Error>) -> Void)?,
onTyplessComplete: ((Result<NonGenericParameter, Error>) -> Void)?) {
// Do the neccessery preparation...
if let onComplete = onTypedComplete {
task(onComplete: onComplete)
}
if let onComplete = onTyplessComplete {
task(onComplete: onComplete)
}
}
func task<T: Decodable>(onComplete: #escaping (Result<T, Error>) -> Void) {
// do task...
}
func task(onComplete: #escaping (Result<NonGenericParameter, Error>) -> Void) {
// do task...
}
However, when i try to invoke the taskPreparation API, specifying onTyplessComplete as nil
taskPreparation(onTypedComplete: nil,
onTyplessComplete: { result in // Do something ... })
I receive the error
Generic parameter 'T' could not be inferred.
I understand, i have to specify the type of the generic parameter. I have tried to create a dummy decodable parameter, and pass it to the closure.
struct DummyDecodable: Decodable {}
taskPreparation(onTypedComplete: { (result: Result<DummyDecodable, Error>) in },
onTyplessComplete: { result in // Do something ... })
But obviously, in this case the onTypedComplete closure is not nil.
Does someone have an idea how could I specify a nil closure and satisfy the type inference too?
You would still need the DummyDecodable for this, which is kind of ugly, but at least you are passing a nil value:
Simply pass ((Result<DummyDecodable, Error>) -> Void)?.none. nil is in fact just a syntactic sugar for Optional<WhateverType>.none.
struct DummyDecodable: Decodable {}
taskPreparation(onTypedComplete: ((Result<DummyDecodable, Error>) -> Void)?.none,
onTyplessComplete: { result in /* Do something ...*/ })

Overloading generic functions in iOS Swift

I am trying to build a finder that tries and finds multiple types that are being passed to it inside a closure.
enum SomeError: Error {
case notInitialized
}
struct TestFinder {
func getSomething<T, U>(_ function: #escaping (T) -> U) throws -> U {
guard let t: T = get() else {
throw SomeError.notInitialized
}
return function(t)
}
func getSomething<T, U, V>(_ function: #escaping (T, U) -> V) throws -> V {
guard let t: T = get(), let u: U = get() else {
throw SomeError.notInitialized
}
return function(t, u)
}
func getSomething<T, U, V, W>(_ function: #escaping (T, U, V) -> W) throws -> W {
guard let t: T = get(), let u: U = get(), let v: V = get() else {
throw SomeError.notInitialized
}
return function(t, u, v)
}
private func get<T>() -> T? {
nil
}
}
struct UserDetails {
let name: String
let roll: String
}
I call the finder as:
let testReturnType = try? TestFinder().getSomething(UserDetails.init)
Compiler throws me an error of:
Ambiguous use of 'getSomething'
Reason for this error (from docs):
You can overload a generic function or initializer by providing different constraints, requirements, or both on the type parameters. When you call an overloaded generic function or initializer, the compiler uses these constraints to resolve which overloaded function or initializer to invoke.
But if I comment:
func getSomething<T, U>(_ function: #escaping (T) -> U) throws -> U
Everything starts working. It has something to do with the compiler not able to identify which function signature to resolve.
Any particular solution for this?
You haven't quite focussed on the actual issue. Let's eliminate everything irrelevant from the example. This compiles and works as expected:
struct TestFinder {
func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}
func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}
func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}
}
And here we'll test it:
func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f) // "four"
But if you add the version with one passed function parameter, everything breaks down:
struct TestFinder {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}
func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}
func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}
func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}
}
Now we can't compile, because the first version is seen as a candidate. And indeed, if we remove the other versions, we still compile!
struct TestFinder {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}
}
That's the weird part. We still compile, even though we are saying:
func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f)
Evidently, this function with four parameters is seen by the compiler as "fitting" the declaration with just one generic parameter.
I regard this as a bug. I think I can guess what might cause it; it could have to do with the legacy of function parameter list as tuples. This function f is "equivalent" to a function taking a single parameter consisting of a four-string tuple. Nevertheless, you cannot actually call the function inside doSomething with a four-string tuple; I cannot find a way to call it at all.
So, I would say, regard this as a bug, and work around it for now by removing the first version of your generic.
UPDATE: On the advice of the Swift team, I tested with the May 4, 2020 Swift 5.3 Development toolchain. With it, your code compiles and behaves as expected. This was indeed a bug, and it was fixed as part of
https://bugs.swift.org/browse/SR-8563
Returning for a moment to my version, my code, too, compiles and behaves as expected, with all four versions of doSomething present. However, note that if you delete all but the first version of doSomething, it still compiles and runs. Moreover, you can call function with four parameters by bundling them into a tuple and force casting, like this:
struct TestFinder2 {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
function(("manny", "moe", "jack", "henry") as! T)
}
}
That seems to confirm my guess that what you're seeing is a consequence of the hidden tuple-nature of a function's parameter list. One can draw the same conclusion from the discussion of the bug, which refers to "tuple-splatting".
The key point is the UserDetails struct, because this struct has two properties and without any designed initializer, the initializer could be UserDetails(name: , roll: ) or UserDetails(name: ) or UserDetails(roll: ), this is the ambitious part. if you just delete one property of UserDetails that will work too, because one property struct has only one designed initializer.
If you comment
func getSomething<T, U>(_ function: #escaping (T) -> U) throws -> U
Which means the finder has only one chosen:
func getSomething<T,U,V>(_ function: #escaping(T,U) -> V) throws -> V

How to constraint function generic type according to a parameter's property?

I have this enum:
enum ItemType: String {
case image
case movie
var rawValue: String {
switch self {
case .image: return String(kUTTypeImage)
case .movie: return String(kUTTypeMovie)
}
}
}
and this function inside a class
func items<T>(for type: ItemType, completion: ([T]) -> Void) where T: NSSecureCoding {}
Now what I would like to achieve is that if the ItemType is .image I would like the completion to be inferred as of type ([UIImage]) -> Void otherwise if it is .video I would like it to be inferred as ([URL]) -> Void
Is this possible in any way in Swift? Or what would be an alternative approach to make the completion type infer according to the type provided.
Additional details:
The body of the function uses NSItemProvider loadItem instance method whose closure returns any type conforming to NSSecureCoding. So as long as I can give a type like that I don't care about what type specifically it is.
func items<T>(for type: ItemType, completion: ([T]) -> Void) where T: NSSecureCoding {
itemProviders(for: [type]).forEach { itemProvider in
itemProvider.loadItem(forTypeIdentifier: type.rawValue, options: nil, completionHandler: { (item: T, error: Error!) in
})
}
}
You can't do this because the parameter type is evaluated at runtime, while at compile time, T needs to be inferred.
A workaround is to separate this into two methods:
func itemsForImages(completion: ([UIImage]) -> Void) { ... }
func itemsForMovies(completion: ([URL]) -> Void) { ... }
And then determine which method to call:
switch itemType {
case .image:
itemsForImages { images in ... }
case .movies:
itemsForMovies { urls in ... }
}
Another alternative is to have a closure of type ([Any]) -> Void and the caller needs to cast the parameter to the correct types, but this is not so type safe.

Using Generics in completionHandler

I have a simple app, that communicates with server via TCP Socket using custom protocol. I want to achieve HTTP-like response-request behaviour, abstracting from socket layer.
So I have simple protocol:
protocol ResponseType {
init(with frame: SocketMessage)
}
And some of examples:
struct MessageAck: ResponseType {
var messageId: String
init(with frame: SocketMessage) {
messageId = frame.messageId
}
}
I created simple protocol for sending requests:
protocol APIClient {
func send<T: ResponseType>(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?)
}
enum SocketAPIRequest {
case textMessage(messageId: String, ...)
...
}
And finally:
enum Result<T> {
case success(T)
case failure(Error)
}
class SocketAPIClient: APIClient {
typealias MessageId = String
private var callbacks = [Receipt: ((Result<ResponseType>) -> Void)]()
...
func send<T>(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?) where T : ResponseType {
....
callbacks[stompFrame.receiptId] = completion
....
}
}
So, when I want to store callback for each request, to call it after answer will be received, I got such error:
Cannot assign value of type '((Result<T>) -> Void)?' to type '((Result<ResponseType>) -> Void)?'
I guess the problem with mixing Type's and objects, or maybe something else.
Swift generics are not covariant (with special hard-coded exceptions for Array which involve copying the elements). That means that Result<Apple> is not a subtype of Result<Fruit>. See Swift Generics & Upcasting for examples of why.
In your case, what would prevent you from passing a Result<MessageBody> to a callback that expected a Result<MessageAck>? For example:
for callback in callbacks {
callback(result)
}
How could you know this was legal at compile time for any given type of result?
EDIT (BETTER ANSWER):
You can hide the type inside a closure to get what you want. Try this:
class SocketAPIClient: APIClient {
typealias MessageId = String
private var callbacks = [Receipt: ((Result<SocketMessage>) -> Void)]() // <--- Change
func send<T>(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?) where T : ResponseType {
// Store the closure we don't understand inside a closure we do
callbacks[stompFrame.receiptId] = { result in
switch result {
case .success(let message):
completion?(.success(T.init(with: message)))
case .failure(let error):
completion?(.failure(error))
}
}
}
}
Now, instead of trying to hold T directly in callbacks, it's held in each individual closure, hidden from the rest of the class, and T never escapes this function. When you get to wherever you call callback in your code, just pass it the Result<SocketMessage> that I assume you already have somewhere.
OLD ANSWER:
The simplest solution to your problem is to have the callback always pass a Result<Data> and remove T entirely:
protocol APIClient {
func send(request: SocketAPIRequest, completion: ((Result<Data>) -> Void)?)
}
Then leave it to the MessageAck (in the completion handler) to deserialize itself from the raw data.
There are other ways to achieve all this with type erasers, but they're much more complex and sometimes very fiddly.
Have you tried the following signature
func send<T:ResponseType>(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?){ ... }
and still getting error?
Edit 1:
or probably you should try something like this
protocol APIClient {
associatedtype T
func send(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?)
}
and,
class SocketAPIClient: APIClient {
typealias MessageId = String
typealias T = ResponseType
private var callbacks = [Receipt: ((Result<ResponseType>) -> Void)]()
...
func send(request: SocketAPIRequest, completion: ((Result<T>) -> Void)?) {
....
callbacks[stompFrame.receiptId] = completion
....
}
}

Partial function application with generics

I'm working with an Observer API (ObserverSet) which have the following function :
public func add<T: AnyObject>(object: T, _ f: T -> Parameters -> Void) -> ObserverSetEntry<Parameters>
It simply register an object then call the instance method f on the object when notification triggers
In one of my manager, I need to hide the previous function with one of mine so I can force an observer to call a predefine function implemented via a protocol.
Here's what I've done so far :
#objc protocol Observer : NSObjectProtocol {
func observe(param: String) -> Void
}
func addObserver<T: AnyObject where T: Observer>(observer: T) {
let f: T -> String -> Void = observer.dynamicType.observe
entries.addObserver(observer, f)
}
Unfortunately, I have the following error showing up Partial application of generic method is not allowed
I've found a possible workaround somewhere on SO which look like that :
let f: T -> String -> Void = { (obs: T) in obs.dynamicType.observe(obs) }
But this line of code drives my XCode crazy with some Segmentation Fault: 11 on compilation (and Communication interrupted with Playground ..)
Is there any workaround for what I'm trying to do ?
I haven't tested but you can try:
#objc protocol Observer : NSObjectProtocol {
func observe(param: String) -> Void
}
func addObserver<T: AnyObject where T: Observer>(observer: T) {
let f: T -> String -> Void = { ($0 as AnyObject).observe }
entries.addObserver(observer, f)
}
At least, this compiles because AnyObject has all methods from ObjC - including #objc - classes/protocols, as ImplicitlyUnwrappedOptional.
So, this compiles:
let str = NSString(string: "test")
(str as AnyObject).observe("foo")
Of course this causes runtime error because NSString has no observe(_:) method. But, in your case, T is guaranteed to be Observer, it should works.

Resources