Understanding Closures

So let’s think about the time display for our media player. Whenever media is playing, we want to periodically get the current playback time, and show that in the label as minutes and seconds…maybe hours, too, for those podcasts that won’t wrap it up already. (You know who you are!)

In Using a Timer, we learned how to use the Timer for our asynchronous tests, and it seems like that would work here, too. We could create a timer to periodically check on the player, get its current time, and update the label. That’s fine, of course, although maybe a little wasteful if it keeps running when playback is paused, and there’s extra code to write if we have to create a new timer when we start playing and destroy it when we pause.

Thinking about it, though, we didn’t need a timer to change the Play/Pause button: that was based on an event we could observe from the player itself with KVO. So it’s reasonable to think that AVPlayer could offer something appropriate for a playback time display.

If we look in the AVPlayer documentation, we find there’s a discussion called “Timed State Observations,” which says:

KVO works well for general state observations, but isn’t intended for observing continuously changing state like the player’s time. AVPlayer provides two methods to observe time changes:

See? Just what we need! The section goes on to explain there are two methods to add these kinds of time observers—one for continuous observation, and another just for specific times, like reaching the end of the playing item. The first is what we need, so follow the link to the documentation for addPeriodicTimeObserver(forInterval:queue:using:). Now let’s look at the declaration to see how we call it:

 func​ addPeriodicTimeObserver(forInterval interval: ​CMTime​,
  queue: ​DispatchQueue​?,
  using block: ​@escaping​ (​CMTime​) -> ​Void​) -> ​Any

What…the…heck?

OK, let’s step back. This takes three arguments, and the first two are easy enough to understand: a CMTime with external name forInterval, and an optional DispatchQueue (whatever that is!) called queue. And the return type is an Any, so that’s fine.

Obviously, the weird part is that third parameter, with external name using and internal name block. What’s weird is its type: @escaping (CMTime) -> Void.

Set aside the @escaping for a moment, and consider what’s left: (CMTime) -> Void. With the types on both sides of the arrow, that looks like a function or method declaration, right? Parameter types on the left, return type on the right?

That’s pretty much what it is, in fact. This is the syntax for a closure, a self-contained block of functionality. Closures can take arguments, do work, and return a value…just like the functions and methods we’re already used to.

But it’s not that closures are a variation on functions; in fact, it’s the other way around. Swift functions and methods are just special cases of closures! A closure is just some code represented as an object, and thus a function is a closure with a name, and then a method is a function associated with an instance of some type.

But closures are important because, as a Swift type, they can also be passed as parameters, stored in variables, and returned by functions and methods. We can pretty much do the same things with closures as we already do with Ints, Strings, and objects.

And using it as a type is what addPeriodicTimeObserver is offering: we pass in a closure to be executed periodically—say, once every half-second—and the code in that closure gets repeatedly executed on that schedule. We don’t have to use some special method name and parameter list like KVO’s observeValue(forKeyPath:of:change:object:), and as a bonus, AVPlayer only calls this method when the media is playing.

Closures are perfect for our time label, so let’s see how to use them.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset