How would i go about checking if a function was called? I have created a function to see if the level was completed like so:
func levelOneCompleted(){
}
When the level one is beat, i call the function levelOneCompleted().
The scene then goes to another scene. It's at this scene that i want to check if the function was called. I am thinking i can make some kind of "if statement".
if levelOneCompleted is called {
//do this
else{
//do this
}
What would be the best way of going about this?
Set a boolean flag to true inside levelOneCompleted():
var isLevelOneCompleted = false
func levelOneCompleted(){
// do things...
isLevelOneCompleted = true
}
And later...
if isLevelOneCompleted {
//do this
} else {
//do this
}
Swift 3 & Xcode 8.3.2
There is 2 trick to do this, here is the code:
// Async operation
func levelOneCompleted(completion: (_ completed: Bool) -> Void) {
// do your function here
completion(true)
}
// Here is how to use it
// than u can declare this in viewDidLoad or viewDidAppear, everywhere you name it
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// this is async operation
levelOneCompleted { (completed) in
if completed {
print("levelOneCompleted is complete")
// do something if levelOneCompleted is complete
DispatchQueue.main.async {
// Update your UI
}
} else {
print("levelOneCompleted is not completee")
// do something if levelOneCompleted is not complete
DispatchQueue.main.async {
// Update your UI or show an alert
}
}
}
}
// Or u can use this code too, and this is Sync operation
var isLevelTwoCompleted: Bool = false
func levelOneCompleted() {
// do your function here
isLevelTwoCompleted = true
}
// to check it u can put this function everywhere you need it
if isLevelTwoCompleted {
//do something if level two is complete
} else {
//do something if level two is not complete
}
Related
Given some class Foo that calls Bar on a background thread to perform some work, how can Bar set some work to be done on the main thread without causing a deadlock, before the enclosing function returns the value it needs to return?
E.g. how can "asdf" print before "done" prints, before true is returned from func done() -> Bool in the example below?
import Dispatch
class Foo {
/// always called from the main queue
func done() -> Bool {
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
Bar().perform {
DispatchQueue.main.async { print("asdf") }
// "asdf" prints after "done" is printed
group.leave()
}
}
group.wait()
print("done")
return true
}
}
where Bar is simply:
struct Bar {
func perform(_ work: #escaping () -> Void) { work() }
}
I need Bar to be able to set some work that should be executed on the main queue before done() returns, without causing a deadlock (as would happen if we changed the above perform block to use DispatchQueue.main.sync, and (assuming done() is always called on the main thread, which it is).
The only solution I could come up with to print "asdf" before we print "done" is to change the done() function as follows:
func done() -> Bool {
let group1 = DispatchGroup()
var completion: (() -> Void)? = nil
group1.enter()
DispatchQueue.global().async(group: group1) {
Bar().perform {
completion = { print("asdf") }
group1.leave()
}
}
group1.wait()
completion?()
print("done")
return true
}
Here the completion block runs on the main thread before the function returns. However this feels kludgy and hacky... seems like something that GCD should just handle for us. But everything else I've tried will run after the function returns.
Thoughts?
The main queue under GCD is not re-entrant, and you cannot stop done from returning immediately except by blocking the main thread, nor (therefore) can you return a value from done after the first async method.
So, as you've been told in a comment, what you're trying to do is impossible.
You can call back into the main thread from inside the Bar completion handler, by way (for example) of another completion handler; but you cannot "wait" the way you are trying to do.
So for example:
struct Bar {
func perform(_ work: #escaping () -> Void) {
print("work")
work()
}
}
class Foo {
func done(_ f: #escaping (Bool) -> ()) {
DispatchQueue.global().async {
Bar().perform {
DispatchQueue.main.async {
// adjust the order of these two lines as desired
print("asdf")
f(true)
}
}
}
}
}
let f = Foo()
f.done { what in
print(what)
print("done")
}
Output:
work
asdf
true
done
Maybe that might work for you:
struct Bar {
func perform(work: #escaping () -> Void, completion: #escaping () -> Void) {
onBackGround(work) {
DispatchQueue.main.async {
print("asdf")
completion()
}
}
}
func done() {
print("done")
}
}
Call it:
bar = ..
bar.perform(work) {
self.done()
}
i am using ReSwift in my project to get a nice and clean redux architecture.
As i am not interested in the whole state i just subscribe to two substates for my viewcontroller:
extension ViewController: StoreSubscriber {
override func viewWillAppear(_ animated: Bool) {
store.subscribe(self) {
$0.select {
($0.subStateA, $0.subStateB)
}
}
}
override func viewWillDisappear(_ animated: Bool) {
store.unsubscribe(self)
}
func newState(state: (subStateA: SubStateA, subStateB: SubStateB)) {
print("test")
}
}
What happens:
My newState method is called every time any update happens to the store.
For example if i update subStateC it still triggers
func newState(state: (subStateA: SubStateA, subStateB: SubStateB)) {}
Can anybody explain why this happens?
Thanks and Greetings!
You can use skipRepeats after select invocation.
This problem is when you need only update state if substate was changed, using skipRepeats you can put a check if this is true you will skip the update, look this code to try to understand.
Example Code
This is a simple code about a state with navigation state and we want update state when navigation state change.
store.subscribe(self) { (subscriber:Subscription<State>) -> Subscription<RoutingState> in
subscriber.select({ (state:State) -> RoutingState in
return state.routing;
}).skipRepeats({ (oldRoutingState, newRoutingState) -> Bool in
return oldRoutingState.navigationState == newRoutingState.navigationState
})
}
This code will prevent call newState if nothing change in your filter.
I hope was helpful
Just do not subscribe whole ViewController or your ViewModel to states.
You can make subclasses (for example DataProvider) - subscribers for each state you need.
ViewController: UIViewController {
let dataProviderA = DataProviderA()
let dataProviderB = DataProviderB()
func viewDidLoad() {
super.viewDidLoad()
store.subscribe(self.dataProviderA) { $0.select { state in state.subStateA }. skipRepeats() }
store.subscribe(self.dataProviderB) { $0.select { state in state.subStateB }. skipRepeats() }
}
}
and your dataProviders must be StoreSubscribers of course:
class DataProviderA: StoreSubsciber<SubStateA> {
func newState(state: SubStateA) {
handle(state)
}
}
class DataProviderB: StoreSubsciber<SubStateB> {
func newState(state: SubStateB) {
handle(state)
}
}
I trust this is right approach - each object handle single state. Cheers!
I'm trying to use – performSelectorOnMainThread:withObject:waitUntilDone: for a Cocoa application that I'm developing in Swift. I need the application to wait until the job is done. Anyway, I have the following lines of code.
func recoverData(path:String) -> Void {
let sheetRect:NSRect = NSMakeRect(0,0,400,114)
let progSheet:NSWindow = NSWindow.init(contentRect:sheetRect, styleMask:NSTitledWindowMask,backing:NSBackingStoreType.Buffered,`defer`:true)
let contentView:NSView = NSView.init(frame:sheetRect)
let progInd:NSProgressIndicator = NSProgressIndicator.init(frame:NSMakeRect(190,74,20,20))
progInd.style = NSProgressIndicatorStyle.SpinningStyle
let msgLabel:NSTextField = NSTextField.init(frame:NSMakeRect(20,20,240,46))
msgLabel.stringValue = "Copying selected file..."
msgLabel.bezeled = false
msgLabel.drawsBackground = false
msgLabel.editable = false
msgLabel.selectable = false
contentView.addSubview(msgLabel)
contentView.addSubview(progInd)
progSheet.contentView = contentView
self.window.beginSheet(progSheet) {(NSModalResponse returnCode) -> Void in
progSheet.makeKeyAndOrderFront(self)
progInd.startAnimation(self)
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority,0)) {
//////////////////////////////////////////////////////////////////////////////////////////////////
self.performSelectorOnMainThread(Selector(self.readData(path)),withObject:path,waitUntilDone:true)
//////////////////////////////////////////////////////////////////////////////////////////////////
}
dispatch_async(dispatch_get_main_queue()) {
progInd.indeterminate = true
self.window.endSheet(progSheet)
progSheet.orderOut(self)
}
}
}
func readData(path:String) -> Void {
print("Hello!?")
}
I'm not sure how I pass path to readData. Xcode requires me to set the argument to something other than nil or nothing. In Objective-C, it would be
[self performSelectorOnMainThread:#selector(readData:) withObject:path waitUntilDone:YES];
Anyway, the application never reaches readData. What am I doing wrong?
Thanks for help.
Why not
self.window.beginSheet(progSheet) {(returnCode) -> Void in
dispatch_async(dispatch_get_main_queue()) {
progInd.startAnimation(self)
self.readData(path)
progInd.indeterminate = true
}
}
At some point you have to call self.window.endSheet(progSheet) to dismiss the sheet and call the completion handler.
Edit:
I guess you actually mean something like this
...
self.window.beginSheet(progSheet) {(returnCode) -> Void in
progInd.stopAnimation(self)
progInd.indeterminate = true
}
progInd.startAnimation(self)
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority,0)) {
self.readData(path) {
dispatch_async(dispatch_get_main_queue()) {
self.window.endSheet(progSheet)
}
}
}
}
func readData(path:String, completion: (() -> Void)) {
print("Hello!?")
completion()
}
I have a BehaviorSubject named createObservable in my view model. And my view controller subscribe it.
viewModel!.createObservable.subscribe(onNext: {[unowned self] (obj:PassbookModelType?) -> Void in
if let _ = obj{
self.dismissVC()
}
}, onError: { (error) -> Void in
print(error)
}).addDisposableTo(self.dispose)
I have a function named saveObject() also in the view model. If I click the navigation bar right item it will be emitted. And there is an error will send to createObservable's observer.
func saveObject(){
```````
```````
if condition {
createObservable.on(Event.Next(model))
createObservable.onCompleted()
}else{
createObservable.onError(MyError.someError)
}
}
The problem is that if the error happened the createObservable will be closed, so I won't receive any Next event in the future. I tried to use retry(), but it seems will cause deadlock, view controller can't response any touch event any more. So can some one tell me how to fix this issue? Thanks a lot
viewModel!.createObservable.retry().subscribe(onNext: {[unowned self] (obj:PassbookModelType?) -> Void in
if let _ = obj{
self.dismissVC()
}
}, onError: { (error) -> Void in
print(error)
}).addDisposableTo(self.dispose)
I suggest to make the type of createObservable PublishSubject<Observable<PassbookModelType>>, instead of BehaviorSubject<PassbookModelType?> which, I guess, accidentally flattens two Rx streams conceptually separatable each other: the saveObject process itself (an one-shot process) and starting the saveObject process initiated by user action repeatedly. I've written a short example to demonstrate it.
let createObservable = PublishSubject<Observable<Int>>()
override func viewDidLoad() {
super.viewDidLoad()
createObservable.flatMap {
$0.map { obj in
print("success: \(obj)")
}
.catchError { err in
print("failure: \(err)")
return empty()
}
}.subscribe()
}
// Simulates an asynchronous proccess to succeed.
#IBAction func testSuccess(sender: UIView!) {
let oneShot = PublishSubject<Int>()
createObservable.onNext(oneShot)
callbackAfter3sec { res in
oneShot.onNext(1)
oneShot.onCompleted()
}
}
// Simulates an asynchronous process to fail.
#IBAction func testFailure(sender: UIView!) {
let oneShot = PublishSubject<Int>()
createObservable.onNext(oneShot)
callbackAfter3sec { res in
oneShot.onError(NSError(domain: "Error", code: 1, userInfo: nil))
}
}
func callbackAfter3sec(completion: Int -> ()) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_SEC * 3)), dispatch_get_main_queue()) {
completion(2)
}
}
There is an important merit with that: If the one-shot process would become in the Rx style (for example, like as callbackAfter3sec() -> Observable<Int>) in the future, there were no need to re-write the use-side code like in the viewDidLoad above. There is an only one change to do is to pass an Observable<> object to createObservable.onNext(...).
Sorry for my poor English skill. I hope this makes sense to you.
I want to run a block of code in 10 seconds from an event, but I want to be able to cancel it so that if something happens before those 10 seconds, the code won't run after 10 seconds have gone by.
I've been using this, but it's not cancellable:
static func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure
)
}
How can I accomplish this?
Swift 3 has DispatchWorkItem:
let task = DispatchWorkItem { print("do something") }
// execute task in 2 seconds
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2, execute: task)
// optional: cancel task
task.cancel()
Update for Swift 3.0
Set Perform Selector
perform(#selector(foo), with: nil, afterDelay: 2)
foo method will call after 2 seconds
func foo()
{
//do something
}
To cancel pending method call
NSObject.cancelPreviousPerformRequests(withTarget: self)
Try this (Swift 2.x, see David's answer below for Swift 3):
typealias dispatch_cancelable_closure = (cancel : Bool) -> ()
func delay(time:NSTimeInterval, closure:()->()) -> dispatch_cancelable_closure? {
func dispatch_later(clsr:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(time * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), clsr)
}
var closure:dispatch_block_t? = closure
var cancelableClosure:dispatch_cancelable_closure?
let delayedClosure:dispatch_cancelable_closure = { cancel in
if let clsr = closure {
if (cancel == false) {
dispatch_async(dispatch_get_main_queue(), clsr);
}
}
closure = nil
cancelableClosure = nil
}
cancelableClosure = delayedClosure
dispatch_later {
if let delayedClosure = cancelableClosure {
delayedClosure(cancel: false)
}
}
return cancelableClosure;
}
func cancel_delay(closure:dispatch_cancelable_closure?) {
if closure != nil {
closure!(cancel: true)
}
}
// usage
let retVal = delay(2.0) {
println("Later")
}
delay(1.0) {
cancel_delay(retVal)
}
From Waam's comment here: dispatch_after - GCD in swift?
You need to do this:
class WorkItem {
private var pendingRequestWorkItem: DispatchWorkItem?
func perform(after: TimeInterval, _ block: #escaping VoidBlock) {
// Cancel the current pending item
pendingRequestWorkItem?.cancel()
// Wrap the request in a work item
let requestWorkItem = DispatchWorkItem(block: block)
pendingRequestWorkItem = requestWorkItem
DispatchQueue.main.asyncAfter(deadline: .now() + after, execute:
requestWorkItem)
}
}
// Use
lazy var workItem = WorkItem()
private func onMapIdle() {
workItem.perform(after: 1.0) {
self.handlePOIListingSearch()
}
}
References
Link swiftbysundell
Link git
This should work:
var doIt = true
var timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: Selector("doSomething"), userInfo: nil, repeats: false)
//you have now 10 seconds to change the doIt variable to false, to not run THE CODE
func doSomething()
{
if(doIt)
{
//THE CODE
}
timer.invalidate()
}
I use #sas 's method in some projects, somehow this doesn't work anymore, maybe something changed after Swift 2.1.1. value copy instead of pointer?
the easiest work around method for me is:
var canceled = false
delay(0.25) {
if !canceled {
doSomething()
}
}
For some reason, NSObject.cancelPreviousPerformRequests(withTarget: self) was not working for me. A work around I thought of was coming up with the max amount of loops I'd allow and then using that Int to control if the function even got called.
I then am able to set the currentLoop value from anywhere else in my code and it stops the loop.
//loopMax = 200
var currentLoop = 0
func loop() {
if currentLoop == 200 {
//do nothing.
} else {
//perform loop.
//keep track of current loop count.
self.currentLoop = self.currentLoop + 1
let deadline = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadline) {
//enter custom loop parameters
print("i looped")
self.loop()
}
}
and then elsewhere in your code you can then
func stopLooping() {
currentLoop = 199
//setting it to 199 allows for one last loop to happen. You can adjust based on the amount of loops you want to be able to do before it just stops. For instance you can set currentLoop to 195 and then implement a fade animation while loop is still happening a bit.
}
It's really quite dynamic actually. For instance you can see if currentLoop == 123456789, and it will run infinitely (pretty much) until you set it to that value somewhere else in your code. Or you can set it to a String() or Bool() even, if your needs are not time based like mine were.