Store completion handler and call success/error later - ios

I have a protocol/function in my class which is below,
func getMovieList(completionHandler: #escaping (Result<[String], Error>) -> Void) { }
When the above method is called, I want to store the completion handler and call the success/error in the latter part.
I tried creating a typealias like below,
typealias AlbumListCompletionHandler = (((Result<[String], Error>)) -> Void)?
And in my class,
var completionHandlerObj: AlbumListCompletionHandler
func getMovieList(completionHandler: #escaping (Result<[String], Error>) -> Void) {
completionHandlerObj = completionHandler
/...
.../
}
But I wonder how do I call the success/error blocks in completionHandlerObj, kind of struck here. Can anyone help me with this ?

It should work like this
completionHandlerObj(.success(["",""]))
completionHandlerObj(.failure(ErrorObject))

Related

iOS Network Extension does not run after attaching to process

I wrote a simple Network Extension for iOS:
class CFilterDataProvider: NEFilterDataProvider {
override func startFilter(completionHandler: #escaping (Error?) -> Void) {
NSLog("startFilter...")
// Add code to initialize the filter.
completionHandler(nil)
}
override func stopFilter(with reason: NEProviderStopReason, completionHandler: #escaping () -> Void) {
// Add code to clean up filter resources.
NSLog("stopFilter")
completionHandler()
}
override func handleNewFlow(_ flow: NEFilterFlow) -> NEFilterNewFlowVerdict {
// Add code to determine if the flow should be dropped or not, downloading new rules if required.
return .drop()
}
}
After attaching to a process with Xcode, startFilter does not called in any situation.
My logs also indicate that function called successfuly but no action after attaching.
What's the reason for that?

CompletionHandler Behavior on Main vs Background thread

Does the thread from which a method is being used matter in terms of completionHandlers? When running on the Main thread, I have no problems; the (data) in completionHandler is called and thus the function's completionHandler within that.
The following is my code:
static func getInfo(user: User,completion: (([String:String]) -> Void)? = nil){
print(user.apiUid)
var enrolledCourses: [String:String] = [:]
NetworkingManager.staticGeneralRequest(withEndpoint: someURL, oauthToken: user.oauth_token, oauthTokenSecret: user.oauth_token_secret) { (data) in
let decodedData: [String:String] = data.getData()
completion?(decodedData)
}
}
//NOTE: According to the compiler,
//the use of an optional completionHandler means that it's `#escaping` by default.
Is this an issue pretaining to threading, and what are some best practices to ensure that code works well across threads?
Your closure was deallocated before you get the response. Adding #escaping like this: static func getInfo(user: User, completion: #escaping (([String:String]) -> Void)? = nil) to keep it in scope.

How to call a function instead of implementing closure

I have this function
func performNetworkRequest(timeout: Int, completion: (Result<Response, Error>) -> Void) {
// ....
}
I want to be able to call performNetworkRequest without writing the closure, but to pass an function to do the logic's over there.
something like :
func onNetwotkResponse(result: (Result<Response, Error>) -> Void) {
}
performNetworkRequest(timeout: 60, completion: onNetwotkResponse)
How can I achieve this?
Thanks
The type of closure is:
(Result<Response, Error>) -> Void
which means
A function that accepts a Result<Response, Error> and returns Void.
This means that your onNetwotkResponse function should accept Result<Response, Error>, rather than (Result<Response, Error>) -> Void:
func onNetwotkResponse(result: Result<Response, Error>) {
}

Swift Optimise nested async calls

I have this case where I need to make 3 nested async calls to receive the data I want.
So the second call needs data from the first one and the third one needs data from the second one. I do not have a lot of cases like this. Only this one and another one with only two nested call so I was thinking about a pure swift solution without any external libraries but I'm open to everything.
Since I'm using Firebase, is it better to move this logic to CloudFunctions? So to prepare it in the backend?
FirestoreService().fetchCollection(query: query) { (result: Result<[Request], Error>) in
// do stuff
FirestoreService().fetchCollection(query: query) { (result: Result<[Request], Error>) in
// do stuff
FirestoreService().fetchDocument(documentReference: documentReference) { (result: Result<Package, Error>) in
// finish
}
}
}
}
If you don't to used 3rd party library, then probably you want to consider wrap those operations inside some class, and utilise closure in imperative way.
here is the sample:
class CustomFirestoreHandler {
private var onFetchFirstQueryArrived: ((Result<[Request], Error>) -> ())? = nil
private var onFetchSecondQueryArrived: ((Result<[Request], Error>) -> ())? = nil
private var onFetchDocumentArrived: ((Result<Package, Error>) -> ())? = nil
init() {
onFetchFirstQueryArrived = { [weak self] (result: Result<[Request], Error>) in
self?.executeSecondQuery()
}
onFetchSecondQueryArrived = { [weak self] (result: Result<[Request], Error>) in
self?.executeFetchDocument()
}
}
func executeQuery(completion: #escaping (Result<Package, Error>) -> ()) {
self.onFetchDocumentArrived = completion
FirestoreService().fetchCollection(query: query) { [weak self] (result: Result<[Request], Error>) in
// validate if some error occurred and do early return here, so that we don't need necessarily call second query.
if (result.error == whatever) {
self?.onFetchDocumentArrived?(result)
return
}
self?.onFetchFirstQueryArrived?(result)
}
}
private func executeSecondQuery() {
FirestoreService().fetchCollection(query: query) { [weak self] (result: Result<[Request], Error>) in
// validate if some error occurred and do early return here, so that we don't need necessarily call fetch document.
if (result.error == whatever) {
self?.onFetchDocumentArrived?(result)
return
}
self?.onFetchSecondQueryArrived?(result)
}
}
private func executeFetchDocument() {
FirestoreService().fetchDocument(documentReference: documentReference) { (result: Result<Package, Error>) in
self?.onFetchDocumentArrived?(result)
}
}
}
And here's the usage of CustomFirestoreHandler above :
let firestoreHandler = CustomFirestoreHandler()
firestoreHandler.executeQuery { (result: Result<Package, Error>) in
// Handle `result` here...
}
I know it look complicated, but this is the only way I think (CMIIW) at the moment to prevent pyramid of dooms since swift doesn't have async await style(just like javascript does).

