Skip to main content
mbehan

Detecting When Your App Gets Backgrounded using Combine

The introduction of the Combine Framework provides a new (reactive) way to respond to system events such as your app entering the background.

In this example I have a CALayer subclass running some CABasicAnimations that need to be paused when the app gets backgrounded and resumed when the app becomes active again. In the layer's init we attach a subscriber to the default notification centre's publisher for the particualr events that we're interested in. When attaching the subscriber we provide a closure that will be executed every time a new event arrives.

let bgSubscriber = NotificationCenter.default
   .publisher(for: UIApplication.willResignActiveNotification)
   .sink {
      _ in

      self.pauseAnimation()
   }

let foregroundSubscriber = NotificationCenter.default
   .publisher(for: UIApplication.didBecomeActiveNotification)
   .sink {
      _ in

      self.resumeAnimation()
   }
}

Let's dig in a little to what's going on here because it may not be obvious if you're not familiar with Combine or NotificationCenter.

NotificationCenter predates Combine as a mechanism for broadcasting information (notifications) to interested observers. Every app gets a default notification centre which is used to broadcast system events such as those in the example here. Without Combine you can register to observe notifications and provide a selector to define what function to call when a notification is received.

With the introduction of Combine however, NotificationCenter added the publisher(for:object:) method which returns a Publisher that will emit values when notifications are broadcast.

A Publisher is how Combine represents a sequence of values over time that can be subscribed to. By calling sink(receiveValue:) we create a subscriber that will receive every value from the publisher.

The true power and utility of Combine isn't seen in this example, but even still I don't think it's overkill to use Combine to provide a modern Swifty replacement for old fashioned notification observers and selectors.

A Note on Memory Management

To complete this specific example there's a little more work to do. In my app I create many of these custom CALayer instances and at various points they get removed and new ones created in their place. With the code above as it is we're preventing the layers from being deallocated when they're no longer being used (because we captured self in the closure passed in to the sink call). To fix this problem we can keep a reference to the subscribers


private var appEventSubscribers =  [AnyCancellable]()

init(){
	
   ...
	
   appEventSubscribers = [foregroundSubscriber, bgSubscriber]

}

so that we can cancel them when the layer gets removed from its parent.

override func removeFromSuperlayer() {
        appEventSubscribers.forEach {
            $0.cancel()
        }

        super.removeFromSuperlayer()
    }
}