Skip to content

Miiha/composable-feedback-generator

Repository files navigation

Composable Feedback Generator

Composable Feedback Generator is a library that bridges the Composable Architecture and UIKit's UIFeedbackGenerator and it's subclasses.

Example

Check out the FeedbackGenerator demo to see FeedbackGenerator in practice.

Basic Usage

The FeedbackGenerator wraps the three subclasses of UIFeedbackGenerator:

To use FeedbackGenerator in your application, add the FeedbackGenerator to your Environment.

struct AppEnvironment {
  var feedbackGenerator: FeedbackGenerator

  // Your domain's other dependencies:
  ...
}

If you only need one FeedbackGenerator, you can add the specific generator to your Environment.

struct AppEnvironment {
  var impactFeedbackGenerator: FeedbackGenerator.Impact
  ...
}

To create a FeedbackGenerator use the create-methode which returns a long running Effect.

let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, environment in
  struct ImpactId: Hashable {}

  switch action {
  case .onAppear:
    return environment.feedbackGenerator.impact.create(ImpactId(), .medium)).map(absurd)
    ...

It's recommended to run UIFeedbackGenerator.prepare before calling to the actual feedback call. The generator will be placed into a prepared state for a short period of time. This will ensure that while the generator is prepared, you can trigger feedback with lower latency.

switch action {
case .someAction:
  return environment.feedbackGenerator.impact.prepare(ImpactId()).map(absurd)
...

To run the feedback use the feedback generators run method.

switch action {
case .buttonTapped:
return environment.feedbackGenerator.impact.impactOccured(ImpactId()).map(absurd)
...

To supply the "live" implementation to your Environment you can use the provided FeedbackGenerator.live property.

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

For testing you can use the provided unimplemented methods on FeedbackGenerator and replace the required implementations. The power of this approach is that your are able to fully test you feedback related logic.

var impactStyle: UIImpactFeedbackGenerator.FeedbackStyle?
var createdImpactGenerator = false
var impactOccurred = false

environment.feedbackGenerator.impact.create = { _, style in
  impactStyle = style
  createdImpactGenerator = true
  return self.impactSubject.eraseToEffect()
}
environment.feedbackGenerator.impact.impactOccurred = { _ in
  impactOccurred = true
  return .fireAndForget {}
}

TestStore(
  initialState: AppState(),
  reducer: appReducer,
  environment: environment
).assert(
  .send(.tappedImpactButton),
  .do { XCTAssertEqual(impactStyle, .medium) },
  .do { XCTAssertTrue(createdImpactGenerator) },
  .do { XCTAssertTrue(impactOccurred) },
  ...

For more information around testability have a look at AppStateTests.swift.

Installation

You can add ComposableFeedbackGenerator 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/Miiha/composable-feedback-generator" into the package repository URL text field

License

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

Releases

No releases published

Packages

No packages published

Languages