• Stars
    star
    100
  • Rank 329,721 (Top 7 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 3 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A library that bridges the Composable Architecture and Core Location.

Composable Core Location

CI

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

Example

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 simultaneously subscribe to delegate actions 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.

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

  switch action {
  case .onAppear:
    return .merge(
      environment.locationManager
        .delegate()
        .map(AppAction.locationManager),

      environment.locationManager
        .requestWhenInUseAuthorization()
        .fireAndForget()
    )

  ...
  }
}

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)),
     .locationManager(.didChangeAuthorization(.authorizedWhenInUse)):

  return environment.locationManager
    .requestLocation()
    .fireAndForget()

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)),
     .locationManager(.didChangeAuthorization(.restricted)):

  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 a .failing version of the LocationManager. Then, you can selectively 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.

let store = TestStore(
  initialState: AppState(),
  reducer: appReducer,
  environment: AppEnvironment(
    locationManager: .failing
  )
)

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

store.environment.locationManager.create = { locationManagerSubject.eraseToEffect() }
store.environment.locationManager.requestWhenInUseAuthorization = {
  .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:

store.send(.onAppear)

// Simulate the user denying location access
locationManagerSubject.send(.didChangeAuthorization(.denied))

// We receive the authorization change delegate action from the effect
store.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.
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.

Installation

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

Documentation

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

Help

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.

License

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

More Repositories

1

swift-composable-architecture

A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
Swift
10,990
star
2

swift-snapshot-testing

๐Ÿ“ธ Delightful Swift snapshot testing.
Swift
3,559
star
3

isowords

Open source game built in SwiftUI and the Composable Architecture.
Swift
2,515
star
4

swiftui-navigation

Tools for making SwiftUI navigation simpler, more ergonomic and more precise.
Swift
1,654
star
5

swift-dependencies

A dependency management library inspired by SwiftUI's "environment."
Swift
1,322
star
6

swift-tagged

๐Ÿท A wrapper type for safer, expressive code.
Swift
1,289
star
7

swift-overture

๐ŸŽผ A library for function composition.
Swift
1,115
star
8

pointfreeco

๐ŸŽฌ The source for www.pointfree.co, a video series on functional programming and the Swift programming language.
Swift
1,054
star
9

episode-code-samples

๐Ÿ’พ Point-Free episode code.
Swift
922
star
10

swift-case-paths

๐Ÿงฐ Case paths extends the key path hierarchy to enum cases.
Swift
852
star
11

swift-nonempty

๐ŸŽ A compile-time guarantee that a collection contains a value.
Swift
817
star
12

swift-parsing

A library for turning nebulous data into well-structured data, with a focus on composition, performance, generality, and ergonomics.
Swift
802
star
13

swift-custom-dump

A collection of tools for debugging, diffing, and testing your application's data structures.
Swift
766
star
14

swift-html

๐Ÿ—บ A Swift DSL for type-safe, extensible, and transformable HTML documents.
Swift
726
star
15

combine-schedulers

โฐ A few schedulers that make working with Combine more testable and more versatile.
Swift
671
star
16

swift-web

๐Ÿ•ธ A collection of Swift server-side frameworks for handling HTML, CSS, routing and middleware.
Swift
477
star
17

swift-identified-collections

A library of data structures for working with collections of identifiable elements in an ergonomic, performant way.
Swift
476
star
18

swift-prelude

๐ŸŽถ A collection of types and functions that enhance the Swift language.
Swift
456
star
19

swift-perception

Observable tools, backported.
Swift
399
star
20

swift-validated

๐Ÿ›‚ A result type that accumulates multiple errors.
Swift
389
star
21

swift-url-routing

A bidirectional router with more type safety and less fuss.
Swift
321
star
22

swift-concurrency-extras

Useful, testable Swift concurrency.
Swift
267
star
23

swift-gen

๐ŸŽฑ Composable, transformable, controllable randomness.
Swift
262
star
24

swift-clocks

โฐ A few clocks that make working with Swift concurrency more testable and more versatile.
Swift
229
star
25

swift-enum-properties

๐Ÿค Struct and enum data access in harmony.
Swift
198
star
26

xctest-dynamic-overlay

Define XCTest assertion helpers directly in your application and library code.
Swift
197
star
27

swift-macro-testing

Magical testing tools for Swift macros.
Swift
192
star
28

syncups

A rebuild of Appleโ€™s โ€œScrumdingerโ€ application using modern, best practices for SwiftUI development.
Swift
153
star
29

vapor-routing

A bidirectional Vapor router with more type safety and less fuss.
Swift
83
star
30

swift-html-vapor

๐Ÿ’ง Vapor plugin for type-safe, transformable HTML views.
Swift
82
star
31

swift-playground-templates

๐Ÿซ A collection of helpful Xcode playground templates.
Makefile
80
star
32

pointfreeco-server

Point-Free server code.
39
star
33

swift-boundaries

๐Ÿฃ Functional core, imperative shell.
Swift
27
star
34

composable-core-motion

A library that bridges the Composable Architecture and Core Motion.
Swift
26
star
35

swift-quickcheck

๐Ÿ An implementation of QuickCheck in Swift.
Swift
24
star
36

swift-algebras

Algebraic laws bundled into concrete data types.
19
star
37

swift-either

For those times you want A or B!
Swift
19
star
38

swift-parser-printer

โ†”๏ธ Parsing and printing
Swift
14
star
39

swift-html-kitura

โ˜๏ธ Kitura plugin for type-safe, transformable HTML views.
Swift
13
star
40

homebrew-swift

Ruby
2
star
41

swift-bugs

2
star
42

Ccmark

Swift
1
star