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()
}
}
Related
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.
I have the code working in simulator/device and I'm trying to write unit tests for it. However, notify callback is not called in unit tests. Here's is a code for Playgrounds which is also not calling notify callback. I suspect it I may be using the wrong queue, but cannot figure out which one I should use.
import UIKit
class Loader {
func fetch(callback: ((_ result: String)-> Void)) {
callback("SomeString")
}
}
class MyService {
var list: Array<String> = Array()
var loader: Loader = Loader()
var dispatchGroup = DispatchGroup()
func loadList(callback: #escaping (()-> Void)) {
for i in 1...3 {
self.dispatchGroup.enter()
self.loader.fetch(callback: { [weak self] (string) in
self?.list.append(string)
self?.dispatchGroup.leave()
})
}
dispatchGroup.notify(queue: .main) {
callback()
}
}
}
var service = MyService()
service.loadList {
print("Done is not called")
}
UPDATE
Thanks to #paulvs, we need to enable indefinite execution. However, how to enable that for unit tests?
import UIKit
import PlaygroundSupport
class Loader {
func fetch(callback: ((_ result: String)-> Void)) {
callback("SomeString")
}
}
class MyService {
var list: Array<String> = Array()
var loader: Loader = Loader()
var dispatchGroup = DispatchGroup()
func loadList(callback: #escaping (()-> Void)) {
for i in 1...3 {
self.dispatchGroup.enter()
self.loader.fetch(callback: { [weak self] (string) in
self?.list.append(string)
self?.dispatchGroup.leave()
})
}
dispatchGroup.notify(queue: .main) {
callback()
}
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
var service = MyService()
service.loadList {
print("Done is called now!")
}
Thanks for the idea to #paulvs, and to this post, here's the code needed for unit tests:
let service = MyService()
let expect = expectation(description: "longRunningFunction")
service.loadList {
expect.fulfill()
}
self.waitForExpectations(timeout: 0.5) { error in
XCTAssert(service.isLoaded, "Not loaded")
}
I have two functions: func Females_NonChat() and func males_NonChat()
I want to wait for both of them to finish before executing the print statement in viewdidload. Do I need another completion handler to accomplish that?
Those functions used are firebase completion handlers for requesting information from the online database...
override func viewDidLoad() {
super.viewDidLoad()
func Females_NonChat()
func males_NonChat()
print("finished executing both asynchronous functions")
}
func Females_NonChat(){
Anon_Ref.child("Chatting").child("female").observeSingleEventOfType(.Value, withBlock: {(snapshot) in
if let FemInChatting = snapshot.value as? [String : String] {
print("executing")
}
})
}
func males_NonChat(){
Anon_Ref.child("Chatting").child("male").observeSingleEventOfType(.Value, withBlock: {(snapshot) in
print("executing")
})
}
Generally you'd use a dispatch group, enter the group before each asynchronous method, leave the group upon completion of each asynchronous method, and then set up a group notification when all "enter" calls are matched by corresponding "leave" calls:
override func viewDidLoad() {
super.viewDidLoad()
let group = dispatch_group_create()
dispatch_group_enter(group)
Females_NonChat() {
dispatch_group_leave(group)
}
dispatch_group_enter(group)
males_NonChat() {
dispatch_group_leave(group)
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
print("finished executing both asynchronous functions")
}
}
func Females_NonChat(completionHandler: () -> ()) {
Anon_Ref.child("Chatting").child("female").observeSingleEventOfType(.Value) { snapshot in
if let FemInChatting = snapshot.value as? [String : String] {
print("executing")
}
completionHandler()
}
}
func males_NonChat(completionHandler: () -> ()) {
Anon_Ref.child("Chatting").child("male").observeSingleEventOfType(.Value) { snapshot in
print("executing")
completionHandler()
}
}
Here's an example that executes two async methods and prints when both are finished.
Try copying this code into a Swift Playground and running it.
import Foundation
func doTwoThings() {
var thing1Done: Bool = false
var thing2Done: Bool = false
func done() {
if thing1Done && thing2Done {
print("Both things done! at \(getTime())")
}
}
doThing1(completionHandler: {
thing1Done = true
done()
})
doThing2(completionHandler: {
thing2Done = true
done()
})
}
func doThing1(completionHandler: #escaping () -> Void) {
print("Starting Thing 1 at \(getTime())")
Timer.scheduledTimer(withTimeInterval: 3, repeats: false, block: {_ in
print("Done with Thing 1 at \(getTime())")
return completionHandler()
})
}
func doThing2(completionHandler: #escaping () -> Void) {
print("Starting Thing 2 at \(getTime())")
Timer.scheduledTimer(withTimeInterval: 5, repeats: false, block: {_ in
print("Done with Thing 2 at \(getTime())")
return completionHandler()
})
}
func getTime() -> String {
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minute = calendar.component(.minute, from: date)
let second = calendar.component(.second, from: date)
return "\(hour):\(minute):\(second)"
}
doTwoThings()
Output:
Starting Thing 1 at 11:48:51
Starting Thing 2 at 11:48:51
Done with Thing 1 at 11:48:54
Done with Thing 2 at 11:48:56
Both things done! at 11:48:56
I want do one action when one method is finished, I execute one method in other method and I want the second method stop until the first method is finished.
I have that method:
func ejecutarOBJC(){
let txtNombre = self.view.viewWithTag(4) as? UITextField
let textoNombre=txtNombre?.text
let txtContra = self.view.viewWithTag(5) as? UITextField
let textoContra=txtContra?.text
let instanceOfCustomObject: SQLViewController = SQLViewController()
instanceOfCustomObject.nombre = textoNombre;
instanceOfCustomObject.contra = textoContra;
instanceOfCustomObject.obtenerExistenciaUsuario()
}
And also the other method:
func otherMethod(){
ejecutarOBJC()
//I want continue with that method when the execution of the other method finish
}
This is how you would achieve this:
func methodOne() {
//Method one code here
methodTwo()
}
func methodTwo() {
//Method Two code here.
}
As per your comment, here is how to wait when using async code:
func methodOne() {
//Code goes here
methodTwo { () -> () in
//Method two has finished
}
}
func methodTwo(completion: () -> ()) {
//Code goes here
completion()
}
Use closures:
func callCompetionFunction()
{
// Tira mola meseda (do your stuff)
completionFunction("a parameter") { () -> Void in
print("function copleted")
}
}
func completionFunction(param: AnyObject, completion: ()->Void)
{
// Do your stuff
completion()
}
To test it in a view controller
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
callCompetionFunction()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func callCompetionFunction()
{
// Tira mola meseda (do your stuff)
print("do something here")
completionFunction("a parameter") { () -> Void in
print("function copleted")
}
}
func completionFunction(param: AnyObject, completion: ()->Void)
{
// Do your stuff
if param is String
{
print("parameter = \(param)")
}
print("going to execute completion closure")
completion()
}
}
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 ...
}
}