DEV Community

AB Dev Hub
AB Dev Hub

Posted on

iOS Background Modes: A Quick Guide

Hi there! Welcome to AB Dev Hub. Let’s talk about iOS Background Modes—the features that let your app do more while it’s not active. We’ll keep it simple and clear so you can start using them right away.

Image description

What Happens When an App Goes to the Background?

When a user minimizes your app or locks their device, iOS moves your app to the background.

Image description

Without background modes, the app pauses and stops running code. But with iOS 4, Apple introduced ways to keep apps working in the background. There are 11 modes you can use.

Image description

Image description


Audio, AirPlay, and Picture in Picture

This mode lets your app play music, share video with AirPlay, or use Picture in Picture. No extra code needed if you use the built-in controllers.

Just for an example of how to use PiP:

import AVKit

let player = AVPlayer(url: videoURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player

if AVPictureInPictureController.isPictureInPictureSupported() {
    let pipController = AVPictureInPictureController(playerLayer: playerViewController.playerLayer!)
    // Start or stop PiP as needed
}
Enter fullscreen mode Exit fullscreen mode

Location Updates

Want your app to track location even when it’s closed? Use this mode. But first, ask for the user’s permission.

You can request location access with this code:

private func checkAuthorization() {
    switch locationManager.authorizationStatus {
    case .notDetermined:
        locationManager.requestWhenInUseAuthorization()
        locationManager.requestAlwaysAuthorization()
    case .authorizedAlways:
        locationManager.startUpdatingLocation()
    case .authorizedWhenInUse:
        locationManager.requestAlwaysAuthorization()
        locationManager.startUpdatingLocation()
    default:
        break
    }
}

Enter fullscreen mode Exit fullscreen mode

And handle updates like this:

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    if let currentLocation = locations.first {
        print(currentLocation)
        updateRegion(location: currentLocation)
    }
}

Enter fullscreen mode Exit fullscreen mode

Background Fetch and Background Processing


Background Fetch (Before iOS 13)

  • This is the older system from iOS 7 and up.
  • It wakes your app occasionally to fetch data.
  • You enable it by adding fetch to UIBackgroundModes in your Info.plist and implementing performFetchWithCompletionHandler in AppDelegate.
  • The system controls when your app gets time, usually up to 30 seconds.

App Refresh Tasks (iOS 13+)

  • They’re part of the BackgroundTasks framework.
  • Use BGAppRefreshTask for short tasks, similar to Background Fetch.
  • You usually have about 30 seconds to update data.
  • Good for quick checks, like pulling in new messages or notifications.

Processing Tasks (iOS 13+)

  • Also from the BackgroundTasks framework.
  • BGProcessingTask allows more time when the device is idle or charging.
  • It’s handy for heavier tasks, like database cleanup or large downloads.
  • The system can still stop the task if conditions change.

How They Differ

Task Type Framework Time Allowed When It Runs Ideal Use Case
Background Fetch UIKit ~30 seconds System decides Quick data refreshes
BGAppRefreshTask BackgroundTasks ~30 seconds System decides Quick updates (iOS 13+)
BGProcessingTask BackgroundTasks Potentially longer Idle/charging Heavy processing, large files

Registering and Scheduling

  1. In Xcode
    • Select your app Target.
    • Go to Signing & Capabilities.
    • Add Background Modes.
    • Check Background Fetch or Background Processing as needed.
  2. In Info.plist
    • Add BGTaskSchedulerPermittedIdentifiers.
    • List each task identifier (like "com.example.myApp.refresh").
  3. In Your Code

    • Import BackgroundTasks.
    • Register tasks in AppDelegate or SceneDelegate:

      BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.example.myApp.refresh",
        using: nil
      ) { task in
        self.handleAppRefresh(task: task as! BGAppRefreshTask)
      }
      
      
  4. Scheduling

    • Create a BGAppRefreshTaskRequest or BGProcessingTaskRequest.
    • Set earliestBeginDate if you want a delay.
    • Submit it with BGTaskScheduler.shared.submit(request).

