How to skip completion handler in Swift - ios

I have one function which return Int value in completion handler, however sometimes, I want to skip completion handler while calling from other class and just have Int value. Below is my code. Here totalEvents is with completion handler.
Like I need to call below method
let initialDBCount = self.totalEvents()
func totalEvents(completion: #escaping (_ eventsCount: Int? ) -> Void ) {
self.fetchEvents(forPredicate: nil, withSort: nil, andLimit: nil, completion: { (events) -> Void in
guard let fetchEvents = events else {
return
}
if fetchEvents.count > 0 {
completion(fetchEvents.count)
}
})
}

Make the completion handler as optional and set nil as its default value, i.e.
func totalEvents(completion: ((_ eventsCount: Int?)->())? = nil)
Usage:
totalEvents can be called in both the ways,
1. Without completion handler
totalEvents()
2. With completion handler
totalEvents { (value) in
print(value)
}

Make optional the completion (_eventsCount: Int?) -> () )? = nil or call the completion like
if fetchEvents.count > 0 {
completion(fetchEvents.count)
}

Related

Completion Handler Call N times

In the Button action, I call getLatLongValues method, It will return completion property after API success.
Hear problem is, If I click N th times in button action, getLatLongValues method execute N times.
Like I click the button in 1st time getLatLongValues execute 1 time, I'm click 2nd time not two times getLatLongValues method execute 2 times.
#IBAction func updateDeliveryAddress() {
guard let address = self.addressTextField.text else { return }
self.getLatLongValues(address, true, viewModel) { success in
if success {
//Success
} else {
// Error
}
}
}
func getLatLongValues(address: String, setAsDefault: Bool, viewModel:ViewModel, completion: #escaping (_ success: Bool) -> Void) {
viewModel.location.subscribe(onNext: { [weak self] results in
guard self != nil else { return }
if let result = results {
completion(true) // Success
}
}).disposed(by: disposeBag)
viewModel.fetchLocation(address: address)
}
Why getLatLongValues Execute N times?
because each time you are creating a new subscription.
a subscription is not a completion handler that gets executed once.
viewModel.location.subscribe(onNext: { [weak self] results in
should only be called once, and on each location update, you get the result in the block
If you want the completion handler to only be called once, you should set a flag:
var completionHandlerExecuted = false /// false at first
func getLatLongValues(address: String, setAsDefault: Bool, viewModel:ViewModel, completion: #escaping (_ success: Bool) -> Void) {
if completionHandlerExecuted == false {
completionHandlerExecuted = true /// set to true, so it won't be called again
viewModel.location.subscribe(onNext: { [weak self] results in
guard self != nil else { return }
if let result = results {
completion(true) // Success
}
}).disposed(by: disposeBag)
viewModel.fetchLocation(address: address)
}
}
Completion handlers are like any other instruction you put in your functions.
Every time getLatLongValues is called, you are doing a subscribe, which will call the completion handler once it's finished.

Super simple trailing closure syntax in Swift

I am trying to follow the example in the Swift docs for a trailing closure.
This is the function:
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
print("we do something here and then go back")//does not print
}
And I call it here.
print("about to call function")//prints ok
someFunctionThatTakesAClosure(closure: {
print("we did what was in the function and can now do something else")//does not print
})
print("after calling function")//prints ok
The function, however, is not getting called. What is wrong with the above?
Here's the Apple example:
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here }
// Here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure(closure: {
// closure's body goes here })
Here is your example fixed:
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
print("we do something here and then go back")
// don't forget to call the closure
closure()
}
print("about to call function")
// call the function using trailing closure syntax
someFunctionThatTakesAClosure() {
print("we did what was in the function and can now do something else")
}
print("after calling function")
Output:
about to call function
we do something here and then go back
we did what was in the function and can now do something else
after calling function
Docs isn't very clear in explanation you need
print("1")
someFunctionThatTakesAClosure() { // can be also someFunctionThatTakesAClosure { without ()
print("3")
}
func someFunctionThatTakesAClosure(closure: () -> Void) {
print("2")
/// do you job here and line blow will get you back
closure()
}
the trailing closure is meant for completions like when you do a network request and finally return the response like this
func someFunctionThatTakesAClosure(completion: #escaping ([String]) -> Void) {
print("inside the function body")
Api.getData {
completion(arr)
}
}
And to call
print("Before calling the function")
someFunctionThatTakesAClosure { (arr) in
print("Inside the function callback / trailing closure " , arr)
}
print("After calling the function")
what you missed to read

Perform polling request for async task

I have already written an Rx query to perform an async task in a timer. This also handles scenario where i need to discard order request whose response comes later. This is written in C#:
public static IObservable<T> PollingAync<T> (Func<Task<T>> AsyncCall, double TimerDuration)
{
return Observable
.Create<T>(o =>
{
var z = 0L;
return
Observable
.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(TimerDuration))
.SelectMany(nr =>
Observable.FromAsync<T>(AsyncCall),
(nr, obj) => new { nr, obj})
.Do(res => z = Math.Max(z, res.nr))
.Where(res => res.nr >= z)
.Select(res => res.obj)
.Subscribe(o);
});
}
I wish to write the same implementation in swift which handles an async task and also discard order request whose response comes later. I wish to write this in swift3.0
Since i am new to swift please help to let me know how can i achieve the same result in swift without using Rx.
This was an interesting question to answer...
enum Result<T> {
case success(T)
case failure(Error)
}
typealias Cancel = () -> Void
func pollingAsync<T>(asyncCall: #escaping (#escaping (Result<T>) -> Void) -> Cancel, duration: TimeInterval, callback: #escaping (Result<T>) -> Void) -> Cancel {
let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
timer.scheduleRepeating(deadline: .now(), interval: .milliseconds(Int(duration * 1000)), leeway: .milliseconds(10))
var asyncCallCancel: Cancel? = nil
timer.setEventHandler {
asyncCallCancel?()
asyncCallCancel = asyncCall {
callback($0)
}
}
timer.resume()
return {
asyncCallCancel?()
timer.cancel()
}
}
To use the above, you would do something like this:
let cancel = pollingAsync(asyncCall: myAsyncOp, duration: 2.0) {
print($0)
}
If you lose track of the Cancel object that is returned from this function, you won't be able to shut down the timer.
For reference, here is the equivalent code in RxSwift:
func pollingAsync<T>(asyncCall: #escaping () -> Observable<T>, duration: TimeInterval) -> Observable<Event<T>> {
return Observable<Int>.interval(duration, scheduler: MainScheduler.instance)
.flatMapLatest { _ in
asyncCall().materialize().filter { !$0.isCompleted }
}
}

