Removing ObservableDefaults, back to old dumb UserDefaults wrapper
This commit is contained in:
parent
0a6426b7f1
commit
dc6281ac46
@ -116,7 +116,6 @@
|
|||||||
7A7AA2C72DA2A45600276D83 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2C62DA2A45600276D83 /* RealmSwift */; };
|
7A7AA2C72DA2A45600276D83 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2C62DA2A45600276D83 /* RealmSwift */; };
|
||||||
7A7AA2CA2DA2C85100276D83 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2C92DA2C85100276D83 /* RealmSwift */; };
|
7A7AA2CA2DA2C85100276D83 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2C92DA2C85100276D83 /* RealmSwift */; };
|
||||||
7A7AA2CB2DA2C85100276D83 /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2C92DA2C85100276D83 /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
7A7AA2CB2DA2C85100276D83 /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2C92DA2C85100276D83 /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
7A7AA2CE2DA3120500276D83 /* ObservableDefaults in Frameworks */ = {isa = PBXBuildFile; productRef = 7A7AA2CD2DA3120500276D83 /* ObservableDefaults */; };
|
|
||||||
7A7DADAC2D99738300F52F6C /* AudioRecordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7DADAB2D99738300F52F6C /* AudioRecordView.swift */; };
|
7A7DADAC2D99738300F52F6C /* AudioRecordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7DADAB2D99738300F52F6C /* AudioRecordView.swift */; };
|
||||||
7A809F392D66755B00CF1B3C /* Error+Canceled.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A809F382D66755B00CF1B3C /* Error+Canceled.swift */; };
|
7A809F392D66755B00CF1B3C /* Error+Canceled.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A809F382D66755B00CF1B3C /* Error+Canceled.swift */; };
|
||||||
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */; };
|
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */; };
|
||||||
@ -548,7 +547,6 @@
|
|||||||
files = (
|
files = (
|
||||||
7A7AA2C72DA2A45600276D83 /* RealmSwift in Frameworks */,
|
7A7AA2C72DA2A45600276D83 /* RealmSwift in Frameworks */,
|
||||||
7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */,
|
7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */,
|
||||||
7A7AA2CE2DA3120500276D83 /* ObservableDefaults in Frameworks */,
|
|
||||||
7AF8606E2CB9B86300954D2F /* Mockable in Frameworks */,
|
7AF8606E2CB9B86300954D2F /* Mockable in Frameworks */,
|
||||||
7A6C4D9E2C56BCA600982597 /* SwiftLocation in Frameworks */,
|
7A6C4D9E2C56BCA600982597 /* SwiftLocation in Frameworks */,
|
||||||
);
|
);
|
||||||
@ -1314,7 +1312,6 @@
|
|||||||
7A6C4D9D2C56BCA600982597 /* SwiftLocation */,
|
7A6C4D9D2C56BCA600982597 /* SwiftLocation */,
|
||||||
7AF8606D2CB9B86300954D2F /* Mockable */,
|
7AF8606D2CB9B86300954D2F /* Mockable */,
|
||||||
7A7AA2C62DA2A45600276D83 /* RealmSwift */,
|
7A7AA2C62DA2A45600276D83 /* RealmSwift */,
|
||||||
7A7AA2CD2DA3120500276D83 /* ObservableDefaults */,
|
|
||||||
);
|
);
|
||||||
productName = AutoCatCore;
|
productName = AutoCatCore;
|
||||||
productReference = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */;
|
productReference = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */;
|
||||||
@ -1363,7 +1360,6 @@
|
|||||||
7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */,
|
7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */,
|
||||||
7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */,
|
7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */,
|
||||||
7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */,
|
7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */,
|
||||||
7A7AA2CC2DA3120500276D83 /* XCRemoteSwiftPackageReference "ObservableDefaults" */,
|
|
||||||
);
|
);
|
||||||
productRefGroup = 7A1146FE23FDE7E500B424AF /* Products */;
|
productRefGroup = 7A1146FE23FDE7E500B424AF /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@ -2118,14 +2114,6 @@
|
|||||||
minimumVersion = 6.0.0;
|
minimumVersion = 6.0.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
7A7AA2CC2DA3120500276D83 /* XCRemoteSwiftPackageReference "ObservableDefaults" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/fatbobman/ObservableDefaults";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 0.6.2;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
7A813DBF2508C4D900CC93B9 /* XCRemoteSwiftPackageReference "ExceptionCatcher" */ = {
|
7A813DBF2508C4D900CC93B9 /* XCRemoteSwiftPackageReference "ExceptionCatcher" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/sindresorhus/ExceptionCatcher";
|
repositoryURL = "https://github.com/sindresorhus/ExceptionCatcher";
|
||||||
@ -2176,11 +2164,6 @@
|
|||||||
package = 7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */;
|
package = 7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */;
|
||||||
productName = RealmSwift;
|
productName = RealmSwift;
|
||||||
};
|
};
|
||||||
7A7AA2CD2DA3120500276D83 /* ObservableDefaults */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = 7A7AA2CC2DA3120500276D83 /* XCRemoteSwiftPackageReference "ObservableDefaults" */;
|
|
||||||
productName = ObservableDefaults;
|
|
||||||
};
|
|
||||||
7A813DC02508C4D900CC93B9 /* ExceptionCatcher */ = {
|
7A813DC02508C4D900CC93B9 /* ExceptionCatcher */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 7A813DBF2508C4D900CC93B9 /* XCRemoteSwiftPackageReference "ExceptionCatcher" */;
|
package = 7A813DBF2508C4D900CC93B9 /* XCRemoteSwiftPackageReference "ExceptionCatcher" */;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "3cc3aec63412867029bf57f8fff11744df284387a47994473d82e7aab44a4293",
|
"originHash" : "6fccb9fdc0d29647d4f0b927aef60f375302d72b5b724992eab52ac0d8ec71c3",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "exceptioncatcher",
|
"identity" : "exceptioncatcher",
|
||||||
@ -19,15 +19,6 @@
|
|||||||
"version" : "0.3.1"
|
"version" : "0.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"identity" : "observabledefaults",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/fatbobman/ObservableDefaults",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "4740e2c39043b7daff946aa3bbec22e6a68850d3",
|
|
||||||
"version" : "0.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "pkhud",
|
"identity" : "pkhud",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@ -38,7 +38,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
|
|
||||||
let container = ServiceContainer.shared
|
let container = ServiceContainer.shared
|
||||||
|
|
||||||
let settingsService = SettingsService()
|
let settingsService = await SettingsService()
|
||||||
|
|
||||||
container.register(SettingsServiceProtocol.self, instance: settingsService)
|
container.register(SettingsServiceProtocol.self, instance: settingsService)
|
||||||
|
|
||||||
|
|||||||
@ -60,6 +60,8 @@ class EventsViewModel: ACHudContainer {
|
|||||||
|
|
||||||
func updateEvents() {
|
func updateEvents() {
|
||||||
|
|
||||||
|
let email = settingsService.user.email
|
||||||
|
|
||||||
events = vehicle.events.sorted { $0.date > $1.date }.map { event in
|
events = vehicle.events.sorted { $0.date > $1.date }.map { event in
|
||||||
|
|
||||||
let date = Date(timeIntervalSince1970: event.date)
|
let date = Date(timeIntervalSince1970: event.date)
|
||||||
@ -71,7 +73,7 @@ class EventsViewModel: ACHudContainer {
|
|||||||
date: dateString,
|
date: dateString,
|
||||||
coordinate: coordinate,
|
coordinate: coordinate,
|
||||||
address: event.address ?? "Lat: \(event.latitude), Lon: \(event.longitude)",
|
address: event.address ?? "Lat: \(event.latitude), Lon: \(event.longitude)",
|
||||||
isMe: event.addedBy == settingsService.user.email
|
isMe: event.addedBy == email
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ struct SettingsScreen: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Picker("Server", selection: $viewModel.settingService.backend) {
|
Picker("Server", selection: $viewModel.backend) {
|
||||||
ForEach(Constants.Backend.allCases, id: \.self) { backend in
|
ForEach(Constants.Backend.allCases, id: \.self) { backend in
|
||||||
Text(backend.name)
|
Text(backend.name)
|
||||||
}
|
}
|
||||||
@ -49,26 +49,26 @@ struct SettingsScreen: View {
|
|||||||
Section("Plate number recognition") {
|
Section("Plate number recognition") {
|
||||||
ToggleRowView(title: "Alternative order",
|
ToggleRowView(title: "Alternative order",
|
||||||
description: "Recognize plate numbers in alternative form. For example 'ЕВА 123 777' instead of 'Е123ВА 777'",
|
description: "Recognize plate numbers in alternative form. For example 'ЕВА 123 777' instead of 'Е123ВА 777'",
|
||||||
toggle: $viewModel.settingService.recognizeAlternativeOrder)
|
toggle: $viewModel.recognizeAlternativeOrder)
|
||||||
ToggleRowView(title: "Shortened numbers",
|
ToggleRowView(title: "Shortened numbers",
|
||||||
description: "If enabled, app will try to recognize shortened plate numbers (without region) and add default region",
|
description: "If enabled, app will try to recognize shortened plate numbers (without region) and add default region",
|
||||||
toggle: $viewModel.settingService.recognizeShortenedNumbers)
|
toggle: $viewModel.recognizeShortenedNumbers)
|
||||||
if viewModel.settingService.recognizeShortenedNumbers {
|
if viewModel.recognizeShortenedNumbers {
|
||||||
LabeledContent("Default region") {
|
LabeledContent("Default region") {
|
||||||
TextField("", text: $viewModel.settingService.defaultRegion)
|
TextField("", text: $viewModel.defaultRegion)
|
||||||
.frame(width: 50)
|
.frame(width: 50)
|
||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToggleRowView(title: "Beep before record",
|
ToggleRowView(title: "Beep before record",
|
||||||
description: "When enabled, you will hear short sound before starting audio recording. This will only work when audio record is started via Siri",
|
description: "When enabled, you will hear short sound before starting audio recording. This will only work when audio record is started via Siri",
|
||||||
toggle: $viewModel.settingService.recordBeep)
|
toggle: $viewModel.recordBeep)
|
||||||
}
|
}
|
||||||
|
|
||||||
Section("Debug") {
|
Section("Debug") {
|
||||||
ToggleRowView(title: "Show debug info",
|
ToggleRowView(title: "Show debug info",
|
||||||
description: nil,
|
description: nil,
|
||||||
toggle: $viewModel.settingService.showDebugInfo)
|
toggle: $viewModel.showDebugInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Settings")
|
.navigationTitle("Settings")
|
||||||
|
|||||||
@ -42,6 +42,36 @@ class SettingsViewModel {
|
|||||||
return jwt.payload.email
|
return jwt.payload.email
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var recognizeAlternativeOrder: Bool {
|
||||||
|
get { settingService.recognizeAlternativeOrder }
|
||||||
|
set { settingService.recognizeAlternativeOrder = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
var recognizeShortenedNumbers: Bool {
|
||||||
|
get { settingService.recognizeShortenedNumbers }
|
||||||
|
set { settingService.recognizeShortenedNumbers = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
var backend: Constants.Backend {
|
||||||
|
get { settingService.backend }
|
||||||
|
set { settingService.backend = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
var recordBeep: Bool {
|
||||||
|
get { settingService.recordBeep }
|
||||||
|
set { settingService.recordBeep = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
var showDebugInfo: Bool {
|
||||||
|
get { settingService.showDebugInfo }
|
||||||
|
set { settingService.showDebugInfo = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultRegion: String {
|
||||||
|
get { settingService.defaultRegion }
|
||||||
|
set { settingService.defaultRegion = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
init(settingsService: SettingsServiceProtocol) {
|
init(settingsService: SettingsServiceProtocol) {
|
||||||
|
|
||||||
self.settingService = settingsService
|
self.settingService = settingsService
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
@MainActor
|
||||||
public struct User: Codable, Sendable {
|
public struct User: Codable, Sendable {
|
||||||
public let email: String
|
public let email: String
|
||||||
public var token: String
|
public var token: String
|
||||||
|
|||||||
@ -20,7 +20,7 @@ public actor ApiService: ApiServiceProtocol {
|
|||||||
|
|
||||||
private func createRequest<B,P>(api: String, method: String, body: B? = nil, params: [String:P]? = nil) async -> URLRequest? where B: Encodable, P: LosslessStringConvertible {
|
private func createRequest<B,P>(api: String, method: String, body: B? = nil, params: [String:P]? = nil) async -> URLRequest? where B: Encodable, P: LosslessStringConvertible {
|
||||||
|
|
||||||
let baseUrl = settingsService.backend.baseUrl
|
let baseUrl = await settingsService.backend.baseUrl
|
||||||
|
|
||||||
guard var urlComponents = URLComponents(string: baseUrl + api) else { return nil }
|
guard var urlComponents = URLComponents(string: baseUrl + api) else { return nil }
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ public actor ApiService: ApiServiceProtocol {
|
|||||||
request.httpMethod = method
|
request.httpMethod = method
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Accept")
|
request.addValue("application/json", forHTTPHeaderField: "Accept")
|
||||||
request.addValue("Bearer " + settingsService.user.token, forHTTPHeaderField: "Authorization")
|
request.addValue("Bearer " + (await settingsService.user.token), forHTTPHeaderField: "Authorization")
|
||||||
|
|
||||||
if let body = body, method.uppercased() != "GET" {
|
if let body = body, method.uppercased() != "GET" {
|
||||||
let encoder = JSONEncoder()
|
let encoder = JSONEncoder()
|
||||||
@ -109,8 +109,8 @@ public actor ApiService: ApiServiceProtocol {
|
|||||||
// MARK: - Firebase API
|
// MARK: - Firebase API
|
||||||
|
|
||||||
public func refreshFbToken() async throws {
|
public func refreshFbToken() async throws {
|
||||||
guard let token = settingsService.user.firebaseIdToken,
|
guard let token = await settingsService.user.firebaseIdToken,
|
||||||
let refreshToken = settingsService.user.firebaseRefreshToken,
|
let refreshToken = await settingsService.user.firebaseRefreshToken,
|
||||||
let jwt = JWT<FirebasePayload>(string: token), jwt.expired else {
|
let jwt = JWT<FirebasePayload>(string: token), jwt.expired else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -138,11 +138,11 @@ public actor ApiService: ApiServiceProtocol {
|
|||||||
let model = try JSONDecoder().decode(FbRefreshTokenModel.self, from: data)
|
let model = try JSONDecoder().decode(FbRefreshTokenModel.self, from: data)
|
||||||
|
|
||||||
if let idToken = model.id_token {
|
if let idToken = model.id_token {
|
||||||
await settingsService.user.firebaseIdToken = idToken
|
await settingsService.setFirebaseIdToken(idToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let refreshToken = model.refresh_token {
|
if let refreshToken = model.refresh_token {
|
||||||
await settingsService.user.firebaseRefreshToken = refreshToken
|
await settingsService.setFirebaseRefreshToken(refreshToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,11 +181,11 @@ public actor ApiService: ApiServiceProtocol {
|
|||||||
let model = try JSONDecoder().decode(FbVerifyTokenModel.self, from: data)
|
let model = try JSONDecoder().decode(FbVerifyTokenModel.self, from: data)
|
||||||
|
|
||||||
if let idToken = model.idToken {
|
if let idToken = model.idToken {
|
||||||
await settingsService.user.firebaseIdToken = idToken
|
await settingsService.setFirebaseIdToken(idToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let refreshToken = model.refreshToken {
|
if let refreshToken = model.refreshToken {
|
||||||
await settingsService.user.firebaseRefreshToken = refreshToken
|
await settingsService.setFirebaseRefreshToken(refreshToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
@ -237,7 +237,7 @@ public actor ApiService: ApiServiceProtocol {
|
|||||||
"forceUpdate": AnyEncodable(force)
|
"forceUpdate": AnyEncodable(force)
|
||||||
]
|
]
|
||||||
|
|
||||||
if let token = settingsService.user.firebaseIdToken {
|
if let token = await settingsService.user.firebaseIdToken {
|
||||||
body["googleIdToken"] = AnyEncodable(token)
|
body["googleIdToken"] = AnyEncodable(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ public actor ApiService: ApiServiceProtocol {
|
|||||||
|
|
||||||
var body = ["number": number]
|
var body = ["number": number]
|
||||||
|
|
||||||
if let token = settingsService.user.firebaseIdToken {
|
if let token = await settingsService.user.firebaseIdToken {
|
||||||
body["token"] = token
|
body["token"] = token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -52,7 +52,11 @@ public final class LocationService {
|
|||||||
throw LocationError.generic
|
throw LocationError.generic
|
||||||
}
|
}
|
||||||
|
|
||||||
return VehicleEventDto(lat: coordinate.latitude, lon: coordinate.longitude, addedBy: settingsService.user.email)
|
return VehicleEventDto(
|
||||||
|
lat: coordinate.latitude,
|
||||||
|
lon: coordinate.longitude,
|
||||||
|
addedBy: await settingsService.user.email
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setLastEvent(_ event: VehicleEventDto) {
|
func setLastEvent(_ event: VehicleEventDto) {
|
||||||
|
|||||||
@ -7,18 +7,37 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import ObservableDefaults
|
|
||||||
|
|
||||||
@ObservableDefaults
|
enum SettingsKey: String {
|
||||||
|
|
||||||
|
case user
|
||||||
|
case recognizeAlternativeOrder
|
||||||
|
case recognizeShortenedNumbers
|
||||||
|
case defaultRegion
|
||||||
|
case recordBeep
|
||||||
|
case showDebugInfo
|
||||||
|
case backendString
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
public final class SettingsService: SettingsServiceProtocol {
|
public final class SettingsService: SettingsServiceProtocol {
|
||||||
|
|
||||||
@Ignore let jsonEncoder = JSONEncoder()
|
let jsonEncoder = JSONEncoder()
|
||||||
@Ignore let jsonDecoder = JSONDecoder()
|
let jsonDecoder = JSONDecoder()
|
||||||
|
|
||||||
|
let suiteName: String?
|
||||||
|
|
||||||
|
var defaults: UserDefaults {
|
||||||
|
if let suiteName {
|
||||||
|
return UserDefaults(suiteName: suiteName) ?? .standard
|
||||||
|
} else {
|
||||||
|
return .standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ObservableOnly
|
|
||||||
public var user: User {
|
public var user: User {
|
||||||
get {
|
get {
|
||||||
if userData.count > 0 {
|
if let userData, userData.count > 0 {
|
||||||
let result = try? jsonDecoder.decode(User.self, from: userData)
|
let result = try? jsonDecoder.decode(User.self, from: userData)
|
||||||
return result ?? User()
|
return result ?? User()
|
||||||
} else {
|
} else {
|
||||||
@ -33,20 +52,102 @@ public final class SettingsService: SettingsServiceProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DefaultsKey(userDefaultsKey: "user")
|
public var userData: Data? {
|
||||||
public var userData: Data = .init()
|
didSet {
|
||||||
|
set(value: userData, for: .user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var recognizeAlternativeOrder: Bool = false
|
public var recognizeAlternativeOrder: Bool = false {
|
||||||
public var recognizeShortenedNumbers: Bool = false
|
didSet {
|
||||||
public var defaultRegion: String = "161"
|
set(value: recognizeAlternativeOrder, for: .recognizeAlternativeOrder)
|
||||||
public var recordBeep: Bool = false
|
}
|
||||||
public var showDebugInfo: Bool = false
|
}
|
||||||
|
|
||||||
|
public var recognizeShortenedNumbers: Bool = false {
|
||||||
|
didSet {
|
||||||
|
set(value: recognizeShortenedNumbers, for: .recognizeShortenedNumbers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var defaultRegion: String = "161" {
|
||||||
|
didSet {
|
||||||
|
set(value: defaultRegion, for: .defaultRegion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var recordBeep: Bool = false {
|
||||||
|
didSet {
|
||||||
|
set(value: recordBeep, for: .recordBeep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var showDebugInfo: Bool = false {
|
||||||
|
didSet {
|
||||||
|
set(value: showDebugInfo, for: .showDebugInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ObservableOnly
|
|
||||||
public var backend: Constants.Backend {
|
public var backend: Constants.Backend {
|
||||||
get { Constants.Backend(rawValue: backendString) ?? .de }
|
get { Constants.Backend(rawValue: backendString) ?? .de }
|
||||||
set { backendString = newValue.rawValue }
|
set { backendString = newValue.rawValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
public var backendString: String = Constants.Backend.de.rawValue
|
public var backendString: String = Constants.Backend.de.rawValue {
|
||||||
|
didSet {
|
||||||
|
set(value: backendString, for: .backendString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(suiteName: String? = nil) async {
|
||||||
|
|
||||||
|
self.suiteName = suiteName
|
||||||
|
|
||||||
|
register(defaultValues: [
|
||||||
|
.recognizeAlternativeOrder: false,
|
||||||
|
.recognizeShortenedNumbers: false,
|
||||||
|
.defaultRegion: "761",
|
||||||
|
.recordBeep: false,
|
||||||
|
.showDebugInfo: false,
|
||||||
|
.backendString: Constants.Backend.de.rawValue
|
||||||
|
])
|
||||||
|
|
||||||
|
userData = data(for: .user)
|
||||||
|
recognizeAlternativeOrder = bool(for: .recognizeAlternativeOrder)
|
||||||
|
recognizeShortenedNumbers = bool(for: .recognizeShortenedNumbers)
|
||||||
|
defaultRegion = string(for: .defaultRegion)
|
||||||
|
recordBeep = bool(for: .recordBeep)
|
||||||
|
showDebugInfo = bool(for: .showDebugInfo)
|
||||||
|
backendString = string(for: .backendString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func register(defaultValues: [SettingsKey: Any]) {
|
||||||
|
defaults.register(defaults: defaultValues.reduce(into: [:], { (partialResult: inout [String: Any], tuple) in
|
||||||
|
partialResult[tuple.key.rawValue] = tuple.value
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func set<T>(value: T, for key: SettingsKey) {
|
||||||
|
defaults.setValue(value, forKey: key.rawValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bool(for key: SettingsKey) -> Bool {
|
||||||
|
defaults.bool(forKey: key.rawValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func string(for key: SettingsKey, defaultValue: String = "") -> String {
|
||||||
|
defaults.string(forKey: key.rawValue) ?? defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func data(for key: SettingsKey) -> Data? {
|
||||||
|
defaults.data(forKey: key.rawValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setFirebaseIdToken(_ idToken: String?) {
|
||||||
|
user.firebaseIdToken = idToken
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setFirebaseRefreshToken(_ refreshToken: String?) {
|
||||||
|
user.firebaseRefreshToken = refreshToken
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,15 +8,18 @@
|
|||||||
|
|
||||||
import Mockable
|
import Mockable
|
||||||
|
|
||||||
|
@MainActor
|
||||||
@Mockable
|
@Mockable
|
||||||
public protocol SettingsServiceProtocol: Sendable {
|
public protocol SettingsServiceProtocol: Sendable {
|
||||||
|
|
||||||
var user: User { get set }
|
var user: User { get set }
|
||||||
|
|
||||||
var recognizeAlternativeOrder: Bool { get set }
|
var recognizeAlternativeOrder: Bool { get set }
|
||||||
var recognizeShortenedNumbers: Bool { get set }
|
var recognizeShortenedNumbers: Bool { get set }
|
||||||
var defaultRegion: String { get set }
|
var defaultRegion: String { get set }
|
||||||
var recordBeep: Bool { get set }
|
var recordBeep: Bool { get set }
|
||||||
var showDebugInfo: Bool { get set }
|
var showDebugInfo: Bool { get set }
|
||||||
var backend: Constants.Backend { get set }
|
var backend: Constants.Backend { get set }
|
||||||
|
|
||||||
|
func setFirebaseIdToken(_ idToken: String?)
|
||||||
|
func setFirebaseRefreshToken(_ refreshToken: String?)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ extension StorageService {
|
|||||||
throw StorageError.vehicleNotFound
|
throw StorageError.vehicleNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
let note = VehicleNote(text: text, user: settingsService.user.email)
|
let note = VehicleNote(text: text, user: await settingsService.user.email)
|
||||||
|
|
||||||
try await realm.asyncWrite {
|
try await realm.asyncWrite {
|
||||||
vehicle.notes.append(note)
|
vehicle.notes.append(note)
|
||||||
|
|||||||
@ -33,7 +33,7 @@ public actor VehicleRecordService {
|
|||||||
self.settingsService = settingsService
|
self.settingsService = settingsService
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPlateNumber(from recognizedText: String?) -> String? {
|
func getPlateNumber(from recognizedText: String?) async -> String? {
|
||||||
guard let recognizedText else {
|
guard let recognizedText else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -45,17 +45,21 @@ public actor VehicleRecordService {
|
|||||||
.replacingOccurrences(of: "НОЛЬ", with: "0")
|
.replacingOccurrences(of: "НОЛЬ", with: "0")
|
||||||
.replacingOccurrences(of: "Э", with: "")
|
.replacingOccurrences(of: "Э", with: "")
|
||||||
|
|
||||||
|
let recognizeAlternativeOrder = await settingsService.recognizeAlternativeOrder
|
||||||
|
let recognizeShortenedNumbers = await settingsService.recognizeShortenedNumbers
|
||||||
|
let defaultRegion = await settingsService.defaultRegion
|
||||||
|
|
||||||
var result = ""
|
var result = ""
|
||||||
if let range = trimmed.range(of: #"\S\d\d\d\S\S\d\d\d?"#, options: .regularExpression) {
|
if let range = trimmed.range(of: #"\S\d\d\d\S\S\d\d\d?"#, options: .regularExpression) {
|
||||||
result = String(trimmed[range])
|
result = String(trimmed[range])
|
||||||
} else if let range = trimmed.range(of: #"\S\S\S\d\d\d\d\d\d?"#, options: .regularExpression), settingsService.recognizeAlternativeOrder {
|
} else if let range = trimmed.range(of: #"\S\S\S\d\d\d\d\d\d?"#, options: .regularExpression), recognizeAlternativeOrder {
|
||||||
let n = String(trimmed[range])
|
let n = String(trimmed[range])
|
||||||
result = String(n.prefix(1)) + n.substring(with: 3..<6) + n.substring(with: 1..<3) + n.substring(from: 6)
|
result = String(n.prefix(1)) + n.substring(with: 3..<6) + n.substring(with: 1..<3) + n.substring(from: 6)
|
||||||
} else if let range = trimmed.range(of: #"\S\d\d\d\S\S"#, options: .regularExpression), settingsService.recognizeShortenedNumbers {
|
} else if let range = trimmed.range(of: #"\S\d\d\d\S\S"#, options: .regularExpression), recognizeShortenedNumbers {
|
||||||
result = String(trimmed[range]) + settingsService.defaultRegion
|
result = String(trimmed[range]) + defaultRegion
|
||||||
} else if let range = trimmed.range(of: #"\S\S\S\d\d\d"#, options: .regularExpression), settingsService.recognizeAlternativeOrder && settingsService.recognizeShortenedNumbers {
|
} else if let range = trimmed.range(of: #"\S\S\S\d\d\d"#, options: .regularExpression), recognizeAlternativeOrder && recognizeShortenedNumbers {
|
||||||
let n = String(trimmed[range])
|
let n = String(trimmed[range])
|
||||||
result = String(n.prefix(1)) + n.substring(with: 3..<6) + n.substring(with: 1..<3) + settingsService.defaultRegion
|
result = String(n.prefix(1)) + n.substring(with: 3..<6) + n.substring(with: 1..<3) + defaultRegion
|
||||||
}
|
}
|
||||||
|
|
||||||
if !result.isEmpty && valid(number: result) {
|
if !result.isEmpty && valid(number: result) {
|
||||||
@ -123,7 +127,7 @@ extension VehicleRecordService: VehicleRecordServiceProtocol {
|
|||||||
|
|
||||||
let record = AudioRecordDto(
|
let record = AudioRecordDto(
|
||||||
path: url.lastPathComponent,
|
path: url.lastPathComponent,
|
||||||
number: getPlateNumber(from: text),
|
number: await getPlateNumber(from: text),
|
||||||
raw: text ?? "",
|
raw: text ?? "",
|
||||||
duration: duration ?? 0,
|
duration: duration ?? 0,
|
||||||
event: location
|
event: location
|
||||||
|
|||||||
@ -30,11 +30,11 @@ struct SettingsServiceTests {
|
|||||||
let defaults: UserDefaults
|
let defaults: UserDefaults
|
||||||
var settingsService: SettingsService
|
var settingsService: SettingsService
|
||||||
|
|
||||||
init() {
|
init() async {
|
||||||
self.testUser = User(email: testEmail, token: testToken)
|
self.testUser = User(email: testEmail, token: testToken)
|
||||||
self.defaults = UserDefaults(suiteName: dbName)!
|
self.defaults = UserDefaults(suiteName: dbName)!
|
||||||
self.defaults.removePersistentDomain(forName: dbName)
|
self.defaults.removePersistentDomain(forName: dbName)
|
||||||
self.settingsService = SettingsService(userDefaults: self.defaults)
|
self.settingsService = await SettingsService(suiteName: dbName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test("Creating default user")
|
@Test("Creating default user")
|
||||||
@ -50,7 +50,7 @@ struct SettingsServiceTests {
|
|||||||
let data = try #require(testUser.data)
|
let data = try #require(testUser.data)
|
||||||
|
|
||||||
defaults.setPersistentDomain(["user": data], forName: dbName)
|
defaults.setPersistentDomain(["user": data], forName: dbName)
|
||||||
let service = SettingsService(userDefaults: defaults)
|
let service = await SettingsService(suiteName: dbName)
|
||||||
|
|
||||||
#expect(service.user.email == testEmail)
|
#expect(service.user.email == testEmail)
|
||||||
#expect(service.user.token == testToken)
|
#expect(service.user.token == testToken)
|
||||||
|
|||||||
@ -34,7 +34,7 @@ struct StorageServiceTests {
|
|||||||
|
|
||||||
try addTestVehicle(config: realmConfig)
|
try addTestVehicle(config: realmConfig)
|
||||||
|
|
||||||
given(settingsServiceMock)
|
await given(settingsServiceMock)
|
||||||
.user.willReturn(User())
|
.user.willReturn(User())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user