I have a few async function which I'd like to run in series to avoid "callback hell" and simplify things I've written the following helper structure:
typealias AsyncCallback = (Bool) -> Void
typealias AsyncFunction = (AsyncCallback) -> Void
public struct AsyncHelpers {
public static func series(_ functions: [AsyncFunction], _ callback: #escaping AsyncCallback) {
var index = 0
var completed: AsyncCallback? = nil
completed = { success in
if success == false { callback(false); return }
index += 1
if index < functions.count {
functions[index](completed!)
return
}
callback(true)
}
functions[index](completed!)
}
}
AsyncHelpers.series([
{ callback in
// do async stuff
callback(true)
}, { callback in
// then do async stuff
callback(true)
}
]) { callback in
// when all completed
}
I can set the #escaping attribute for completion handler, but when I try to apply this attribute to [AsyncFunction] compilator fails with this error: "#escaping attribute only applies to function types". Should I mark these closured as escaping in other way, please?
What is lifecycle of index variable, can I use it inside completed closure without any problem?
Related
I have a method that can be summed up to look like this:
func apply(username: String, email: String, password: String,
onDone: #escaping (_ error: Error?) -> ())
{
//Do async stuff
do
{
try asyncGood()
onDone(nil)
return
}
catch
{
onDone(error)
return
}
}
What's the difference between doing:
return onDone(error)
versus
onDone(error)
return
?
Does it just save an extra line of code? I don't see any difference between the two. Am I missing some fundamental aspect of asynchronous Swift programming?
In your opinion, should I always try to condense everything down such that onDone(...) only gets called once at the end?
Semantically, both cases are the same. You are basically saying:
return ()
Your method is declared to return (), and since the onDone closure also returns a (), you can say return onDone(error). The return types match.
However, I find writing them in 2 separate lines more readable:
// This clearly shows that you are doing 2 separate things
onDone(error) // call the completion handler
return // return
Or even omit the return!
onDone(error)
// there will be an implicit return at the end of the method.
Both are same. apply function return type is Void and onDone closure return type is also Void. So both are same.
return onDone(error)
or
onDone(error)
return
or you can just ignore return because return type is Void
onDone(error)
There is no difference. In fact, there is no need for return keyword at all.
For swift all the following declaration is equivalent:
func doNothing() {
}
func doNothing2() -> Void {
}
func doNothing3() -> () {
}
func doNothing4() {
return ()
}
func doNothing5() -> Void {
return ()
}
When you return () you return nothing. No return is exactly the same as return nothing. Functions returning Void may be equivalently used as following
doNothing()
var result = doNothing()
More, Void can also be used as a type parameter which is a very powerful feature:
func genericCall<T>(_ f: () -> T) -> T {
return f()
}
var result1 = genericCall { print("test") } // result1 is Void
var result2 = genericCall { return 5 } // result2 is Int
Answering your initial question, I would suggest to omit return at all
func doStuffAsync(_ callback: #escaping (Error?) -> Void) {
// Just an example. Could be any other async call.
DispatchQueue.main.async {
do {
try doStuff()
callback(nil)
}
catch {
callback(error)
}
}
}
I am trying to wait for an asynchronous function to finish before processing it's data (e.g. saving it to my database).
I have a function loadFacebookDetails() containing two tasks:
Loading Data from Facebook makeRequest()
Saving the Data to my Database saveAndProceed()
I need makeRequest() -> (asynchronous) to finish before handling the saving.
This is what I got so far:
I declared a typealias FinishedDownload = () -> ()
I created:
func makeRequest(completed: FinishedDownload){
.... // bunch of code
completed() // call that I completed my task at end of function
}
now I don't now how to call makeRequest in my loadFacebookDetails.
I also created this:
makeRequest { () -> () in
saveAndProceed()
}
and my saveAndProceed().
Does anyone now how to make this syntactical correct?
You should have something like :
func makeRequest(completion : ( ( Bool ) -> Void)){
//your stuff goes hre
completion(true)
//or
completion(false)
}
func saveAndProceed() {
//your stuff
}
func loadFacebookDetails() {
makeRequest { (hasSucceed) in
if hasSucceed {
saveAndProceed()
}else{
//handle Error
}
}
}
func makeRequest(url: String ,callback :#escaping (YourObject) -> Void , errorCallBack : #escaping (String) -> Void ){
// if finish or success
callback(objecttoSend);
// or if failed
errorCallBack(message)
}
and call it like this
makeRequest(url: "http://", callback: {(Object)in
// on ur first action
}, errorCallBack: {(error)in
// on ur second action
})
What is the new syntax for dispatch_once in Swift after the changes made in language version 3? The old version was as follows.
var token: dispatch_once_t = 0
func test() {
dispatch_once(&token) {
}
}
These are the changes to libdispatch that were made.
While using lazy initialized globals can make sense for some one time initialization, it doesn't make sense for other types. It makes a lot of sense to use lazy initialized globals for things like singletons, it doesn't make a lot of sense for things like guarding a swizzle setup.
Here is a Swift 3 style implementation of dispatch_once:
public extension DispatchQueue {
private static var _onceTracker = [String]()
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(token: String, block:#noescape(Void)->Void) {
objc_sync_enter(self); defer { objc_sync_exit(self) }
if _onceTracker.contains(token) {
return
}
_onceTracker.append(token)
block()
}
}
Here is an example usage:
DispatchQueue.once(token: "com.vectorform.test") {
print( "Do This Once!" )
}
or using a UUID
private let _onceToken = NSUUID().uuidString
DispatchQueue.once(token: _onceToken) {
print( "Do This Once!" )
}
As we are currently in a time of transition from swift 2 to 3, here is an example swift 2 implementation:
public class Dispatch
{
private static var _onceTokenTracker = [String]()
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(token token: String, #noescape block:dispatch_block_t) {
objc_sync_enter(self); defer { objc_sync_exit(self) }
if _onceTokenTracker.contains(token) {
return
}
_onceTokenTracker.append(token)
block()
}
}
From the doc:
Dispatch
The free function dispatch_once is no longer available in
Swift. In Swift, you can use lazily initialized globals or static
properties and get the same thread-safety and called-once guarantees
as dispatch_once provided. Example:
let myGlobal: () = { … global contains initialization in a call to a closure … }()
_ = myGlobal // using myGlobal will invoke the initialization code only the first time it is used.
Expanding on Tod Cunningham's answer above, I've added another method which makes the token automatically from file, function, and line.
public extension DispatchQueue {
private static var _onceTracker = [String]()
public class func once(
file: String = #file,
function: String = #function,
line: Int = #line,
block: () -> Void
) {
let token = "\(file):\(function):\(line)"
once(token: token, block: block)
}
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(
token: String,
block: () -> Void
) {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
guard !_onceTracker.contains(token) else { return }
_onceTracker.append(token)
block()
}
}
So it can be simpler to call:
DispatchQueue.once {
setupUI()
}
and you can still specify a token if you wish:
DispatchQueue.once(token: "com.hostname.project") {
setupUI()
}
I suppose you could get a collision if you have the same file in two modules. Too bad there isn't #module
Edit
#Frizlab's answer - this solution is not guaranteed to be thread-safe. An alternative should be used if this is crucial
Simple solution is
lazy var dispatchOnce : Void = { // or anyName I choose
self.title = "Hello Lazy Guy"
return
}()
used like
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
_ = dispatchOnce
}
You can declare a top-level variable function like this:
private var doOnce: ()->() = {
/* do some work only once per instance */
return {}
}()
then call this anywhere:
doOnce()
You can still use it if you add a bridging header:
typedef dispatch_once_t mxcl_dispatch_once_t;
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block);
Then in a .m somewhere:
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block) {
dispatch_once(predicate, block);
}
You should now be able to use mxcl_dispatch_once from Swift.
Mostly you should use what Apple suggest instead, but I had some legitimate uses where I needed to dispatch_once with a single token in two functions and there is not covered by what Apple provide instead.
Swift 3: For those who likes reusable classes (or structures):
public final class /* struct */ DispatchOnce {
private var lock: OSSpinLock = OS_SPINLOCK_INIT
private var isInitialized = false
public /* mutating */ func perform(block: (Void) -> Void) {
OSSpinLockLock(&lock)
if !isInitialized {
block()
isInitialized = true
}
OSSpinLockUnlock(&lock)
}
}
Usage:
class MyViewController: UIViewController {
private let /* var */ setUpOnce = DispatchOnce()
override func viewWillAppear() {
super.viewWillAppear()
setUpOnce.perform {
// Do some work here
// ...
}
}
}
Update (28 April 2017): OSSpinLock replaced with os_unfair_lock due deprecation warnings in macOS SDK 10.12.
public final class /* struct */ DispatchOnce {
private var lock = os_unfair_lock()
private var isInitialized = false
public /* mutating */ func perform(block: (Void) -> Void) {
os_unfair_lock_lock(&lock)
if !isInitialized {
block()
isInitialized = true
}
os_unfair_lock_unlock(&lock)
}
}
I improve above answers get result:
import Foundation
extension DispatchQueue {
private static var _onceTracker = [AnyHashable]()
///only excute once in same file&&func&&line
public class func onceInLocation(file: String = #file,
function: String = #function,
line: Int = #line,
block: () -> Void) {
let token = "\(file):\(function):\(line)"
once(token: token, block: block)
}
///only excute once in same Variable
public class func onceInVariable(variable:NSObject, block: () -> Void){
once(token: variable.rawPointer, block: block)
}
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public class func once(token: AnyHashable,block: () -> Void) {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
guard !_onceTracker.contains(token) else { return }
_onceTracker.append(token)
block()
}
}
extension NSObject {
public var rawPointer:UnsafeMutableRawPointer? {
get {
Unmanaged.passUnretained(self).toOpaque()
}
}
}
import UIKit
// dispatch once
class StaticOnceTest {
static let test2 = {
print("Test " + $0 + " \($1)")
}("mediaHSL", 5)
lazy var closure: () = {
test(entryPoint: $0, videos: $1)
}("see all" , 4)
private func test(entryPoint: String, videos: Int) {
print("Test " + entryPoint + " \(videos)")
}
}
print("Test-1")
let a = StaticOnceTest()
a.closure
a.closure
a.closure
a.closure
StaticOnceTest.test2
StaticOnceTest.test2
StaticOnceTest.test2
StaticOnceTest.test2
OUTPUT:
Test-1
Test see all 4
Test mediaHSL 5
You can use a lazy var closure and execute it immediately with (#arguments_if_needed) so that it will call only one time. You can call any instance function inside of the closure [advantage].
You can pass multiple arguments based on need. You can capture those arguments when the class has been initialised and use them.
Another option: You can use a static let closure and it will execute only one time but you cannot call any instance func inside that static let clsoure. [disadvantage]
thanks!
Swift 5
dispatch_once is still available in libswiftFoundation.dylib standard library which is embedded to any swift app so you can access to exported symbols dynamically, get the function's symbol pointer, cast and call:
import Darwin
typealias DispatchOnce = #convention(c) (
_ predicate: UnsafePointer<UInt>?,
_ block: () -> Void
) -> Void
func dispatchOnce(_ predicate: UnsafePointer<UInt>?, _ block: () -> Void) {
let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
if let sym = dlsym(RTLD_DEFAULT, "dispatch_once") {
let f = unsafeBitCast(sym, to: DispatchOnce.self)
f(predicate, block)
}
else {
fatalError("Symbol not found")
}
}
Example:
var token: UInt = 0
for i in 0...10 {
print("iteration: \(i)")
dispatchOnce(&token) {
print("This is printed only on the first call")
}
}
Outputs:
iteration: 0
This is printed only on the first call
iteration: 1
iteration: 2
iteration: 3
iteration: 4
iteration: 5
iteration: 6
iteration: 7
iteration: 8
iteration: 9
iteration: 10
Use the class constant approach if you are using Swift 1.2 or above and the nested struct approach if you need to support earlier versions.
An exploration of the Singleton pattern in Swift. All approaches below support lazy initialization and thread safety.
dispatch_once approach is not worked in Swift 3.0
Approach A: Class constant
class SingletonA {
static let sharedInstance = SingletonA()
init() {
println("AAA");
}
}
Approach B: Nested struct
class SingletonB {
class var sharedInstance: SingletonB {
struct Static {
static let instance: SingletonB = SingletonB()
}
return Static.instance
}
}
Approach C: dispatch_once
class SingletonC {
class var sharedInstance: SingletonC {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: SingletonC? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = SingletonC()
}
return Static.instance!
}
}
I have two help methods that I want to move to an extension to increase it's reusability. The two methods manages multiple callbacks when calling parallell http request. However when making the methods static and moving them to an extension I get this error:
Cannot convert value of type '[Action]' to expected argument type '[_]'
The code is
extension Array
{
private static func iterateObjectList<Type>(objectList:[Type], multiplier:Int=1, foreach:(object:Type, (newObject:Type?, error:NSError?) -> Void) -> (), finally: (objectList:[Type], errorList:[NSError]) -> Void)
{
var iterationsLeft = objectList.count * multiplier
var errorList:[NSError] = []
var returnObjectList:[Type] = []
if (iterationsLeft == 0) {
finally (objectList:objectList, errorList:[])
}
for object:Type in objectList {
foreach (object:object, { (requestObject, requestError) -> Void in
iterationsLeft -= 1
if (requestError != nil) {
errorList.append(requestError!);
}
if (requestObject != nil) {
returnObjectList.append(requestObject!)
}
if (iterationsLeft <= 0) {
finally (objectList:returnObjectList, errorList:errorList)
}
})
}
}
private static func simpleIterate<Type>(objectList:[Type], multiplier:Int=1, foreach:(object:Type, Void -> Void) -> (), finally: Void -> Void)
{
var iterationsLeft = objectList.count * multiplier
if (iterationsLeft == 0) {
finally ()
}
for object:Type in objectList {
foreach (object:object, { Void -> Void in
iterationsLeft -= 1
if (iterationsLeft <= 0) {
finally ()
}
})
}
}
}
The error is when using the methods:
Array.iterateObjectList(actions, foreach: { (action, iterationComplete) -> () in
self.fetchStatusAndUpdateAction(action, callback: { (error) -> Void in
iterationComplete(newObject: action, error:error)
})
}, finally: { (objectList, errorList) -> Void in
callback(error: errorList.first)
})
where actions is of type [Action] where Action is a custom object.
The problem is that
public struct Array<Element>
is a generic type, and in
Array.iterateObjectList(actions, foreach: { (action, iterationComplete) -> () in
// ...
}, finally: { (objectList, errorList) -> Void in
// ...
})
the compiler cannot infer what Element should be. You could
make it compile as
Array<Action>.iterateObjectList(actions, foreach: { (action, iterationComplete) -> () in
// ...
}, finally: { (objectList, errorList) -> Void in
// ...
})
or even
Array<Int>.iterateObjectList(...)
The array Element is unrelated to your generic placeholder Type,
so any type will do.
But the better solution would be to make the static method an
instance method:
func iterateObjectList(multiplier:Int=1, foreach:(object:Element, (newObject:Element?, error:NSError?) -> Void) -> (), finally: (objectList:[Element], errorList:[NSError]) -> Void)
{
// Your code with `Type` replaced by `Element`,
// and `objectList` replaced by `self`.
// ...
}
and call it on the actions array:
actions.iterateObjectList(foreach: { (action, iterationComplete) -> () in
// ...
}, finally: { (objectList, errorList) -> Void in
// ...
})
I would like to do the following in swift code:
I have to call my api in order to update several items. So I call the api for each item asynchronously. Each api call executes a callback function when it's done. These callbacks decrease a counter, so that when the counter reaches 0 I know that all my api calls are completed. When the counter reaches 0 I would like to call a final callback function (once, when all calls are complete), in order to update my UI and so forth. This final callback is passes to my service in the beginning and stored in a class property for later execution.
Executable Playground source:
// Playground - noun: a place where people can play
class MyService
{
let api = MyApi()
var storedFinalCallback: () -> Void = { arg in }
var queue: Int = 0
func update(items: [String], finalCallback: () -> Void )
{
// Count the necessary API calls
queue = items.count
// Store callback for later execution
storedFinalCallback = finalCallback
for item in items {
// Call api per item and pass queueCounter as async callback
api.updateCall(item, callback: self.callback())
}
}
func callback()
{
queue--
// Execute final callback when queue is empty
if queue == 0 {
println("Executing final callback")
storedFinalCallback()
}
}
}
class MyApi
{
func updateCall(item: String, callback: ())
{
println("Updating \(item)")
}
}
let myItems: [String] = ["Item1", "Item2", "Item3"]
let myInstance: MyService = MyService()
myInstance.update(myItems, finalCallback: {() -> Void in
println("Done")
})
The problem is that with this code the final callback is called in the wrong order.
Apparently the callback function is already executed and not properly passed. However, this was the only way I was able to do it, without compiler errors.
Any help would be really appreciated - I have been stuck on this for two days now.
I finally found the working code:
// Playground - noun: a place where people can play
class MyService
{
let api = MyApi()
var storedFinalCallback: () -> Void = { arg in }
var queue: Int = 0
func update(items: [String], finalCallback: () -> Void )
{
// Count the necessary API calls
queue = items.count
// Store callback for later execution
storedFinalCallback = finalCallback
for item in items {
// Call api per item and pass queueCounter as async callback
api.updateCall(item, callback: self.callback)
}
}
func callback()
{
queue--
// Execute final callback when queue is empty
if queue == 0 {
println("Executing final callback")
storedFinalCallback()
}
}
}
class MyApi
{
func updateCall(item: String, callback: () -> Void)
{
println("Updating \(item)")
callback()
}
}
let myItems: [String] = ["Item1", "Item2", "Item3"]
let myInstance: MyService = MyService()
myInstance.update(myItems, finalCallback: {() -> Void in
println("Done")
})