Handling Tasks

BGAppRefreshTask

func handleAppRefresh(task: BGAppRefreshTask) {
    scheduleAppRefresh() // reschedule

    let operation = MyFetchOperation()
    task.expirationHandler = {
        operation.cancel()
    }
    operation.completionBlock = {
        task.setTaskCompleted(success: !operation.isCancelled)
    }
    OperationQueue().addOperation(operation)
}

Enter fullscreen mode Exit fullscreen mode
  • You have about 30 seconds.

BGProcessingTask

func handleProcessingTask(task: BGProcessingTask) {
    scheduleProcessingTask() // reschedule

    let operation = MyLongRunningOperation()
    task.expirationHandler = {
        operation.cancel()
    }
    operation.completionBlock = {
        task.setTaskCompleted(success: !operation.isCancelled)
    }
    OperationQueue().addOperation(operation)
}

Enter fullscreen mode Exit fullscreen mode
  • You may have more time, but the task can still end early if conditions change.

Testing

  • Xcode > Debug > Simulate Background Fetch (for the old style).
  • Xcode > Debug > Simulate Background Tasks (for the new BGTaskScheduler).
  • You can also run these commands in the debugger:

    e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.myApp.refresh"]
    
    
```bash
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.myApp.processing"]

```
Enter fullscreen mode Exit fullscreen mode

But note, _simulateLaunchForTaskWithIdentifier: is a private API. It may not always work. Using Xcode’s Debug menu is safer.


Summary

  • Background Fetch is the older approach for quick data updates.
  • BGAppRefreshTask and BGProcessingTask use the new BackgroundTasks framework.
  • Always register your tasks in Info.plist and in code.
  • Test with Xcode’s debug options or the console if needed.

Voice over IP (VoIP)

This mode lets apps handle internet calls in the background. When a VoIP push arrives, your app wakes up to connect the call.


  • This mode is for apps that make or receive calls over the internet.
  • When your app has VoIP mode, it can wake up to handle incoming calls even if it’s in the background.
  • On iOS, you usually receive a VoIP push notification from your server.
  • That notification tells your app to ring the user’s device or start the call.
  • Apple wants VoIP apps to show calls through CallKit, which makes them look like regular phone calls.

Steps to Enable VoIP Mode

  1. Enable Background Modes in Signing & Capabilities:
    • Add Background Modes.
    • Check Voice over IP.
  2. Use Push Notifications:
    • Have your server send a VoIP push (type “voip”) when an incoming call starts.
  3. Use CallKit (Recommended by Apple):
    • When you get the VoIP push, show the system call UI with CallKit.
    • This gives a consistent user experience.

How it works

  • VoIP apps need to respond to calls instantly, even if the app is in the background.
  • A normal push might not wake the app fast enough, but a VoIP push does.

Tips

  • Apple wants you to use VoIP pushes only for calls. Don’t use them to keep your app running in the background for non-call purposes.
  • Make sure you end your call or remove the app from the “active call” state when it’s done, so the system knows it can sleep again.

Summary

  • VoIP mode lets apps answer internet calls in the background.
  • You set it in Background Modes and use VoIP push notifications.
  • CallKit helps display a familiar call screen to the user.
  • Stick to call-related functions, or Apple may reject your app.

External Accessory Communication

If your app connects to certified devices (like Bluetooth speakers or USB gadgets), this mode keeps communication going in the background.


Bluetooth LE Accessories

With this mode, your app can:

  • Receive data from Bluetooth devices.
  • Act as a Bluetooth device and send data.

Here’s how to scan for devices:

let SERVICE_UUID = CBUUID(string: "0000ff0-0000-0000-0000-00405f9b99fb")
centralManager.scanForPeripherals(
    withServices: [SERVICE_UUID],
    options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]
)

Enter fullscreen mode Exit fullscreen mode

Remote Notifications

Silent push notifications wake your app to fetch new data. You have 30 seconds to process them. Just remember:

  • They don’t always arrive. (Apple does not guarantee it at all)
  • They’re limited in Low Power Mode.

