I have a function written in Swift. I want the completion block to return a boolean. How can I go about doing this? I am using Grand Central Dispatch.
func myFunc() -> Bool
{
var success:Bool = false
// code here
dispatch_async(dispatch_get_main_queue(), {
return success
)}
)}
}
thanks!
Standard why of dealing with this async nature is not to return value, but pass in completion handler:
func myFunc(completion:(success: Bool) -> ()) {
var success:Bool = false
// code here
dispatch_async(dispatch_get_main_queue()) {
completion(success: success)
}
}
Then work with it:
myFunc({ (success) in
// ...
})
More swifty way (Swift 5):
func myFunc(completion: #escaping (Bool) -> Void) {
var success = false
// code...
completion(success)
}
}
Usage:
myFunc { success in
if success {
// code ...
}
}
Related
I found the weird crash when using Set in async queue closure below, confirmed that it only happens in async queue, but Array works.
func testA1() {
var set = Set<Int>()
for i in 0...10 {
DispatchQueue.global().async {
set.update(with: i) // Crash here: EXC_BAD_ACCESS
// set.insert(i)
}
}
print(set as Any)
}
func testA2() {
var set = Set<Int>()
for i in 0...10 {
DispatchQueue.global().sync {
set.update(with: i) // Works!
}
}
print(set as Any)
}
func testB() {
var array = [Int]() // Works!
for i in 0...10 {
DispatchQueue.global().async {
array.append(i)
}
}
print(array as Any)
}
Swift version:
Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
Target: x86_64-apple-darwin20.2.0
My bad or swift bug? Why?
Most of datatypes in Swift aren't thread-safe.
You should interact using different ways.
protocol ThreadSafeExecutor {
var semaphore: DispatchSemaphore { get set }
func wait()
func signal()
}
extension ThreadSafeExecutor {
func wait() {
semaphore.wait()
}
func signal() {
semaphore.signal()
}
}
class YourClass: ThreadSafeExecutor {
func someMethod() {
wait()
defer {
signal()
}
/// thread-safe code
}
}
I've got a function which is called by observing the NotificationCenter:
NotificationCenter.default.addObserver(self, selector: #selector(observedPosition(_: ), name: "calculatePosition", object: nil)
and then the function:
#objc func observedPosition(_ notification: NSNotification) {
if let data = notification.object as? Int {
self.sendPosition(from: data)
}
As this function can be called multiple times in very short time periods I would like to add it to the queue and call sendPosition() only once the previous sendPosition() has finished.
I tried something like this but dunno if it's a correct approach:
#objc func observedPosition(_ notification: NSNotification) {
let queue = DispatchQueue(label: queueLabel, attributes: [], targer: nil)
queue.sync {
if let data = notification.object as? Int {
self.sendPosition(from: data)
}
}
}
Details
Xcode Version 10.3 (10G8), Swift 5
Key features
Implemented own queue which will execute functions one by one
All operations (closures) stored in array
Thread safety
Solution
// MARK: - StackableOperationsQueue performs functions from the stack one by one (serial performing)
class StackableOperationsQueue {
private let semaphore = DispatchSemaphore(value: 1)
private lazy var operations = [QueueOperation]()
private lazy var isExecuting = false
fileprivate func _append(operation: QueueOperation) {
semaphore.wait()
operations.append(operation)
semaphore.signal()
execute()
}
func append(operation: QueueOperation) { _append(operation: operation) }
private func execute() {
semaphore.wait()
guard !operations.isEmpty, !isExecuting else { semaphore.signal(); return }
let operation = operations.removeFirst()
isExecuting = true
semaphore.signal()
operation.perform()
semaphore.wait()
isExecuting = false
semaphore.signal()
execute()
}
}
// MARK: - StackableOperationsCuncurentQueue performs functions from the stack one by one (serial performing) but in cuncurent queue
class StackableOperationsCuncurentQueue: StackableOperationsQueue {
private var queue: DispatchQueue
init(queue: DispatchQueue) { self.queue = queue }
override func append(operation: QueueOperation) {
queue.async { [weak self] in self?._append(operation: operation) }
}
}
// MARK: QueueOperation interface
protocol QueueOperation: class {
var сlosure: (() -> Void)? { get }
var actualityCheckingClosure: (() -> Bool)? { get }
init (actualityCheckingClosure: (() -> Bool)?, serialClosure: (() -> Void)?)
func perform()
}
extension QueueOperation {
// MARK: - Can queue perform the operation `сlosure: (() -> Void)?` or not
var isActual: Bool {
guard let actualityCheckingClosure = self.actualityCheckingClosure,
self.сlosure != nil else { return false }
return actualityCheckingClosure()
}
func perform() { if isActual { сlosure?() } }
init (actualIifNotNill object: AnyObject?, serialClosure: (() -> Void)?) {
self.init(actualityCheckingClosure: { return object != nil }, serialClosure: serialClosure)
}
}
class SerialQueueOperation: QueueOperation {
let сlosure: (() -> Void)?
let actualityCheckingClosure: (() -> Bool)?
required init (actualityCheckingClosure: (() -> Bool)?, serialClosure: (() -> Void)?) {
self.actualityCheckingClosure = actualityCheckingClosure
self.сlosure = serialClosure
}
}
Usage example
class TEST {
private lazy var stackableOperationsQueue: StackableOperationsCuncurentQueue = {
let queue = DispatchQueue(label: "custom_queue", qos: .background,
attributes: [.concurrent], autoreleaseFrequency: .workItem, target: nil)
return StackableOperationsCuncurentQueue(queue: queue)
}()
private func addOperationToQueue(closure: (() -> Void)?) {
let operation = SerialQueueOperation(actualIifNotNill: self) { closure?() }
stackableOperationsQueue.append(operation: operation)
print("!!!! Function added ")
}
private func simpleFunc(index: Int) {
print("Func \(index) started")
sleep(UInt32(index+1));
print("Func \(index) ended")
}
func run() {
(0...3).forEach { index in
addOperationToQueue { [weak self] in self?.simpleFunc(index: index) }
}
}
}
let test = TEST()
test.run()
Usage example results
// qos: .background
!!!! Function added
!!!! Function added
!!!! Function added
!!!! Function added
Func 0 started
Func 0 ended
Func 1 started
Func 1 ended
Func 2 started
Func 2 ended
Func 3 started
Func 3 ended
// qos: .userInitiated
!!!! Function added
Func 0 started
!!!! Function added
!!!! Function added
!!!! Function added
Func 0 ended
Func 1 started
Func 1 ended
Func 2 started
Func 2 ended
Func 3 started
Func 3 ended
That is correct, so long as you ensure the same queue is being used to schedule all sendPosition method calls. For example, if this queue were a local variable, it would be of no use at all.
In my code I do not want to use protocol I want use closures but I couldn't get it done because I am new on Swift.
Here is the example of class
class SplashPresenterImp: SplashPresenter, OnFinishedListener {
private var interactor: SplashInteractor
private var splashNetworkProtocol: SplashNetworkProtocol
init() {
interactor = SplashNetworking()
}
func startDownloadConfigs(splashNetworkProtocol: SplashNetworkProtocol){
if interactor != nil {
interactor.loadConfigs(listener: self)
self.splashNetworkProtocol = splashNetworkProtocol
}
}
func startDownloadDictionary(splashNetworkProtocol: SplashNetworkProtocol) {
if interactor != nil {
interactor.loadDictionary(listener: self)
self.splashNetworkProtocol = splashNetworkProtocol
}
}
func onFinishedGetDictionary(dictionary: Dictionary) {
//save dictionary
if splashNetworkProtocol != nil {
splashNetworkProtocol.onSuccess()
}
}
func onFinishedGetConfigs(config: Config) {
//save configs
if splashNetworkProtocol != nil {
splashNetworkProtocol.onSuccess()
}
}
func onFinishedWithError(error: AMError) {
if splashNetworkProtocol != nil {
splashNetworkProtocol.onError(error: error)()
}
}
}
Here is the protocol
protocol SplashNetworkProtocol: class {
func onSuccess()
func onError(error: AMError)
}
What I want to have on my viewcontroller to have closure when downloadConfig is complete to start downloadDictionary.
I know how it handle on Java here is the code
mPresenter.startDownloadConfigs(new SplashNetworkProtocol() {
#Override
public void onSuccess() {
downloadDictionary();
}
#Override
public void onError(final AMError error) {
}
});
I want to have same result in swift. Is anyone can give me advice how to do this?
More clearly I want get rid of SplashNetworkProtocol and use only closure.
swift result should be this
mPresenter.startDownloadConfigs(onSuccess: {} onError{}
Should be as simple as:
func startDownloadDictionary(onSuccess: () -> Void, onError: () -> Void)
But even better to use a single closure that handles both success and error. For instance, with an error as an optional parameter:
func startDownloadDictionary(onCompletion: (Error?) -> Void)
A full example:
func someOtherFunc() {
startDownloadDictionary(onCompletion: {(error) -> Void in
if let error = error {
print(error.localizedDescription)
}
//no error
})
}
func startDownloadDictionary(onCompletion: (Error?) -> Void)
{
//dostuff
var error: Error?
// if error happens, create it
onCompletion(error)
}
If you need help with Swift closure syntax, this is a good resource:
http://fuckingswiftblocksyntax.com/
I have a function which relies on the completionHandler of another function. This completionHandler should be called when the delegate method completedLogin is called. Below is a snippet of my code:
class loginClass : LoginScreenDelegate {
var loginCompleted : Bool = false
func performLogin(completionHandler: (() -> Void)) {
...
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
while self.loginCompleted != true {
// Do nothing
}
completionHandler()
})
}
func didLogin(sender: LogInScreen, success: Bool) {
// Do nothing
}
func completedLogin(sender: LogInScreen) {
self.loginCompleted = true
}
}
However, using a while loop inside a background thread seems like a very resource intensive way. I have tried using NSTimer() but the problem is is that it executes another function so i cannot use my callback function anymore. Is there a better / resource friendly way to keep checking this?
You have to add a completion handler to the function which needs to be completed before the other like this:
func completedLogin(sender: LogInScreen, completionHandler :(evaluatedSuccessfully: Bool) -> ()){
self.loginCompleted = true
completionHandler(evaluatedSuccessfully: true)
}
And then you can just call this function in any other function like this:
completedLogin(sender: <your sender>){ success in
If success{
//do something
}
else{
//report an error
}
}
class loginClass : LoginScreenDelegate {
var loginCompleted : Bool = false
var completionHandler: (() -> Void)!
func performLogin(completionHandler: (() -> Void)) {
...
self.completionHandler = completionHandler
}
func didLogin(sender: LogInScreen, success: Bool) {
// Do nothing
}
func completedLogin(sender: LogInScreen) {
self.loginCompleted = true
self.completionHandler()
}
}
I need to write a function in swift with a workflow process like this:
Call a ViewController to get data
Call a web service to perform a calculation
Call another web service to persist data
The three processes are asynchronous.
The user calls this process, with code like this:
Get_Calc_And_Save_Data( self, closure: {(error:NSError?) -> Void in
if error==nil {
print("OPERATION OK")
} else {
print("OPERATION ERROR: \(error)")
}
})
How should I write the processes?
Should I use GCD?
You can use several completion handlers along with Booleans to check whether each of the functions have finished like so:
func singleMethodToCall(completion: () -> ()) {
var methodOneFinished = false
var methodTwoFinished = false
var methodThreeFinished = false
asyncMethodOne { () -> () in
methodOneFinished = true
if methodOneFinished && methodTwoFinished && methodThreeFinished {
completion()
}
}
asyncMethodTwo { () -> () in
methodTwoFinished = true
if methodOneFinished && methodTwoFinished && methodThreeFinished {
completion()
}
}
asyncMethodThree { () -> () in
methodThreeFinished = true
if methodOneFinished && methodTwoFinished && methodThreeFinished {
completion()
}
}
}
func asyncMethodOne(completion: () -> ()) {
//Do Stuff
completion()
}
func asyncMethodTwo(completion: () -> ()) {
//Do Stuff
completion()
}
func asyncMethodThree(completion: () -> ()) {
//Do Stuff
completion()
}
singleMethodToCall { () -> () in
print("All three methods have finishsed")
}