Swift SceneKit: SCNAudioPlayer completion block (didFinishPlayback) syntax? - ios

I'm not sure why I can't work this out, but In the documentation for the SCNAudioPlayer class, there are 2 blocks used for playback about to start and playback being completed.
https://developer.apple.com/reference/scenekit/scnaudioplayer/1522818-didfinishplayback
I routinely create and use closures but I can't seem to get this one working:
let player = SCNAudioPlayer(source: source)
player.didFinishPlayback({()->void in
//code here
})
Or variants of the same thing makes swift 3 complain.
I was thinking this might be a NSNotification strategy but it seems a complicated procedure for something seemingly simple.

You are trying to call didFinishPlayback like a function. But as your own reference link shows, it is a variable that you must set.
player.didFinishPlayback = { /* ... */ }

Related

Get a callback from AKPlayer at a user specified time

I’m trying to get a callback at a given point in an AKPlayer’s file playback (currently, just before the end). I see the Apple docs on addBoundaryTimeObserver(), which would work, but it doesn’t seem to be accessible from AKPlayer (I guess an AVAudioPlayerNode vs AVPlayer thing). Any suggestions? I see a few callbacks in AVAudioPlayerNode… maybe I could determine the buffer based on the desired time and use dataConsumed?
The goal is to trigger another event just before the file finishes playing (there is a callback on completion, but obviously that's too late).
If anybody has done something similar, or knows of something similar (a gist, etc), that would be great.
There's an AudioKit playground called AKPlaygroundLoop that shows you how to call an arbitrary handler periodically, based on CADisplayLink. In your handler you could check the AKPlayer's currentTime and if it's close to the end (say 1 second before) you could trigger whatever event you want.
This is a rough outline:
var player: AKPlayer!
var loop: AKPlaygroundLoop!
func play() {
// ...
playgroundLoop = AKPlaygroundLoop(frequency: 10.0, handler: myHandler)
}
func myHandler() {
if player.currentTime >= player.duration - 1.0 {
// trigger some event
}
}
See also this answer for advice on how to synchronize events with AudioKit.

Clean up SimplePing code with RunLoop.current.run

I found the SimplePing library from Apple and want to use it in a SwiftUI Project.
To use the library I code online which works fine. The start function is as follows:
public func start(hostName: String) {
let pinger = SimplePing(hostName: "192.168.178.20")
pinger.delegate = self
pinger.start()
var count = 5
repeat {
if (self.canStartPinging) {
pinger.send(with: nil)
count-=1
if count == 0{
self.canStartPinging = false
break
}
}
RunLoop.current.run(mode: RunLoop.Mode.default, before: NSDate.distantFuture)
} while(true)
I don't really understand why I need the RunLoop.current.run(mode: RunLoop.Mode.default, before: NSDate.distantFuture) line. When I remove it the delegates of SimplePing doesn't get called.
How can I simplify this code and use it without blocking the Main thread?
The run(mode:before:) is there to allow the run loop to process events while this repeat-while loop spins. It’s a way to make a blocking loop allow things to occur on the run loop.
You haven’t shared the code that is setting canStartPinging, but I’m guessing that, at the very least, SimplePingDelegate method didStartWithAddress sets it. So, if you’re spinning on the main thread without calling that run(mode:before:), the SimplePing delegate method probably never gets a chance to be called. By adding that run call, at least the delegate method can run.
Your suspicion about this this whole pattern of spinning and calling run(mode:before:) is warranted. It’s horribly inefficient pattern. It should be eliminated.
If this were a standard Swift project, I’d suggest just using the delegate-protocol pattern and you’d be done. Since this is Swift UI, I’d suggest refactoring this to be a Combine Publisher, which you can then integrate into your SwiftUI project.

What does it mean to call an escaping closure after the function returns? [duplicate]

This question already has answers here:
Escaping Closures in Swift
(8 answers)
Closed 5 years ago.
I was reading the apple developer documentation's definition of escaping closures. It says "a closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns"
I am not sure what the last part is supposed to mean, what does it mean by "after the function returns"? Does it mean "after the function returns a value"?
Take for example a call to an API. This call is going to take time, but we are going to want to do something after the call is completed. For example, say we want to refresh a UITableView with the new data that we pull. If we were to do it immediately, the data wouldn't have been received yet:
ApiObject.getObjects(completion: { (error, objects) in })
tableView.reloadData()
If we were to reload the data here, the table view will refresh immediately (before we actually have received the data). By doing it in the completion block, we are saying, run the code when we have completed the function, not when the function actually returns:
ApiObject.getObjects(completion: {(error, objects) in
self.tableView.reloadData()
})
Here we are running it once the objects have been fetched, not once the function itself has reached the end.
Edit
Maybe this will make it easier; I have the following code:
let comeInAnimation = POPBasicAnimation(propertyNamed: kPOPLayoutConstraintConstant)!
comeInAnimation.toValue = 0
comeInAnimation.completionBlock = { (anim, success) -> Void in
self.loginButton.enabled = true
self.signupButton.enabled = true
}
signUpContainingViewLeftConstraint.pop_add(comeInAnimation, forKey: AnimationString.EnterExit.identifier)
This is using the POP animation framework. In this case, I have a login and signup button, but I also have an animation for them to appear. I dont want the buttons to be clicked while they are appearing so i have their enabled set to false originally. Now you can see they get set to enabled in the completionBlock. This means, when the animation is completed, the completion block gets called and I know now is the time to set them to be enabled. Had I done it like so:
let comeInAnimation = POPBasicAnimation(propertyNamed: kPOPLayoutConstraintConstant)!
comeInAnimation.toValue = 0
signUpContainingViewLeftConstraint.pop_add(comeInAnimation, forKey: AnimationString.EnterExit.identifier)
self.loginButton.enabled = true
self.signupButton.enabled = true
Even though the enabled properties are set after the animation is called, the properties are actually being set before the animation is complete. Because the program runs line by line, the animation gets added and then immediately the properties are set (this is too early).
Note: This is an issue because these functions run asynchronously. That is, it allows the program to keep running while it is doing its thing. If this line of code blocked (stopped the program until it was compete) then putting the code in a completion block and putting it immediately after would be the same thing. In real life though, we dont want to block because it gives the appearance that the program has frozen.

Differrence between closure and function as argument in swift

I have almost 4 years of experience with Objective C and a newbie in swift. Am trying to understand the concept of swift from the perspective of Objective C. So if I am wrong please guide me through :)
In objective c, we have blocks (chunck of code which can be executed later asynchronously) which made absolutely perfect sense. But in swift now we can pass a function as a parameter to another function, which can be executed later, and then we have closure as well.
As per Apple "functions are special cases of clauses."
As per O'Reilly "when a function is passed around as a value, it carries along its internal references to external variables. That is what makes a function a closure."
So I tried a little bit to understand the same :)
Here is my closure
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a ni
let tempNumber : Int = 5
let numbers = [1,2,3,4,5]
print (numbers.map({ $0 - tempNumber}))
}
The variable tempNumber is declared even before the closure was declared, yet closure has the access to the variable. Now rather then a map, I tried using a custom class passed closure as a parameter and tried executing the same code :) Though now closure is getting executed in differrent scope, it still has the access to tempNumber.
I concluded : closures have an access to the variables and methods which are declared in the same scope as closure it self, though it gets execcuted in differrent scope.
Now rather then passing closure as paramter, tried passing function as a parameter,
class test {
func testFunctionAsParameter(testMethod : (Int) -> Int){
let seconds = 4.0
let delay = seconds * Double(NSEC_PER_SEC) // nanoseconds per seconds
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.callLater(testMethod)
})
}
func callLater(testMethod : (Int) -> Int) -> Int {
return testMethod(100)
}
}
In a differrent class I created an instance of Test and used it as follow
/* in differrent class */
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a ni
let tempAge : Int = 5
func test2(val : Int) -> Int {
return val - tempAge;
}
let testObj = test();
print(testObj.testFunctionAsParameter(test2))
}
Declared a class called test, which has a method called testFunctionAsParameter which in turn calls another method called callLater and finally this method executes the passed function :)
Now all these circus, just to ensure that passed method gets executed in differrent scope :)
When I executed the above code :) I was shocked to see that even though function passed as a parameter finally gets executed in different scope, still has the access to the variables testNumber that was declared at the same scope as the method declaration :)
I concluded : O'Reilly's statement "when a function is passed around as a value, it carries along its internal references to external variables." was bang on :)
Now my doubt is apple says functions are special cases of clauses. I thought special case must be something to do with scope :) But to my surprise code shows that both closure and function have access to variables in external scope !!!!
Other then, syntax differrence how closure is differrent from function passed as argument ??? Now there must be some differrence internally, otherwise Apple wouldn't have spent so much time in designing it :)
If not scope?? then what else is different in closure and function ?? O'Reilly states "when a function is passed around as a value, it carries along its internal references to external variables. That is what makes a function a closure." so what is it trying to point out ? that closure wont carry references to external variables ? Now they can't be wrong either, are they?
I am going mad with two conflicting statements from Apple and O'Reilly :( Please help, am I understanding something wrong ?? Please help me understand the difference.
In swift there really isn't any difference between functions and closures. A closure is an anonymous function (A function with no name.) That's about it, other than the syntax differences you noted.
In Objective-C functions and blocks/closures ARE different.
Based on your post, it looks like you have a pretty full understanding on the topic and you may just be getting lost in the semantics. It basically boils down to:
1. a closure is a closure
2. a function is a closure with a name
Here's a post with more details. But again, it's mostly a discussion of semantics. The "answer" is very simple.
What is the difference between functions and closures?

Can I call a block inside a block, with both blocks being inside a method in iOS?

In my iOS project i call a method that contains 2 blocks say A & B.
Both blocks perform 2 different network calls, A goes first and based on some network response, executes block B. I've done so for tidiness since both do a lot of stuff & I've seen this work in my project but I'm not sure if it is correct to do so. Some illustration:
// somewhere in code
...
[self methodThatContainsTwoBlocks];
...
- (void)methodThatContainsTwoBlocks {
Block B ^(responseFromSingletonOperation) {
// do another network operation
[MyNetworkManagerSingleton performAnotherOperationAsynchronouslyWithCompletion:^{
// call some method when done
[self wereDone];
}
}
[MyNetworkManagerSingleton performAnOperationAsynchronouslyWithCompletion:^{
// do a network operation
// call block B with response
B(responseFromNetworkOperation);
}
}
The phrasing of my question may not be correct since the networking singleton instance takes a completion block, hence my saying, two blocks. I hope the illustration makes my question clearer though as I've only just started iOS programming and still dabbling my way with blocks since senior devs recommend them a lot.
Thanks.

Resources