swift question about "#escaping" inside class - ios

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)
}

Related

How to use below closure variable in Swift

I am working on clouser in swift iOS, I have tried to create variable using clouser and add 2 clouser in single variable, variable declaration I did right, but I don't understand how to access it in my code.
here is the variable declaration.
var multipleClouser: ((_ success: (Bool), _ failer:(Error) -> Void)?
Most probably you meant callback, like
var multipleClouser: ((_ success: (Bool), _ failer: (Error?)) -> Void)?
and usage, like
multipleClouser?(true, nil)
or
multipleClouser?(false, SomeErrorHere)
set up like
multipleClouser = { success, error in
if success {
print("Done!")
} else {
print("Failure")
if let error = error {
print("\t with error: \(error)")
}
}
}

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 ...*/ })

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
....
}
}

Dealing with Closures - Make code more generic

There are two functions as shown below. Most of the functionality is the same in both. Its idea is to get the output of the webservice from getResponse() [Helper Callback], parse and pass the info to wrapper call back through getResult().
static func getAllDealers(dealerSearchServiceDomain: ARSDealerSearchServiceDomain, wrapperCallback:(getResult: () throws -> Void) -> Void) throws
{
try ARSInputValidator.validateZipCode(dealerSearchServiceDomain.zip)
try ARSDealerConnection.getAllDealers(dealerSearchServiceDomain, helperCallback: { (getResponse) -> Void in
do
{
let result = try getResponse()
try ARSDealerParser.parseDealerSearchResponse(dealerSearchServiceDomain)
wrapperCallback(getResult: { return })
}
catch
{
wrapperCallback(getResult: { throw error })
}
})
}
static func getDealerDetails(dealerDetailsServiceDomain: ARSDealerDetailsServiceDomain, wrapperCallback:(getResult: () throws -> Void) -> Void) throws
{
try ARSDealerConnection.getDealerDetails(dealerDetailsServiceDomain, helperCallback: { (getResponse) -> Void in
do
{
let result = try getResponse()
try ARSDealerParser.parseDealerDetailsResponse(dealerDetailsServiceDomain)
wrapperCallback(getResult: { return })
}
catch
{
wrapperCallback(getResult: { throw error })
}
})
}
I am trying to add a separate function for the common functionality like,
static func parser(serviceCallDomain: ARSServiceCallDomain ,wrapperCallback:(getResult:() throws -> String) -> Void, helperCallback:(getResponse:() throws -> String) -> Void) throws
{
helperCallback { (getResponse) -> Void in
But there is a compilation error & i am not able to complete it. There are 15+ web service calls, so a common shown as i am trying will be very helpful.
Next step, i also need to pass the functions parseDealerSearchResponse() & parseDealerDetailsResponse() to the common function.
I am new to closures. Kindly help.
//EDIT -- ADDING SAMPLE
I have a sample for the problem in Git - Refer class Layer1.swift
https://github.com/vivinjeganathan/ErrorHandling/tree/Closures-Refactor
I think the best you can do to refactor the code is to define a function that handles some of the common functionality like parsing and validation and that ultimately calls the completion closure back to the controller, something like this:
static func handleResponse(parser: Parser, validator: Validator, getResult: () throws -> AnyObject, completion: (getParsedResult: () throws -> AnyObject) -> Void) {
do
{
let result = try getResult()
let parsedObject = try parser.parse(result)
try validator.validate(parsedObject)
completion(getParsedResult: { return parsedObject })
}
catch
{
completion(getParsedResult: { throw error })
}
}
notice that it receives the parser, validator, the closure that captures the result from the layer below and the completion closure that belongs to the final user (usually the View Controller), and then this function could be used like this:
static func getAllDealers(dealerSearchServiceDomain: AnyObject, wrapperCallback:(getResult: () throws -> AnyObject) -> Void) throws {
let validator = DealersValidator() // create real validator
let parser = DealersParser() // create real parser
try validator.validate(dealerSearchServiceDomain)
try ARSDealerConnection.getAllDealers(dealerSearchServiceDomain, helperCallback: { (getResponse) -> Void in
self.handleResponse(parser, validator: validator, getResult: getResponse, completion: wrapperCallback)
})
}
in this case handleResponse lives in the same class with getAllDealers but it can actually be a global function that every service can call.
I think that it might be possible to write a better implementation using generics but it wouldn't be much shorter than this, in the end you can't save yourself from creating the validators and parsers and call the next layer.

How to specify completion handler with one function not return [duplicate]

Error: Cannot convert the expression type (String, MyType) to ()
From the following code
Test(method: {[weak self] (message: String) in self?.callback(message)}, instance: self)
and if I add a return statement, it works, and the error goes away
Test(method: {[weak self] (message: String) in self?.callback(message); return}, instance: self)
Not sure how to handle the above without having to have the dummy return statement, any advise.
Here's my class Test
public class Test {
private var instance: AnyObject?
private var method: ((message: String) -> ())?
public init(method: (String -> ())?, instance: AnyObject) {
}
}
Edit
I've done a playground based minimalistic example (please copy paste for a test)
class Test {
private var _method: ((String) -> ())?
weak private var _instance: AnyObject?
init(method: (String -> ())?, instance: AnyObject?) {
_method = method
_instance = instance
}
}
class Another {
func register() {
//this doesn't need a return
Test(method: {(message: String) in self.callback(message)}, instance: self)
//this needs a return once I add [weak self]
Test(method: { [weak self] (message: String) in self?.callback(message); return}, instance: self)
}
func callback(message: String) {
println(message)
}
}
Not sure how to handle the above without having to have the dummy return statement, any advise.
You have solved the problem beautifully. Anonymous functions automatically use a one-line function body as a return value, so to prevent that from causing a type mismatch with the expected return type (Void) you have to add another line of code so that it is not a one-line function body. The dummy return statement, which itself returns Void, is a great way to handle it; I would just use that and move on. There are some snazzier workarounds but what you have is precisely what I would do.
EDIT: To understand the source of the type mismatch, try this:
struct Test {
func voider() -> Void {}
}
let testMaybe = Optional(Test())
let result = testMaybe?.voider()
Now result is not a Void; it's an Optional wrapping a Void. That is what's happening to you; a Void is expected but your one-line anonymous function returns an Optional wrapping a Void. By adding another line that returns Void explicitly, you solved the problem.
The implicit return is returning the result of your callback() method. That return value conflicts with the closure's return value of void. You thus need an explicit, if ugly, return.

Resources