How can I return true/false in this Alamofire function using Swift?

func checkIfFriend()->Bool{
request(.POST, "", parameters:["":""]).responseJSON{_,_,jsonData in
if something{
return true}
else{
return false
}
}
It appears that "return true/false" has to be in the same level than function is and not inside another function (in this case the Alamofire one).
In that case, how can I return bool in checkIfFriend function depending on what the request return?
Your checkIfFriend() function does not run on the same thread as your Alamofire request (Which runs asynchronously). You should use a callback function/ completion handler as shown below:
func checkIfFriend(completion : (Bool, Any?, Error?) -> Void) {
request(.POST, "", parameters:["":""]).responseJSON{_,_,jsonData in
if something{
completion(true, contentFromResponse, nil)
//return true
}else{
completion(false, contentFromResponse, nil)
// return false
}
}
//Then you can call your checkIfFriend Function like shown below and make use
// of the "returned" bool values from the completion Handler
override func viewDidLoad() {
super.viewDidLoad()
var areWeFriends: Bool = Bool()
var responseContent: Any = Any()
checkIfFriend(completion: { (success, content, error) in
areWeFriends = success // are We Friends will equal true or false depending on your response from alamofire.
//You can also use the content of the response any errors if you wish.
})
}

Swift closure: cannot invoke a function with its argument list

I am writing a Swift function using closure. A should-be-compilable code sample is like this,
import Foundation
typealias PKSynchronizeProgressBlock = (Double) -> Void
typealias PKSynchronizeCompletionBlock = (Bool, NSError?) -> Void
class X {
func synchronizeAppDataWithProgress(
progress: PKSynchronizeProgressBlock?,
completion: PKSynchronizeCompletionBlock?) {
dispatch_async(dispatch_get_main_queue(), {
// Do a lot of downloading, and during the process
// {
// If progress is updated
if (progress != nil) {
progress!(Double(0))
}
//
// If something goes wrong
if (completion != nil) {
completion!(false, nil)
}
// }
dispatch_async(dispatch_get_main_queue(), {
if (completion != nil) {
completion!(true, nil)
}
})
})
}
func foo() {
self.synchronizeAppDataWithProgress({ (progress: Double) -> Void in
self.launchProgressBar.progress = progress
}, completion: { (success: Bool, error: NSError?) -> Void in
if success {
self.launchProgressBar.progress = 1.0
}
else {
print("Failed to synchronize app data with error %#", error!)
}
})
}
}
However, this code does not compile. Xcode says that
cannot invoke 'synchronizeAppDataWithProgress' with an argument list
'(progress: (Double) -> Void, completion: (Bool, NSError?) -> Void)'
What should I do? Did I make any stupid mistake in my code?
Update:
Thanks to #Mario Zannone. I fixed the first two mistakes in my code above. That was: (1) I inserted a redundant progress: in the function call. I have removed that. (2) I updated UI in a thread other than main thread.
But the code still does not work if I don't comment out the following single line in the foo(),
self.launchProgressBar.progress = progress
Do you have any clue why?
Xcode can be picky sometimes with the way arguments are listed inside closures. I've found it best to leave the type inferred. Also be sure to use capture lists to avoid strong reference cycles in your closures.
Using the Alamofire dependency, I've rewritten your code above and it compiles.
import Alamofire
typealias ProgressBlock = (Double) -> Void
typealias CompletionBlock = (Bool, ErrorType?) -> Void
class ExampleDataSource {
func fetchData(progress: ProgressBlock?, completion: CompletionBlock?) {
// Here we use the Alamofire Dependency for progress reporting simplicity.
Alamofire.request(.GET, "https://www.myexampledomain.com")
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
// bytesRead, totalBytesRead, and totalBytesExpectedToRead are Int64
// so we must perform unit conversion
let progressPercentage = Double(totalBytesRead) / Double(totalBytesExpectedToRead)
// here we optionally call the ProgressBlock 'progress' and pass it the progressPercentage
progress?(progressPercentage)
}
.response { request, response, data, error in
// here we usually parse the data, but for simplicity we'll
// simply check to see if it exists.
let completionFlag = (data != nil)
// note that NSError? is interchangable with ErrorType?
completion?(completionFlag, error)
}
}
func performTasks() {
// first we'll set up our closures...
let progressBlock: ProgressBlock = { progress in
// here we update the UI or whatever
// the nice thing about the Alamofire dependency is
// that it's thread-safe :]
}
let completionBlock: CompletionBlock = { success, error in
// here we do whatever we need to do when the
// network operation finishes, or handle the
// errors appropriately
}
// then we'll pass them into our fetchData method
fetchData(progressBlock, completion: completionBlock)
}
}

Resources