Skip to content

Commit 59a2e0b

Browse files
committed
feat: Add HapticEngineClient
1 parent 6d01a36 commit 59a2e0b

8 files changed

+277
-4
lines changed

Diff for: Package.swift

+33
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ let package = Package(
3030
targets: ["DataRepresentable"]
3131
),
3232

33+
// MARK: HapticEngine
34+
.library(
35+
name: "HapticEngineClient",
36+
type: .static,
37+
targets: ["HapticEngineClient"]
38+
),
39+
.library(
40+
name: "HapticEngineClientLive",
41+
type: .static,
42+
targets: ["HapticEngineClientLive"]
43+
),
44+
3345
// MARK: IDFA
3446
.library(
3547
name: "IDFAPermissionsClient",
@@ -107,6 +119,23 @@ let package = Package(
107119
// MARK: DataRepresentable
108120
.target(name: "DataRepresentable"),
109121

122+
// MARK: HapticEngine
123+
.target(
124+
name: "HapticEngineClient",
125+
dependencies: [
126+
.product(
127+
name: "Prelude",
128+
package: "swift-prelude"
129+
)
130+
]
131+
),
132+
.target(
133+
name: "HapticEngineClientLive",
134+
dependencies: [
135+
.target(name: "HapticEngineClient")
136+
]
137+
),
138+
110139
// MARK: IDFA
111140
.target(
112141
name: "IDFAPermissionsClient",
@@ -139,6 +168,10 @@ let package = Package(
139168
name: "KeychainClientLive",
140169
dependencies: [
141170
.target(name: "KeychainClient")
171+
],
172+
exclude: [
173+
"Keychain/Licence/LICENCE",
174+
"Keychain/Licence/README"
142175
]
143176
),
144177

Diff for: README.md

+39-4
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,28 @@ Client declarations and live implementations for standard iOS managers
1717

1818
| Description | Interface | Implementations |
1919
| ------------- | ------------- | ------------- |
20-
| [Caching](#Caching) | [CacheClient](Sources/CacheClient) | [MemoryCacheClient](Sources/CacheClientLive) |
20+
| [Caching](#Caching) | [CacheClient](Sources/CacheClient) | [MemoryCacheClient](Sources/MemoryCacheClientLive) |
2121
| [IDFA](#IDFA) | [IDFAPermissionsClient](Sources/IDFAPermissionsClient) | [IDFAPermissionsClientLive](Sources/IDFAPermissionsClientLive) |
2222
| [Keychain](#Keychain) | [KeychainClient](Sources/KeychainClient) | [KeychainClientLive](Sources/KeychainClientLive) |
23-
| [Notifications](#Notifications) | [NotificationsPermissionsClient](Sources/NotificationsPermissionsClient) | [KeychainClientLive](Sources/KeychainClientLive) |
24-
| [UserDefaults](#UserDefaults) | [UserDefaultsClient](Sources/UserDefaultsClient) | [UserDefaultsClientLive](Sources/UserDefaultsClientLive) |
23+
| [Notifications](#Notifications) | [NotificationsPermissionsClient](Sources/NotificationsPermissionsClient) | [NotificationsPermissionsClientLive](Sources/NotificationsPermissionsClientLive) |
24+
| [HapticEngine](#HapticEngine) | [HapticEngineClient](Sources/HapticEngineClient) | [HapticEngineClientLive](Sources/HapticEngineClientLive) |
25+
| [UserDefaults](#UserDefaults) | [UserDefaultsClient](Sources/UserDefaultsClient) | [UserDefaultsClientLive](Sources/UserDefaultsClientLive) |
26+
27+
28+
29+
### Todos
30+
31+
- [ ] Improve readme by adding examples and simplifying descriptions.
32+
- [ ] Add LocalAuthenticationClient [ _Soon_ ]
33+
- [ ] Find out if it's better to use `Any`-based UserDefaults storage instead of `DataRepresentable`-based.
34+
- Add more tests
35+
- [ ] Caching
36+
- [ ] IDFA
37+
- [ ] Keychain
38+
- [ ] Notifications
39+
- [ ] HapticEngine
40+
- [ ] UserDefaults
41+
- [x] DataRepresentable
2542

2643

2744

@@ -122,6 +139,24 @@ Client declarations and live implementations for standard iOS managers
122139
123140

124141

142+
## HapticEngine
143+
144+
`HapticEngineClient` is a factory-client for `HapticFeedback` clients. `HapticFeedback` is a client for `UIFeedbackGenerator`.
145+
146+
### Usage
147+
148+
```swift
149+
import HapticEngineClientLive
150+
151+
// If you need just one generator you can use HapticFeedback directly
152+
HapticFeedback.success.trigger()
153+
154+
// Otherwise if you need more flexible way to create Haptic feedbacks use HapticEngineClient
155+
HapticEngineClient.live.generator(for: .success).trigger()
156+
```
157+
158+
159+
125160
## UserDefaults
126161

127162
`UserDefaultsClient` is a client for UserDefaults object, it **stores objects as data** (Using [`DataRepresentable`](#DataRepresentable) protocol) and provides interfaces for the following operations:
@@ -158,7 +193,7 @@ If you use SwiftPM for your project, you can add StandardClients to your package
158193
.package(
159194
name: "swift-standard-clients",
160195
url: "https://github.com/capturecontext/swift-standard-clients.git",
161-
.upToNextMinor(from: "0.0.1")
196+
.upToNextMinor(from: "0.1.0")
162197
)
163198
```
164199

Diff for: Sources/HapticEngineClient/HapticEngineClient.swift

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
public struct HapticEngineClient {
2+
public init(generator: Operations.CreateFeedback) {
3+
self.generator = generator
4+
}
5+
6+
public var generator: Operations.CreateFeedback
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import Prelude
2+
3+
extension HapticEngineClient {
4+
public enum Operations {}
5+
}
6+
7+
extension HapticEngineClient.Operations {
8+
public struct CreateFeedback: Function {
9+
public enum Input: Equatable {
10+
case success
11+
case warning
12+
case error
13+
case selection
14+
15+
case light(intensity: Double? = nil)
16+
case medium(intensity: Double? = nil)
17+
case heavy(intensity: Double? = nil)
18+
case soft(intensity: Double? = nil)
19+
case rigid(intensity: Double? = nil)
20+
}
21+
22+
public typealias Output = HapticFeedback
23+
24+
public init(_ call: @escaping Signature) {
25+
self.call = call
26+
}
27+
28+
public var call: Signature
29+
30+
public func callAsFunction(for input: Input) -> Output {
31+
return call(input)
32+
}
33+
}
34+
}

Diff for: Sources/HapticEngineClient/HapticFeedback.swift

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
public struct HapticFeedback {
2+
public init(
3+
prepare: @escaping () -> Void,
4+
action: @escaping () -> Void
5+
) {
6+
self._prepare = prepare
7+
self._action = action
8+
}
9+
10+
private let _prepare: () -> Void
11+
private let _action: () -> Void
12+
public func prepare() { _prepare() }
13+
public func trigger() { _action() }
14+
}

Diff for: Sources/HapticEngineClientLive/Exports.swift

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@_exported import HapticEngineClient
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#if os(iOS)
2+
import HapticEngineClient
3+
import UIKit
4+
5+
extension HapticEngineClient {
6+
public static let live: HapticEngineClient = .init(generator: .live)
7+
}
8+
9+
extension HapticEngineClient.Operations.CreateFeedback {
10+
public static var live: Self {
11+
return .init { input in
12+
switch input {
13+
case .success : return .success
14+
case .warning : return .warning
15+
case .error : return .error
16+
case .selection : return .selection
17+
18+
case let .light(intensity):
19+
guard let intensity = intensity
20+
else { return .light }
21+
return .light(intensity: CGFloat(intensity))
22+
23+
case let .medium(intensity):
24+
guard let intensity = intensity
25+
else { return .medium }
26+
return .medium(intensity: CGFloat(intensity))
27+
28+
case let .heavy(intensity):
29+
guard let intensity = intensity
30+
else { return .heavy }
31+
return .heavy(intensity: CGFloat(intensity))
32+
33+
case let .soft(intensity):
34+
guard let intensity = intensity
35+
else { return .soft }
36+
return .soft(intensity: CGFloat(intensity))
37+
38+
case let .rigid(intensity):
39+
guard let intensity = intensity
40+
else { return .rigid }
41+
return .rigid(intensity: CGFloat(intensity))
42+
43+
}
44+
}
45+
}
46+
}
47+
#endif

Diff for: Sources/HapticEngineClientLive/HapticFeedback.swift

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#if os(iOS)
2+
import HapticEngineClient
3+
import UIKit
4+
5+
extension HapticFeedback {
6+
public static func custom<Generator: UIFeedbackGenerator>(
7+
_ generator: Generator,
8+
_ action: @escaping (Generator) -> Void
9+
) -> HapticFeedback {
10+
HapticFeedback(
11+
prepare: { generator.prepare() },
12+
action: { action(generator) }
13+
)
14+
}
15+
16+
public static var light: HapticFeedback {
17+
.custom(UIImpactFeedbackGenerator(style: .light)) {
18+
$0.impactOccurred()
19+
}
20+
}
21+
22+
public static var medium: HapticFeedback {
23+
.custom(UIImpactFeedbackGenerator(style: .medium)) {
24+
$0.impactOccurred()
25+
}
26+
}
27+
28+
public static var heavy: HapticFeedback {
29+
.custom(UIImpactFeedbackGenerator(style: .heavy)) {
30+
$0.impactOccurred()
31+
}
32+
}
33+
34+
public static var soft: HapticFeedback {
35+
.custom(UIImpactFeedbackGenerator(style: .soft)) {
36+
$0.impactOccurred()
37+
}
38+
}
39+
40+
public static var rigid: HapticFeedback {
41+
.custom(UIImpactFeedbackGenerator(style: .rigid)) {
42+
$0.impactOccurred()
43+
}
44+
}
45+
46+
public static func light(intensity: CGFloat) -> HapticFeedback {
47+
.custom(UIImpactFeedbackGenerator(style: .light)) {
48+
$0.impactOccurred(intensity: intensity)
49+
}
50+
}
51+
52+
public static func medium(intensity: CGFloat) -> HapticFeedback {
53+
.custom(UIImpactFeedbackGenerator(style: .medium)) {
54+
$0.impactOccurred(intensity: intensity)
55+
}
56+
}
57+
58+
public static func heavy(intensity: CGFloat) -> HapticFeedback {
59+
.custom(UIImpactFeedbackGenerator(style: .heavy)) {
60+
$0.impactOccurred(intensity: intensity)
61+
}
62+
}
63+
64+
public static func soft(intensity: CGFloat) -> HapticFeedback {
65+
.custom(UIImpactFeedbackGenerator(style: .soft)) {
66+
$0.impactOccurred(intensity: intensity)
67+
}
68+
}
69+
70+
public static func rigid(intensity: CGFloat) -> HapticFeedback {
71+
.custom(UIImpactFeedbackGenerator(style: .rigid)) {
72+
$0.impactOccurred(intensity: intensity)
73+
}
74+
}
75+
76+
public static var success: HapticFeedback {
77+
.custom(UINotificationFeedbackGenerator()) {
78+
$0.notificationOccurred(.success)
79+
}
80+
}
81+
82+
public static var warning: HapticFeedback {
83+
.custom(UINotificationFeedbackGenerator()) {
84+
$0.notificationOccurred(.success)
85+
}
86+
}
87+
88+
public static var error: HapticFeedback {
89+
.custom(UINotificationFeedbackGenerator()) {
90+
$0.notificationOccurred(.success)
91+
}
92+
}
93+
94+
public static var selection: HapticFeedback {
95+
.custom(UISelectionFeedbackGenerator()) {
96+
$0.selectionChanged()
97+
}
98+
}
99+
}
100+
#endif
101+
102+

0 commit comments

Comments
 (0)