SCNAction.wait not working in swift playgrounds - swift

In my code I have a sequence in which I am telling swift to wait for a sequence to complete before i move on to another function however it seems to skip this and just move on
let earthSequence = SCNAction.sequence([SCNAction.wait(duration: 10),rollInGroup,rollOutGroup,earthNormalRotation])
defaultEarthNode.runAction(earthSequence)
nextFunction()
the result is simply moving on to the next function without waiting 10 seconds

That's because nextFunction() is called immediately after you start the action, it doesn't wait for it to be completed. runAction has a completionHandler you can use to know when the sequence has finished.
defaultEarthNode.runAction(earthSequence, completionHandler: {
nextFunction()
})

Related

Wait for a *specific* (single) XCTestExpectation with a completion handler?

Has anyone managed to wait for a specific, single, XCTestExpectation with completion handler (XCWaitCompletionHandler or equivalent) similar to that of (waitForExpectations:timeout:handler)[https://developer.apple.com/documentation/xctest/xctestcase/1500748-waitforexpectations].
I want to wait for just one specific expectation, but I would like to know if it timed out. The XCWaitCompletionHandler gives me that information. But there are no properties on the expectation itself flagging whether it timed out or not.
(yes this is a rather advanced use case...)
Create an XCTWaiter and use wait(for:timeout:), which returns a XCTWaiter.Result, which you can then assert is/is not .timedOut if you wish, or otherwise act on the result. By using XCTWaiter, you can have an expectation time out without the test automatically failing.
let result = XCTWaiter().wait(for: [expectation], timeout: 5)
if result == .timedOut { ... }

Xcode sprites not waiting for completion

I am working on an Xcode playground in Swift and I would like for a selection of sprites to perform some actions in a set order, one after the other. However they appear to be happening all at once, despite my use of the completion handler.
Any while loops I try (using a boolean to see if the action is completed or not - set to true in the completion handler) result in an infinite loop.
Here is a code I've tried:
centreOfRotation.run(SKAction.rotate(byAngle: CGFloat(degreesToRadians(angleInDegrees)), duration: 5), completion: {
finished = true
print("finished")
})
But this code doesn't wait for completion before going to the next line.
Does anyone know how to stop this?
Thanks.

Background Process as NSOperation or Thread to monitor and update File

I want to check if a pdf file is changed or not, and if is changed i want to update the corresponding view. I don't know if it's more suitable to use a background process as a Thread or as an NSOperation to do this task. The Apple Documentation says: "Examples of tasks that lend themselves well to NSOperation include network requests, image resizing, text processing, or any other repeatable, structured, long-running task that produces associated state or data.But simply wrapping computation into an object doesn’t do much without a little oversight".
Also, if I understood correctly from the documentation, a Thread once started can't be stopped during his execution while an NSOperation could be paused or stopped and also they could rely on dependency to wait the completion of another task.
The workflow of this task should be more or less this diagram:
Task workflow
I managed to get the handler working after the notification of type .write has been sent. If i monitor for example a *.txt file everything works as expected and i receive only one notification. But i am monitoring a pdf file which is generated from terminal by pdflatex and thus i receive with '.write' nearly 15 notification. If i change to '.attrib' i get 3 notification. I need the handler to be called only once, not 15 or 3 times. Do you have any idea how can i do it or is not possible with a Dispatch Source? Maybe there is a way to execute a dispatchWorkItem only once?
I have tried to implement it like this(This is inside a FileMonitor class):
func startMonitoring()
{
....
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: fileStringURL)
let fileDescriptor = open(fileSystemRepresentation, O_EVTONLY)
let newfileMonitorSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fileDescriptor,
eventMask: .attrib,
queue: queue)
newfileMonitorSource.setEventHandler(handler:
{
self.queue.async
{
print(" \n received first write event, removing handler..." )
self.newfileMonitorSource.setEventHandler(handler: nil)
self.test()
}
})
self.fileMonitorSource = newfileMonitorSource
fileMonitorSource!.resume()
}
func test()
{
fileMonitorSource?.cancel()
print(" restart monitoring ")
startMonitoring()
}
I have tried to reassign the handler in test(), but it's not working(if a regenerate the pdf file, what is inside the new handler it's not executed) and to me, doing in this way, it seems a bit boilerplate code. I have also tried the following things:
suspend the DispatchSource in the setEventHandler of startMonitoring() (passing nil), but then when i am resuming it, i get the remaining .write events.
cancel the DispatchSource object and recall the startMonitoring() as you can see in the code above, but in this way i create and destroy the DispatchSource object everytime i receive an event, which i don't like because the cancel() function shoul be called in my case only when the user decide to disable this feauture i am implementing.
I will try to write better how the workflow of the app should be so you can have an more clear idea of what i am doing:
When the app starts, a functions sets the default value of some checkboxes of the window preference. The user can modify this checkboxes. So when the user open a pdf file, the idea is to launch in a background thread the following task:
I create a new queue call it A and launch asynch an infinite while where i check the value of the UserDefault checkboxe (that i use to reload and update the pdf file) and two things could happen
if the user set the value to off and the pdf document has been loaded there could be two situations:
if there is no current monitoring of the file (when the app starts): continue to check the checkboxe value
if there is currently a monitoring of the file: stop it
if the user set value to on and the pdf document has been loaded in this background thread (the same queue A) i will create a class Monitor (that could be a subclass of NSThread or a class that uses DispatchSourceFileSystemObject like above), then i will call startMonitoring() that will check the date or .write events and when there is a change it will call the handler. Basically this handler should recall the main thread (the main queue) and check if the file can be loaded or is corrupted and if so update the view.
Note: The infinite while loop(that should be running in the background), that check the UserDefault related to the feature i am implementing it's launched when the user open the pdf file.
Because of the problem above (multiple handlers calls), i should use the cancel() function when the user set checkboxe to off, and not create/destroy the DispatchSource object everytime i receive a .write event.