Handle them like this:

func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    do {
        let data = try await fetchSomeData()
        completionHandler(data == nil ? .noData : .newData)
    } catch {
        completionHandler(.failed)
    }
}

Enter fullscreen mode Exit fullscreen mode

Push to Talk

In iOS 16, Apple introduced Push to Talk. It lets users send real-time voice messages, like a walkie-talkie. When a push arrives, your app wakes up and plays the audio.

How It Works

  • Your server sends a special push notification.
  • When that push arrives, your app wakes up to play or record voice.
  • It feels instant, like pressing a button on a real walkie-talkie.

When to use It

  • It’s good for quick voice chats where typing is slow.
  • It’s more personal than a text message.

Tips

  • Make sure you handle these pushes carefully, so the app can wake up fast.
  • Keep your audio handling simple—no long recordings.

Summary

  • Push to Talk adds real-time voice messaging.
  • It’s like a walkie-talkie for iOS.
  • When a push arrives, your app activates to play or record.

Nearby Interactions

This mode uses Ultra Wideband (UWB) technology to communicate with nearby devices. It’s great for games and location-based apps. You’ll need pre-paired Bluetooth accessories to use it in the background.

  • It’s useful for location-based games, device tracking, and real-time peer-to-peer apps.
  • In the background, you need pre-paired Bluetooth accessories to keep the session active.

How It Works

  1. Import the NearbyInteraction framework.
  2. Create an NISession.
  3. Set a delegate to respond to updates.
  4. Start the session with a valid NINearbyPeerConfiguration or other configs.

Basic Code Example

import NearbyInteraction
import CoreBluetooth

class NearbyManager: NSObject, NISessionDelegate {
    private var niSession: NISession?

    override init() {
        super.init()
        niSession = NISession()
        niSession?.delegate = self
    }

    func startNearbySession() {
        // Example config for a peer-to-peer session
        guard let tokenData = fetchSharedToken() else { return }
        let config = NINearbyPeerConfiguration(peerToken: tokenData)
        niSession?.run(config)
    }

    func session(_ session: NISession, didUpdate nearbyObjects: [NINearbyObject]) {
        // Handle real-time distance/angle updates
        guard let firstObject = nearbyObjects.first else { return }
        print("Distance: \(firstObject.distance ?? 0), Direction: \(firstObject.direction)")
    }

    func session(_ session: NISession, didInvalidateWith error: Error) {
        // Handle errors or session invalidation
        print("Session invalidated: \(error.localizedDescription)")
    }

    private func fetchSharedToken() -> NIDiscoveryToken? {
        // Typically retrieved from another device or server
        return nil
    }
}

Enter fullscreen mode Exit fullscreen mode

Background Use

  • By default, Nearby Interaction stops when the app goes to the background.
  • If you have a Bluetooth accessory that’s paired, you can keep tracking in the background.
  • You still have to enable any necessary background modes in Xcode (like Uses Bluetooth LE Accessories).

Tips

  • Test with two UWB-capable devices (like recent iPhones).
  • Make sure Bluetooth is on.
  • Show clear prompts so users know when you’re scanning for nearby devices.

Summary

  • Nearby Interactions uses UWB for close-range tracking.
  • You need a paired accessory for background use.
  • Create an NISession, set the delegate, and start with a configuration.

Hey there, developers! 👨‍💻

If you found today’s article helpful, consider giving a little back to help this project thrive. Here’s how you can show your support:

🌟 Follow me on these platforms:

Each follow makes a huge difference—it connects us with more learners like you and fuels our mission to create high-quality, beginner-friendly content.

Buy Me a Coffee

Want to go the extra mile? You can support me through Buy me a coffee. Your generosity directly contributes to creating new tutorials, lessons, and resources to help aspiring developers like you master Swift. Every bit of support is deeply appreciated!


That’s it! These modes make your app more useful, even when it’s not on the screen. Got questions? Let’s chat in the comments. Thanks for reading!

Top comments (0)