Skip to content

Commit b5a1a80

Browse files
authored
Merge pull request #2 from ezefranca/feature/update_delegate
Created didUpdateAuthorizationStatus and speedManagerDidFailWithLocat…
2 parents d4797b1 + 38b4eb5 commit b5a1a80

File tree

9 files changed

+135
-85
lines changed

9 files changed

+135
-85
lines changed

Demo/Demo/Demo/ContentView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import SwiftUI
99
import SpeedManagerModule
1010

1111
struct ContentView: View {
12-
@StateObject var speedManager = SpeedManager(.kilometersPerHour)
12+
@StateObject var speedManager = SpeedManager(speedUnit: .kilometersPerHour)
1313

1414
var body: some View {
1515
VStack {
1616
switch speedManager.authorizationStatus {
1717
case .authorized:
1818
Text("Your current speed is:")
19-
Text("\(speedManager.speed.fixed())")
19+
Text("\(speedManager.speed.fixedToOneDecimal())")
2020
Text("km/h")
2121

2222
CustomGauge(currentSpeed: $speedManager.speed)

Demo/Demo/Demo/CustomGauge.swift

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,78 +5,72 @@
55
// Created by Ezequiel Santos on 19/12/2022.
66
//
77

8-
import SwiftUI
9-
108
import SwiftUI
119
import Foundation
1210

1311
struct CustomGauge: View {
14-
1512
@Binding var currentSpeed: Double
16-
13+
1714
var body: some View {
1815
if #available(macOS 13.0, *) {
1916
Gauge(value: currentSpeed, in: 0...200) {
2017
Image(systemName: "gauge.medium")
2118
.font(.system(size: 50.0))
2219
} currentValueLabel: {
23-
Text("\(currentSpeed.fixed().formatted(.number))")
24-
20+
Text("\(currentSpeed.fixedToOneDecimal().formatted(.number))")
21+
.monospaced()
2522
}
2623
.gaugeStyle(SpeedometerGaugeStyle())
24+
.animation(nil, value: currentSpeed) // Disable animations for gauge updates
2725
} else {
2826
// Fallback on earlier versions
2927
}
30-
3128
}
3229
}
3330

3431
struct SpeedometerGaugeStyle: GaugeStyle {
35-
private var purpleGradient = LinearGradient(gradient: Gradient(colors: [ Color(red: 207/255, green: 150/255, blue: 207/255), Color(red: 107/255, green: 116/255, blue: 179/255) ]), startPoint: .trailing, endPoint: .leading)
36-
32+
private var purpleGradient = LinearGradient(gradient: Gradient(colors: [Color(red: 207/255, green: 150/255, blue: 207/255), Color(red: 107/255, green: 116/255, blue: 179/255)]), startPoint: .trailing, endPoint: .leading)
33+
3734
func makeBody(configuration: Configuration) -> some View {
3835
ZStack {
39-
4036
Circle()
4137
.foregroundColor(Color(.lightGray))
42-
38+
4339
Circle()
4440
.trim(from: 0, to: 0.75 * configuration.value)
4541
.stroke(purpleGradient, lineWidth: 20)
4642
.rotationEffect(.degrees(135))
47-
43+
4844
Circle()
4945
.trim(from: 0, to: 0.75)
5046
.stroke(Color.black, style: StrokeStyle(lineWidth: 10, lineCap: .butt, lineJoin: .round, dash: [1, 34], dashPhase: 0.0))
5147
.rotationEffect(.degrees(135))
52-
48+
5349
VStack {
5450
configuration.currentValueLabel
5551
.font(.system(size: 80, weight: .bold, design: .rounded))
5652
.foregroundColor(.gray)
53+
.monospaced()
5754
Text("KM/H")
5855
.font(.system(.body, design: .rounded))
5956
.bold()
6057
.foregroundColor(.gray)
6158
}
62-
6359
}
6460
.frame(width: 300, height: 300)
65-
6661
}
67-
6862
}
6963

7064
struct Gauge_Previews: PreviewProvider {
7165
static var previews: some View {
72-
CustomGauge(currentSpeed: .constant(100))
66+
CustomGauge(currentSpeed: .constant(100.0))
7367
}
7468
}
7569

7670
extension Double {
77-
78-
func fixed() -> Double {
79-
Double(String(format:"%.2f", self)) ?? self
71+
/// Returns the double value fixed to one decimal place.
72+
func fixedToOneDecimal() -> Double {
73+
(self * 10).rounded() / 10
8074
}
8175
}
8276

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 5.5
1+
// swift-tools-version: 5.10
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription

README.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ I like to measure my speed inside trains and buses. When I was searching for a s
2424

2525
## Installation
2626

27-
The Swift Package Manager is the easiest way to install and manage SpeedManagerModule as a dependecy.
27+
The Swift Package Manager is the easiest way to install and manage SpeedManagerModule as a dependency.
2828
Simply add SpeedManagerModule to your dependencies in your Package.swift file:
2929

3030
```swift
@@ -61,7 +61,6 @@ Or add the info to the Info.plist
6161

6262
## Usage example
6363

64-
6564
### @StateObject
6665

6766
```swift
@@ -77,7 +76,6 @@ struct ContentView: View {
7776
case .authorized:
7877
Text("Your current speed is:")
7978
Text("\(speedManager.speed)")
80-
Text("km/h")
8179
default:
8280
Spacer()
8381
}
@@ -88,8 +86,7 @@ struct ContentView: View {
8886

8987
### Using Delegates
9088

91-
``` swift
92-
89+
```swift
9390
import UIKit
9491

9592
class SpeedViewController: UIViewController {
@@ -105,40 +102,45 @@ class SpeedViewController: UIViewController {
105102

106103
extension SpeedViewController: SpeedManagerDelegate {
107104

108-
func speedManager(_ manager: SpeedManager, didUpdateSpeed speed: Double) {
105+
func speedManager(_ manager: SpeedManager, didUpdateSpeed speed: Double, speedAccuracy: Double) {
106+
// Update UI with the current speed and accuracy
109107
}
110108

111109
func speedManager(_ manager: SpeedManager, didFailWithError error: Error) {
110+
// Handle error
111+
}
112+
113+
func speedManager(_ speedManager: SpeedManager, didUpdateAuthorizationStatus status: SpeedManagerAuthorizationStatus) {
114+
// Handle authorization status update
115+
}
116+
117+
func speedManagerDidFailWithLocationServicesUnavailable(_ speedManager: SpeedManager) {
118+
// Handle location services unavailable
112119
}
113120
}
114-
115121
```
116122

117123
### Changing Unit
118124

119125
Just choose the unit during the class init.
120126

121127
```swift
122-
123128
var speedManagerKmh = SpeedManager(.kilometersPerHour)
124-
var speedManagerMs = SpeedManager(.meterPerSecond)
129+
var speedManagerMs = SpeedManager(.metersPerSecond)
125130
var speedManagerMph = SpeedManager(.milesPerHour)
126-
127131
```
128132

129133
### Demo
130134

131-
Check the ```Demo``` folder to see it in action.
132-
135+
Check the `Demo` folder to see it in action.
133136

134137
https://user-images.githubusercontent.com/3648336/208701407-ebf7319f-32c1-45bc-adc7-aa8509f0336d.mov
135138

136-
137139
## Meta
138140

139141
@ezefranca[@ezefranca](https://twitter.com/ezefranca)
140142

141-
Distributed under the MIT license. See ``LICENSE`` for more information.
143+
Distributed under the MIT license. See `LICENSE` for more information.
142144

143145
[https://github.com/ezefranca/SpeedManagerModule](https://github.com/ezefranca/SpeedManagerModule)
144146

Lines changed: 76 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,59 @@
11
import Foundation
22
import CoreLocation
33

4-
public protocol SpeedManagerTrigger {
5-
func startUpdatingSpeed()
6-
func startMonitoringSpeed()
7-
}
8-
9-
public class SpeedManager : NSObject, ObservableObject, SpeedManagerTrigger {
4+
/// A class that manages the monitoring and updating of speed using CoreLocation.
5+
public class SpeedManager: NSObject, ObservableObject, SpeedManagerTrigger {
6+
7+
// MARK: - Properties
108

11-
// MARK: Private
9+
/// The CoreLocation manager used to get location updates.
1210
private let locationManager = CLLocationManager()
11+
12+
/// The unit of speed to be used.
1313
private var speedUnit: SpeedManagerUnit
14+
15+
/// The trigger for starting speed updates.
1416
private var trigger: SpeedManagerTrigger?
15-
private var allowsBackgroundLocationUpdates: Bool = false
16-
17-
// MARK: Public
18-
public var delegate: SpeedManagerDelegate?
19-
20-
@Published public var authorizationStatus: SpeedManagerAuthorizationStatus = .notDetermined
21-
@Published public var speed: Double = 0
22-
@Published public var speedAccuracy: Double = 0
17+
18+
/// Indicates whether background location updates are allowed.
19+
private var allowsBackgroundLocationUpdates: Bool
20+
21+
/// The delegate to receive updates from the SpeedManager.
22+
public weak var delegate: SpeedManagerDelegate?
23+
24+
/// The current authorization status for location services.
25+
@Published public private(set) var authorizationStatus: SpeedManagerAuthorizationStatus = .notDetermined {
26+
didSet {
27+
DispatchQueue.main.async {
28+
self.delegate?.speedManager(self, didUpdateAuthorizationStatus: self.authorizationStatus)
29+
}
30+
}
31+
}
32+
33+
/// The current speed.
34+
@Published public var speed: Double = 0 {
35+
didSet {
36+
DispatchQueue.main.async {
37+
self.delegate?.speedManager(self, didUpdateSpeed: self.speed, speedAccuracy: self.speedAccuracy)
38+
}
39+
}
40+
}
41+
42+
/// The accuracy of the current speed.
43+
@Published public private(set) var speedAccuracy: Double = 0
2344

24-
private var isRequestingLocation = false
45+
// MARK: - Initializer
2546

26-
public init(_ speedUnit: SpeedManagerUnit,
47+
/// Initializes a new SpeedManager.
48+
/// - Parameters:
49+
/// - speedUnit: The unit of speed measurement.
50+
/// - trigger: An optional trigger for starting speed updates. If nil, the SpeedManager will trigger itself.
51+
/// - allowsBackgroundLocationUpdates: A Boolean value indicating whether background location updates are allowed.
52+
public init(speedUnit: SpeedManagerUnit,
2753
trigger: SpeedManagerTrigger? = nil,
2854
allowsBackgroundLocationUpdates: Bool = false) {
2955

3056
self.speedUnit = speedUnit
31-
self.delegate = nil
3257
self.allowsBackgroundLocationUpdates = allowsBackgroundLocationUpdates
3358
super.init()
3459
self.trigger = trigger ?? self
@@ -40,63 +65,73 @@ public class SpeedManager : NSObject, ObservableObject, SpeedManagerTrigger {
4065
self.locationManager.requestAlwaysAuthorization()
4166
}
4267

68+
// MARK: - Public Methods
69+
70+
/// Starts updating the speed.
4371
public func startUpdatingSpeed() {
4472
trigger?.startMonitoringSpeed()
4573
}
4674

75+
/// Starts monitoring the speed.
4776
public func startMonitoringSpeed() {
48-
49-
switch self.authorizationStatus {
50-
77+
switch authorizationStatus {
5178
case .authorized:
52-
if allowsBackgroundLocationUpdates { self.locationManager.allowsBackgroundLocationUpdates = true
79+
if allowsBackgroundLocationUpdates {
80+
locationManager.allowsBackgroundLocationUpdates = true
5381
}
54-
self.locationManager.startUpdatingLocation()
82+
locationManager.startUpdatingLocation()
5583
case .notDetermined:
56-
self.locationManager.requestAlwaysAuthorization()
84+
locationManager.requestAlwaysAuthorization()
5785
case .denied:
58-
fatalError("No location services available")
86+
DispatchQueue.main.async {
87+
self.delegate?.speedManagerDidFailWithLocationServicesUnavailable(self)
88+
}
5989
}
6090
}
6191
}
6292

63-
// MARK: CLLocationManagerDelegate methods
93+
// MARK: - CLLocationManagerDelegate
6494

6595
extension SpeedManager: CLLocationManagerDelegate {
6696

97+
/// Called when the authorization status changes.
98+
/// - Parameter manager: The location manager reporting the change.
6799
public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
68100
switch manager.authorizationStatus {
69-
70-
case .authorizedWhenInUse,
71-
.authorizedAlways:
101+
case .authorizedWhenInUse, .authorizedAlways:
72102
authorizationStatus = .authorized
73-
locationManager.requestLocation()
74-
break
75-
103+
locationManager.requestLocation()
76104
case .notDetermined:
77105
authorizationStatus = .notDetermined
78106
manager.requestWhenInUseAuthorization()
79-
break
80-
81107
default:
82108
authorizationStatus = .denied
83109
}
84110

85-
self.startMonitoringSpeed()
111+
startMonitoringSpeed()
86112
}
87113

114+
/// Called when new location data is available.
115+
/// - Parameters:
116+
/// - manager: The location manager providing the data.
117+
/// - locations: An array of new location data objects.
88118
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
119+
guard let lastLocation = locations.last else { return }
89120

90-
let currentSpeed = locations.last?.speed ?? 0
121+
let currentSpeed = lastLocation.speed
91122
speed = currentSpeed >= 0 ? currentSpeed * speedUnit.rawValue : .nan
92-
speedAccuracy = locations.last?.speedAccuracy ?? .nan
123+
speedAccuracy = lastLocation.speedAccuracy
93124

94-
self.delegate?.speedManager(self, didUpdateSpeed: speed, speedAccuracy: speedAccuracy)
95-
96-
self.locationManager.requestLocation()
125+
locationManager.requestLocation()
97126
}
98127

128+
/// Called when the location manager encounters an error.
129+
/// - Parameters:
130+
/// - manager: The location manager reporting the error.
131+
/// - error: The error encountered by the location manager.
99132
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
100-
self.delegate?.speedManager(self, didFailWithError: error)
133+
DispatchQueue.main.async {
134+
self.delegate?.speedManager(self, didFailWithError: error)
135+
}
101136
}
102137
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
/// Enumeration representing the authorization status for the speed manager.
12
public enum SpeedManagerAuthorizationStatus {
2-
case authorized
33
case notDetermined
4+
case authorized
45
case denied
56
}

0 commit comments

Comments
 (0)