Swift incorrect closure in function declaration

I am trying to use Moya Networking in my project. I am using this example. The example is making moya request to connect to server in view controller on Line 56 which is below and using callback methods in Line 72 and Line 78
func uploadGiphy() {
provider.request(MultiTarget(Giphy.upload(gif: Giphy.animatedBirdData)),
callbackQueue: DispatchQueue.main,
progress: progressClosure,
completion: progressCompletionClosure)
}
I want to write this function in NetworkHelper.swift instead of my view controller but, use its two callback methods (Line 72 and Line 78) in my view controller.
So I wrote the function in NetworkHelper:
static func getUsers(amount:Int=2,
gender:Gender = .Male,
success successCallback: #escaping ([UserModelMain]) -> Void,
error errorCallback: #escaping (Swift.Error) -> Void,
failure failureCallback: #escaping (Moya.MoyaError) -> Void,
progress progressClosure: #escaping (Moya.ProgressResponse) -> Void,
progressCompletion progressCompletionClosure: #escaping (Moya.Completion) -> Void)
{
provider.request(.getUsers(amount: amount, gender: gender),
callbackQueue: DispatchQueue.main,
progress: progressClosure,
completion: progressCompletionClosure)
}
Its showing error:
Cannot convert value of type '((Result) -> Void)
-> Void' to expected argument type 'Completion' (aka '(Result) -> ()')
I think I am writing my function getUsers wrong. I messed it up. I am weak at closures.
Kindly help.
Source Code of request function from Moya networking library:
/// Designated request-making method.
Returns a `Cancellable` token to cancel the request later.
#discardableResult
open func request(_ target: Target,
callbackQueue: DispatchQueue? = .none,
progress: ProgressBlock? = .none,
completion: #escaping Completion) -> Cancellable {
let callbackQueue = callbackQueue ?? self.callbackQueue
return requestNormal(target, callbackQueue: callbackQueue, progress: progress, completion: completion)
}
Moya.Completion is already a completion block. You just need to pass Moya.Completion as an argument, instead of (Moya.Completion) -> Void.
progressCompletion progressCompletionClosure: #escaping Moya.Completion)
Your current code, like the error suggest, is sending ((Result) -> Void) -> Void

Resources