Swiftpack.co -  pointfreeco/composable-core-location as Swift Package
Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
A library that bridges the Composable Architecture and Core Location.
.package(url: "https://github.com/pointfreeco/composable-core-location.git", from: "0.1.0")

Composable Core Location


Composable Core Location is library that bridges the Composable Architecture and Core Location.


Check out the LocationManager demo to see ComposableCoreLocation in practice.

Basic Usage

To use ComposableCoreLocation in your application, you can add an action to your domain that represents all of the actions the manager can emit via the CLLocationManagerDelegate methods:

import ComposableCoreLocation

enum AppAction {
  case locationManager(LocationManager.Action)

  // Your domain's other actions:

The LocationManager.Action enum holds a case for each delegate method of CLLocationManagerDelegate, such as didUpdateLocations, didEnterRegion, didUpdateHeading, and more.

Next we add a LocationManager, which is a wrapper around CLLocationManager that the library provides, to the application's environment of dependencies:

struct AppEnvironment {
  var locationManager: LocationManager

  // Your domain's other dependencies:

Then, we create a location manager and request authorization from our application's reducer by returning an effect from an action to kick things off. One good choice for such an action is the onAppear of your view. You must also provide a unique identifier to associate with the location manager you create since it is possible to have multiple managers running at once.

let appReducer = Reducer<AppState, AppAction, AppEnvironment> {
  state, action, environment in

  // A unique identifier for our location manager, just in case we want to use
  // more than one in our application.
  struct LocationManagerId: Hashable {}

  switch action {
  case .onAppear:
    return .merge(
        .create(id: LocationManagerId())

        .requestWhenInUseAuthorization(id: LocationManagerId())


With that initial setup we will now get all of CLLocationManagerDelegate's delegate methods delivered to our reducer via actions. To handle a particular delegate action we can destructure it inside the .locationManager case we added to our AppAction. For example, once we get location authorization from the user we could request their current location:

case .locationManager(.didChangeAuthorization(.authorizedAlways)),

  return environment.locationManager
    .requestLocation(id: LocationManagerId())

If the user denies location access we can show an alert telling them that we need access to be able to do anything in the app:

case .locationManager(.didChangeAuthorization(.denied)),

  state.alert = """
    Please give location access so that we can show you some cool stuff.
  return .none

Otherwise, we'll be notified of the user's location by handling the .didUpdateLocations action:

case let .locationManager(.didUpdateLocations(locations)):
  // Do something cool with user's current location.

Once you have handled all the CLLocationManagerDelegate actions you care about, you can ignore the rest:

case .locationManager:
  return .none

And finally, when creating the Store to power your application you will supply the "live" implementation of the LocationManager, which is an instance that holds onto a CLLocationManager on the inside and interacts with it directly:

let store = Store(
  initialState: AppState(),
  reducer: appReducer,
  environment: AppEnvironment(
    locationManager: .live,
    // And your other dependencies...

This is enough to implement a basic application that interacts with Core Location.

The true power of building your application and interfacing with Core Location in this way is the ability to test how your application interacts with Core Location. It starts by creating a TestStore whose environment contains an .unimplemented version of the LocationManager. The .unimplemented function allows you to create a fully controlled version of the location manager that does not interact with CLLocationManager at all. Instead, you override whichever endpoints your feature needs to supply deterministic functionality.

For example, to test the flow of asking for location authorization, being denied, and showing an alert, we need to override the create and requestWhenInUseAuthorization endpoints. The create endpoint needs to return an effect that emits the delegate actions, which we can control via a publish subject. And the requestWhenInUseAuthorization endpoint is a fire-and-forget effect, but we can make assertions that it was called how we expect.

var didRequestInUseAuthorization = false
let locationManagerSubject = PassthroughSubject<LocationManager.Action, Never>()

let store = TestStore(
  initialState: AppState(),
  reducer: appReducer,
  environment: AppEnvironment(
    locationManager: .unimplemented(
      create: { _ in locationManagerSubject.eraseToEffect() },
      requestWhenInUseAuthorization: { _ in
        .fireAndForget { didRequestInUseAuthorization = true }

Then we can write an assertion that simulates a sequence of user steps and location manager delegate actions, and we can assert against how state mutates and how effects are received. For example, we can have the user come to the screen, deny the location authorization request, and then assert that an effect was received which caused the alert to show:


  // Simulate the user denying location access
  .do {

  // We receive the authorization change delegate action from the effect
  .receive(.locationManager(.didChangeAuthorization(.denied))) {
    $0.alert = """
      Please give location access so that we can show you some cool stuff.

  // Store assertions require all effects to be completed, so we complete
  // the subject manually.
  .do {
    locationManagerSubject.send(completion: .finished)

And this is only the tip of the iceberg. We can further test what happens when we are granted authorization by the user and the request for their location returns a specific location that we control, and even what happens when the request for their location fails. It is very easy to write these tests, and we can test deep, subtle properties of our application.


You can add ComposableCoreLocation to an Xcode project by adding it as a package dependency.

  1. From the File menu, select Swift Packages › Add Package Dependency…
  2. Enter "https://github.com/pointfreeco/composable-core-location" into the package repository URL text field


The latest documentation for the Composable Core Location APIs is available here.


If you want to discuss Composable Core Location and the Composable Architecture, or have a question about how to use them to solve a particular problem, ask around on its Swift forum.


This library is released under the MIT license. See LICENSE for details.


Stars: 32
Last commit: 9 weeks ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Release Notes

38 weeks ago

First release since being extracted from https://github.com/pointfreeco/swift-composable-architecture

  • Added: support for headings APIs (thanks @dannyhertz).
  • Added: support for accuracy authorization (thanks @mackoj).

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API