Beginning and End of animation using SKAction.animateWithTextures in Swift

I'm working on a little project in spritekit and can't quite figure something out. I am animating a sprite using SKAction.animateWithTextures and moving through and array. Works fine just like it should. The issue is that I would like to a function that starts when the animation starts and one when it ends. I see there is a .animationDidStart(CAAnimation), but because what I'm doing is not a CAAnimation I can't really use it. Is there something like this for the method I'm using? As you can may or may not be able to tell I'm still rather new to swift. Thanks for any help in advance.
I'd create a sequence of actions. First a block action to call your method you want called when the animation starts, then your animateWithTextures action, finally another block action that calls your finished method.
let startAction = SKAction.runBlock {
self.startAnimation()
}
let textureAction = SKAction.animateWithTextures...
let finishedAction = SKAction.runBlock {
self.finishedAnimation()
}
SKAction.sequence([startAction, textureAction , finishedAction])

dispatch_after time triggers immediately

This is the first time I've used GCD, I'll admit, so sorry if I've been stupid. I have a dispatch_after command which acts as a handy delay for me.
My problem is that when I send
dispatch_after(500000000000, dispatch_get_main_queue()){
println("triggered") //or any other code
}
the closure is triggered immediately (e.g. I have tested this and "triggered" prints immediately). It should take longer right? Like 500 seconds longer.
Thanks :)
The first parameter of dispatch_after(_:_:_:) is not a delay, but a point in time. From the docs:
when: The temporal milestone returned by dispatch_time or dispatch_walltime.
Discussion
This function waits until the specified time and then asynchronously
adds block to the specified queue.
You need to construct a delay relative to the current time, using dispatch_time(_:_:):
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(500 * NSEC_PER_SEC))
dispatch_after(delayTime, dispatch_get_main_queue()) { ... }