Here I have a function that creates a DataResponseSerializer with a generic type called T
extension DataResponseSerializer {
static func mappableObjectSerializer<T: Mappable>() -> DataResponseSerializer<T> {...}
}
And another extension with a function to do the request, which returns a generic type called SuccessObjectType, in this one I call the function above.
public extension DataRequest {
#discardableResult func requestObject<SuccessObjectType: Mappable>(onSuccess success: #escaping ((SuccessObjectType?) -> Void),
onFailure failure: #escaping ((NSError?) -> Void),
onCompletion completion: (() -> Void)? = nil) -> Self {
let responseSerializer = DataResponseSerializer<SuccessObjectType>.mappableObjectSerializer()
return response(queue: nil, responseSerializer: responseSerializer, completionHandler: { (response) in
switch response.result {...})
}
}
The idea is to disassociate the serializer from the request itself, so I can implement UnitTest on a legacy project, but for some reason I get the error
Generic parameter 'T' could not be inferred
On the line:
let responseSerializer = DataResponseSerializer<SuccessObjectType>.mappableObjectSerializer()
Thanks to this answer Cannot explicitly specialize a generic function I could find a way around it, it's pretty straight forward, from this:
let responseSerializer = DataResponseSerializer<SuccessObjectType>.mappableObjectSerializer()
To this:
let responseSerializer: DataResponseSerializer<SuccessObjectType> = DataResponseSerializer<SuccessObjectType>.mappableObjectSerializer()
What it does is to type cast the expected return from the function, this way the compile knows what to expect from the function.
Related
I am a newbie and maybe this is a silly question but I need some help.
I have this code like below but I wonder that should I remove "#escaping" inside the checkSignedIn function.
Class A{
public func checkSignedIn(complete: #escaping (Bool) -> Void) {
_ = Amplify.Auth.fetchAuthSession { (result) in
do {
let session = try result.get()
complete(session.isSignedIn)
} catch {
print("Fetch auth session failed with error - \(error)")
complete(false)
}
}
}
I imagine that using "#escaping" will escape the return value from closure if I assign complete() to a variable like below.
Class A{
var complete: (() -> Void)?
public func checkSignedIn(complete: #escaping (Bool) -> Void) {
_ = Amplify.Auth.fetchAuthSession { (result) in
do {
let session = try result.get()
self.complete = complete(session.isSignedIn)
} catch {
print("Fetch auth session failed with error - \(error)")
self.complete = complete(false)
}
}
}
Then I can call A.complete again.
Am I wrong? I appreciate it if you teach me about this.
No, they won't be the same.
The complete: #escaping (Bool) -> Void defines this:
a function (or a callback) that takes 1 argument (Bool), and returns nothing (Void). It's an equivalent of function that looks like this:
func complete(_ value: Bool) { }
this function escapes the scope of the function it's passed to, as it runs asynchronously (that's #escaping keyword meaning)
And then this function is called with complete(session.isSignedIn), where session.isSignedIn is a boolean argument you pass in, just as function's definition states, and function returns nothing
The statement self.complete = complete(session.isSignedIn) won't compile:
You defined self.complete as (() -> Void) type - that is a function or callback that takes no arguments, and returns nothing. It's an equivalent of function:
func y() { }
So complete(session.isSignedIn) returns Void type as we know. Assigning Void type to (() -> Void) type is not going to work.
If you want to save the escaping function / callback to be used elsewhere, you can do this:
// Make sure signature of the variable matches that of a function argument
var complete: ((Bool) -> Void)?
public func checkSignedIn(complete: #escaping (Bool) -> Void) {
// Save callback at the start of the function
self.complete = complete
// Then continue to a asynch part of the code:
_ = Amplify.Auth.fetchAuthSession { (result) in
// Inside this callback, you still can use the function argument
complete(session.isSignedIn)
...
}
// But in another function, you have to use self.complete, e.g.:
func x() {
// Use saved callback. You don't have to say `self` here, just using it for clarity
self.complete(true)
}
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 ...*/ })
I am using generics to build a paging library where I have code similar to this one (simplified):
struct PageData<T> {
let item: T
let pageNumber: Int
}
protocol PagedServiceProvider: class {
func fetchPage<ItemType>(page: Int, completion: #escaping (PageData<ItemType>?) -> Void)
}
class TestPagedServiceProvider<ItemType>: PagedServiceProvider {
var pageData: PageData<ItemType>?
func fetchPage<ItemType>(page: Int, completion: #escaping (PageData<ItemType>?) -> Void) {
completion(pageData) // COMPILATION ERROR HERE
}
}
the call to completion(pageData) produces the following error:
Cannot convert value of type PageData<ItemType>? to expected argument type PageData<_>?
This workaround casting with as? gets rid of the error:
class TestPagedServiceProvider<ItemType>: PagedServiceProvider {
var pageData: PageData<ItemType>?
func fetchPage<ItemType>(page: Int, completion: #escaping (PageData<ItemType>?) -> Void) {
completion(pageData as? PageData<ItemType>)
}
}
Also if var pageData: PageData<ItemType>? is declared as a local variable inside the fetchPage<ItemType>... method, the error is gone as well.
NOTE: This gist provides additional context.
I am confused as to why the compiler requires an explicit cast (that looks redundant to me in this case).
Could it be a matter of placeholder types scope (the ItemType in the fetchPage<ItemType>... method is not the same as the ItemType for the TestPagedServiceProvider<ItemType> class)?
I tried giving them different names and using where clauses to no avail.
The following code worked for me.
struct PageData<T> {
let item: T
let pageNumber: Int
}
protocol PagedServiceProvider: class {
associatedtype T
var pageData: PageData<T>? { get set }
func fetchPage(page: Int, completion: #escaping (PageData<T>?) -> Void)
}
class TestPagedServiceProvider: PagedServiceProvider {
var pageData: PageData<String>?
func fetchPage(page: Int, completion: #escaping (PageData<String>?) -> Void) {
completion(pageData)
}
}
Your type signature says that the caller can pass any type as ItemType, but then you require the closure to accept a PageData<String>?.
fetchPage is not actually generic (it can't accept types chosen by the caller), so you can't claim it is. What you mean is to remove the <ItemType> and say what you require:
func fetchPage(page: Int, completion: #escaping (PageData<String>?) -> Void) {
^^ ^^^^^^
This, however, breaks your PagedServiceProvider protocol, because that protocol requires that TestPagedServiceProvider accept absolutely any ItemType, and it doesn't. You'll need to decide what you really mean. You might mean a protocol with an associated type here, which is a type that the implementation gets to pick rather than the caller.
protocol PagedServiceProvider: class {
associatedtype ItemType
func fetchPage(page: Int, completion: #escaping (PageData<ItemType>?) -> Void)
}
This opens up some headaches, however, since now PagedServiceProvider cannot easily be put in an Array (it's no longer a concrete type). How you deal with this depends heavily on what problem you're actually solving. There is no general solution that is always appropriate.
I think the below code will work for you. I have passed String as the Generic type while instantiating the PageData object.
func fetchPage<String>(page: Int, completion: #escaping (PageData<String>?) -> Void) {
let page = PageData<String>(item: "Item", pageNumber: 1)
completion(page)
}
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.
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
....
}
}