Adding stubs for SwiftUI version of records screen
This commit is contained in:
parent
593deaf6f6
commit
c63ff50b00
@ -134,6 +134,12 @@
|
||||
7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8AB76425A0DB8F00ECF2C1 /* BundleVersion.swift */; };
|
||||
7A912F372D381B7400002938 /* LicensePlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A912F362D381B7400002938 /* LicensePlateView.swift */; };
|
||||
7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A91894E29A2BD8700519C74 /* GestureRecognizers.swift */; };
|
||||
7A9519792D80B3E800E69883 /* AudioRecordService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9519782D80B3E800E69883 /* AudioRecordService.swift */; };
|
||||
7A95197B2D80B41600E69883 /* AudioRecordServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A95197A2D80B41600E69883 /* AudioRecordServiceProtocol.swift */; };
|
||||
7A95197D2D80B43D00E69883 /* AudioRecordError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A95197C2D80B43D00E69883 /* AudioRecordError.swift */; };
|
||||
7A9519802D80B6C100E69883 /* RecordsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A95197F2D80B6C100E69883 /* RecordsScreen.swift */; };
|
||||
7A9519822D80B6E500E69883 /* RecordsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9519812D80B6E500E69883 /* RecordsViewModel.swift */; };
|
||||
7A9519842D80B72B00E69883 /* RecordsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9519832D80B72B00E69883 /* RecordsCoordinator.swift */; };
|
||||
7A961C6C2C4C3C8600CE2211 /* TextRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A961C6B2C4C3C8600CE2211 /* TextRowView.swift */; };
|
||||
7A961C6E2C4C3C9E00CE2211 /* LinkRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A961C6D2C4C3C9E00CE2211 /* LinkRowView.swift */; };
|
||||
7A96AE2D246B2B7400297C33 /* GoogleSignInController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE2C246B2B7400297C33 /* GoogleSignInController.swift */; };
|
||||
@ -419,6 +425,12 @@
|
||||
7A912F362D381B7400002938 /* LicensePlateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensePlateView.swift; sourceTree = "<group>"; };
|
||||
7A91894E29A2BD8700519C74 /* GestureRecognizers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GestureRecognizers.swift; sourceTree = "<group>"; };
|
||||
7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = "<group>"; };
|
||||
7A9519782D80B3E800E69883 /* AudioRecordService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecordService.swift; sourceTree = "<group>"; };
|
||||
7A95197A2D80B41600E69883 /* AudioRecordServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecordServiceProtocol.swift; sourceTree = "<group>"; };
|
||||
7A95197C2D80B43D00E69883 /* AudioRecordError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecordError.swift; sourceTree = "<group>"; };
|
||||
7A95197F2D80B6C100E69883 /* RecordsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordsScreen.swift; sourceTree = "<group>"; };
|
||||
7A9519812D80B6E500E69883 /* RecordsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordsViewModel.swift; sourceTree = "<group>"; };
|
||||
7A9519832D80B72B00E69883 /* RecordsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordsCoordinator.swift; sourceTree = "<group>"; };
|
||||
7A961C6B2C4C3C8600CE2211 /* TextRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRowView.swift; sourceTree = "<group>"; };
|
||||
7A961C6D2C4C3C9E00CE2211 /* LinkRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkRowView.swift; sourceTree = "<group>"; };
|
||||
7A96AE2C246B2B7400297C33 /* GoogleSignInController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSignInController.swift; sourceTree = "<group>"; };
|
||||
@ -726,6 +738,7 @@
|
||||
7A1441632C297E9800E79018 /* Screens */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A95197E2D80B69800E69883 /* RecordsScreen */,
|
||||
7A5911EC2D63225500EC51BA /* SearchScreen */,
|
||||
7A131FD12D37B74100DC7755 /* HistoryScreen */,
|
||||
7AB9FE202D08C28E005DE374 /* EventsScreen */,
|
||||
@ -802,6 +815,7 @@
|
||||
7A45FB362C2706D000618694 /* Services */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A9519772D80B3B200E69883 /* AudioRecordService */,
|
||||
7AB4E4392D3D3F390006D052 /* VehicleService */,
|
||||
7A06E0B12C707DD7005731AC /* SettingsService */,
|
||||
7A60D24B2C5A9D2700D13F7B /* LocationService */,
|
||||
@ -990,6 +1004,26 @@
|
||||
path = Location;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7A9519772D80B3B200E69883 /* AudioRecordService */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A9519782D80B3E800E69883 /* AudioRecordService.swift */,
|
||||
7A95197A2D80B41600E69883 /* AudioRecordServiceProtocol.swift */,
|
||||
7A95197C2D80B43D00E69883 /* AudioRecordError.swift */,
|
||||
);
|
||||
path = AudioRecordService;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7A95197E2D80B69800E69883 /* RecordsScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A95197F2D80B6C100E69883 /* RecordsScreen.swift */,
|
||||
7A9519812D80B6E500E69883 /* RecordsViewModel.swift */,
|
||||
7A9519832D80B72B00E69883 /* RecordsCoordinator.swift */,
|
||||
);
|
||||
path = RecordsScreen;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7AAAFAD12C4D0FB00050410D /* ACImageSlider */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1398,6 +1432,7 @@
|
||||
7A813DC32508EE4F00CC93B9 /* EventCell.swift in Sources */,
|
||||
7A1441682C297EFD00E79018 /* NotesViewModel.swift in Sources */,
|
||||
7AFBE8C02C3024E5003C491D /* ACHud.swift in Sources */,
|
||||
7A9519842D80B72B00E69883 /* RecordsCoordinator.swift in Sources */,
|
||||
7AABDE26253350C30041AFC6 /* RxSectionedDataSource.swift in Sources */,
|
||||
7AAAFADA2C4D1AFE0050410D /* Zoomable.swift in Sources */,
|
||||
7AC8B2762D6A01C700190706 /* UISearchTextField+Dumb.swift in Sources */,
|
||||
@ -1468,6 +1503,7 @@
|
||||
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */,
|
||||
7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */,
|
||||
7AC3555229696E3F00889457 /* UIView+layout.swift in Sources */,
|
||||
7A9519822D80B6E500E69883 /* RecordsViewModel.swift in Sources */,
|
||||
7AC355592969746600889457 /* UIControl.swift in Sources */,
|
||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */,
|
||||
7AC35554296973E100889457 /* ACButton.swift in Sources */,
|
||||
@ -1500,6 +1536,7 @@
|
||||
7A1022702C551EFD00B84627 /* LocationEditCoordinator.swift in Sources */,
|
||||
7A7158042C43EAA200852088 /* OwnersCoordinator.swift in Sources */,
|
||||
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */,
|
||||
7A9519802D80B6C100E69883 /* RecordsScreen.swift in Sources */,
|
||||
7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -1544,6 +1581,7 @@
|
||||
7A64A21E2C19E8D500284124 /* VehicleAdDto.swift in Sources */,
|
||||
7AF6D2202677C1680086EA64 /* Filter.swift in Sources */,
|
||||
7A761C042677F18E0005F28F /* ApiService.swift in Sources */,
|
||||
7A95197B2D80B41600E69883 /* AudioRecordServiceProtocol.swift in Sources */,
|
||||
7AF6D21C2677C1680086EA64 /* DebugInfo.swift in Sources */,
|
||||
7AF6D2122677C12E0086EA64 /* Location.swift in Sources */,
|
||||
7AF6D2142677C1680086EA64 /* VehicleEvent.swift in Sources */,
|
||||
@ -1562,6 +1600,7 @@
|
||||
7A761C07267E8E7F0005F28F /* AnyEncodable.swift in Sources */,
|
||||
7A64A2032C19DA1000284124 /* VehicleDto.swift in Sources */,
|
||||
7AB4E4332D3C21C00006D052 /* FileManagerExt.swift in Sources */,
|
||||
7A9519792D80B3E800E69883 /* AudioRecordService.swift in Sources */,
|
||||
7AB587322C42D38E00FA7B66 /* StorageServiceProtocol.swift in Sources */,
|
||||
7A3E12D72C7B42B700EE710D /* UserDefaults+Settings.swift in Sources */,
|
||||
7AB4E43B2D3D3F4F0006D052 /* VehicleServiceProtocol.swift in Sources */,
|
||||
@ -1587,6 +1626,7 @@
|
||||
7A599C362C18AC7F00D47C18 /* ApiError.swift in Sources */,
|
||||
7A45FB382C27073700618694 /* StorageService.swift in Sources */,
|
||||
7A64A20A2C19E07100284124 /* VehicleNameDto.swift in Sources */,
|
||||
7A95197D2D80B43D00E69883 /* AudioRecordError.swift in Sources */,
|
||||
7AF6D22A2677C3AD0086EA64 /* Exportable.swift in Sources */,
|
||||
7AF6D2212677C1680086EA64 /* PagedResponse.swift in Sources */,
|
||||
7AF6D2192677C1680086EA64 /* DateSection.swift in Sources */,
|
||||
|
||||
@ -18,6 +18,7 @@ class MainTabController: UITabBarController, UITabBarControllerDelegate {
|
||||
}
|
||||
|
||||
addHistoryTab()
|
||||
//addRecordsTab()
|
||||
|
||||
#if !targetEnvironment(macCatalyst)
|
||||
addDummyTab()
|
||||
@ -36,11 +37,20 @@ class MainTabController: UITabBarController, UITabBarControllerDelegate {
|
||||
historyViewModel = viewModel
|
||||
}
|
||||
|
||||
func addRecordsTab() {
|
||||
|
||||
let coordinator = RecordsCoordinator()
|
||||
let controller = coordinator.start()
|
||||
controller.tabBarItem = UITabBarItem(title: NSLocalizedString("Records", comment: ""),
|
||||
image: UIImage(named: "record"), tag: 0)
|
||||
viewControllers?[1] = controller
|
||||
}
|
||||
|
||||
func addDummyTab() {
|
||||
|
||||
let controller = DummyNewController()
|
||||
controller.tabBarItem = UITabBarItem(title: "", image: UIImage(systemName: "plus"), tag: 0)
|
||||
viewControllers?.insert(controller, at: 2)
|
||||
viewControllers?.append(controller)
|
||||
}
|
||||
|
||||
func addSearchTab() {
|
||||
|
||||
@ -60,6 +60,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
storageService: storageService,
|
||||
locationService: locationService)
|
||||
container.register(VehicleServiceProtocol.self, instance: vehicleService)
|
||||
|
||||
container.register(AudioRecordServiceProtocol.self, instance: AudioRecordService())
|
||||
}
|
||||
|
||||
func setupRootController(scene: UIScene, openReport number: String?) {
|
||||
|
||||
34
AutoCat/Screens/RecordsScreen/RecordsCoordinator.swift
Normal file
34
AutoCat/Screens/RecordsScreen/RecordsCoordinator.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// RecordsCoordinator.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 11.03.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
@MainActor
|
||||
final class RecordsCoordinator {
|
||||
|
||||
var navController = UINavigationController()
|
||||
|
||||
func start() -> UIViewController {
|
||||
|
||||
let resolver = ServiceContainer.shared
|
||||
let viewModel = RecordsViewModel(
|
||||
recordService: resolver.resolve(AudioRecordServiceProtocol.self),
|
||||
storageService: resolver.resolve(StorageServiceProtocol.self),
|
||||
locationService: resolver.resolve(LocationServiceProtocol.self)
|
||||
)
|
||||
|
||||
let view = RecordsScreen(viewModel: viewModel)
|
||||
let controller = UIHostingController(rootView: view)
|
||||
|
||||
let navController = UINavigationController(rootViewController: controller)
|
||||
self.navController = navController
|
||||
return navController
|
||||
}
|
||||
}
|
||||
18
AutoCat/Screens/RecordsScreen/RecordsScreen.swift
Normal file
18
AutoCat/Screens/RecordsScreen/RecordsScreen.swift
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// RecordsScreen.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 11.03.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct RecordsScreen: View {
|
||||
|
||||
@State var viewModel: RecordsViewModel
|
||||
|
||||
var body: some View {
|
||||
Text("Hello, World!")
|
||||
}
|
||||
}
|
||||
28
AutoCat/Screens/RecordsScreen/RecordsViewModel.swift
Normal file
28
AutoCat/Screens/RecordsScreen/RecordsViewModel.swift
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// RecordsViewModel.swift
|
||||
// AutoCat
|
||||
//
|
||||
// Created by Selim Mustafaev on 11.03.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AutoCatCore
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
final class RecordsViewModel {
|
||||
|
||||
let recordService: AudioRecordServiceProtocol
|
||||
let storageService: StorageServiceProtocol
|
||||
let locationService: LocationServiceProtocol
|
||||
|
||||
init(recordService: AudioRecordServiceProtocol,
|
||||
storageService: StorageServiceProtocol,
|
||||
locationService: LocationServiceProtocol) {
|
||||
|
||||
self.recordService = recordService
|
||||
self.storageService = storageService
|
||||
self.locationService = locationService
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
//
|
||||
// AudioRecordError.swift
|
||||
// AutoCatCore
|
||||
//
|
||||
// Created by Selim Mustafaev on 11.03.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum AudioRecordError: LocalizedError {
|
||||
|
||||
case permissionDenied
|
||||
case unknown
|
||||
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .permissionDenied: "Permission denied to record audio"
|
||||
case .unknown: "Unknown error"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
//
|
||||
// AudioRecordService.swift
|
||||
// AutoCatCore
|
||||
//
|
||||
// Created by Selim Mustafaev on 11.03.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import AVFoundation
|
||||
import Speech
|
||||
|
||||
public final class AudioRecordService {
|
||||
|
||||
let audioFileSettings: [String : Any] = [
|
||||
AVFormatIDKey: kAudioFormatMPEG4AAC,
|
||||
AVSampleRateKey: 44100,
|
||||
AVNumberOfChannelsKey: 2,
|
||||
]
|
||||
|
||||
var recorder: AVAudioRecorder?
|
||||
|
||||
public init() {
|
||||
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func requestRecognitionAuthorization() async -> SFSpeechRecognizerAuthorizationStatus {
|
||||
|
||||
let status = SFSpeechRecognizer.authorizationStatus()
|
||||
guard status == .notDetermined else {
|
||||
return status
|
||||
}
|
||||
|
||||
return await withCheckedContinuation { continuation in
|
||||
SFSpeechRecognizer.requestAuthorization { status in
|
||||
continuation.resume(returning: status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AudioRecordService: AudioRecordServiceProtocol {
|
||||
|
||||
public func requestPermissions() async -> Bool {
|
||||
|
||||
await requestRecognitionAuthorization()
|
||||
return await AVAudioApplication.requestRecordPermission()
|
||||
}
|
||||
|
||||
public func startRecording(to url: URL) async throws {
|
||||
guard AVAudioApplication.shared.recordPermission != .denied else {
|
||||
throw AudioRecordError.permissionDenied
|
||||
}
|
||||
|
||||
switch AVAudioApplication.shared.recordPermission {
|
||||
case .denied:
|
||||
throw AudioRecordError.permissionDenied
|
||||
case .undetermined:
|
||||
if await AVAudioApplication.requestRecordPermission() {
|
||||
break
|
||||
} else {
|
||||
return
|
||||
}
|
||||
case .granted:
|
||||
break
|
||||
@unknown default:
|
||||
throw AudioRecordError.unknown
|
||||
}
|
||||
|
||||
try AVAudioSession.sharedInstance().setCategory(.playAndRecord)
|
||||
try AVAudioSession.sharedInstance().setActive(true)
|
||||
|
||||
recorder = try AVAudioRecorder(url: url, settings: audioFileSettings)
|
||||
recorder?.record()
|
||||
}
|
||||
|
||||
public func stopRecording() {
|
||||
|
||||
recorder?.stop()
|
||||
recorder = nil
|
||||
}
|
||||
|
||||
public func recognizeText(from url: URL) async -> String? {
|
||||
|
||||
guard let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "ru-RU")), recognizer.isAvailable else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let request = SFSpeechURLRecognitionRequest(url: url)
|
||||
|
||||
return await withCheckedContinuation { continuation in
|
||||
recognizer.recognitionTask(with: request) { result, error in
|
||||
continuation.resume(returning: result?.bestTranscription.formattedString)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
//
|
||||
// AudioRecordServiceProtocol.swift
|
||||
// AutoCatCore
|
||||
//
|
||||
// Created by Selim Mustafaev on 11.03.2025.
|
||||
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol AudioRecordServiceProtocol {
|
||||
|
||||
func requestPermissions() async -> Bool
|
||||
func startRecording(to url: URL) async throws
|
||||
func stopRecording()
|
||||
func recognizeText(from url: URL) async -> String?
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user