Switching from CoreData to Realm

This commit is contained in:
Selim Mustafaev 2023-02-12 15:30:42 +03:00
parent 21d396b311
commit 18f1a00d82
32 changed files with 488 additions and 679 deletions

View File

@ -49,6 +49,9 @@
7A36E56027FB5A2F0025AACB /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AAE27FA3CCF001A18EE /* MockURLProtocol.swift */; };
7A36E56127FB5A330025AACB /* ApiMethodMockProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AAF27FA3CCF001A18EE /* ApiMethodMockProtocol.swift */; };
7A36E56327FB5BEB0025AACB /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A36E56227FB5BEB0025AACB /* TestError.swift */; };
7A39D0772998F25600A6F6FC /* PlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A39D0762998F25600A6F6FC /* PlaceholderView.swift */; };
7A39D07A2998FF3500A6F6FC /* RealmDecoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A39D0792998FF3500A6F6FC /* RealmDecoding.swift */; };
7A39D07D299904B700A6F6FC /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A39D07C299904B700A6F6FC /* SidebarView.swift */; };
7A48B26727D9442A004D1A4B /* PKHUD in Frameworks */ = {isa = PBXBuildFile; productRef = 7A48B26627D9442A004D1A4B /* PKHUD */; };
7A4951B7288D3A6100C644B6 /* AutoCat2SUIApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4951B6288D3A6100C644B6 /* AutoCat2SUIApp.swift */; };
7A4951B9288D3A6100C644B6 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4951B8288D3A6100C644B6 /* RootView.swift */; };
@ -87,9 +90,10 @@
7A49F51027D406CB00AEAAE0 /* MainSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50927D406CB00AEAAE0 /* MainSettings.swift */; };
7A49F51127D406CB00AEAAE0 /* PlateNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50A27D406CB00AEAAE0 /* PlateNumber.swift */; };
7A49F51227D406CB00AEAAE0 /* VName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50B27D406CB00AEAAE0 /* VName.swift */; };
7A49F51527D40C6100AEAAE0 /* AutoCat2.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F51327D40C6100AEAAE0 /* AutoCat2.xcdatamodeld */; };
7A558AB027FA3CCF001A18EE /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AA527FA3CCF001A18EE /* SettingsTests.swift */; };
7A558AB127FA3CCF001A18EE /* ApiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A558AA627FA3CCF001A18EE /* ApiTests.swift */; };
7A91E8112986DE9C0008A982 /* Realm in Frameworks */ = {isa = PBXBuildFile; productRef = 7A91E8102986DE9C0008A982 /* Realm */; };
7A91E8132986DE9C0008A982 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A91E8122986DE9C0008A982 /* RealmSwift */; };
7A9471BD28C52DD80054CB0A /* PlateViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9471BC28C52DD80054CB0A /* PlateViewItem.swift */; };
7A9F2AC327E71531006492A9 /* ACTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F2AC227E71531006492A9 /* ACTabBarController.swift */; };
7A9FD4072857AF590057ECFA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FD4062857AF590057ECFA /* AppDelegate.swift */; };
@ -246,6 +250,9 @@
7A2B6CD527FCEC8600519F1E /* XCUIApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIApplication.swift; sourceTree = "<group>"; };
7A36E55B27FB55570025AACB /* Testing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Testing.swift; sourceTree = "<group>"; };
7A36E56227FB5BEB0025AACB /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = "<group>"; };
7A39D0762998F25600A6F6FC /* PlaceholderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderView.swift; sourceTree = "<group>"; };
7A39D0792998FF3500A6F6FC /* RealmDecoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmDecoding.swift; sourceTree = "<group>"; };
7A39D07C299904B700A6F6FC /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
7A4951B4288D3A6100C644B6 /* AutoCat2SUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoCat2SUI.app; sourceTree = BUILT_PRODUCTS_DIR; };
7A4951B6288D3A6100C644B6 /* AutoCat2SUIApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCat2SUIApp.swift; sourceTree = "<group>"; };
7A4951B8288D3A6100C644B6 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
@ -286,7 +293,6 @@
7A49F50927D406CB00AEAAE0 /* MainSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainSettings.swift; sourceTree = "<group>"; };
7A49F50A27D406CB00AEAAE0 /* PlateNumber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlateNumber.swift; sourceTree = "<group>"; };
7A49F50B27D406CB00AEAAE0 /* VName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VName.swift; sourceTree = "<group>"; };
7A49F51427D40C6100AEAAE0 /* Shared.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Shared.xcdatamodel; sourceTree = "<group>"; };
7A558AA527FA3CCF001A18EE /* SettingsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = "<group>"; };
7A558AA627FA3CCF001A18EE /* ApiTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiTests.swift; sourceTree = "<group>"; };
7A558AA927FA3CCF001A18EE /* LoginMethodMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginMethodMock.swift; sourceTree = "<group>"; };
@ -367,6 +373,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7A91E8132986DE9C0008A982 /* RealmSwift in Frameworks */,
7A91E8112986DE9C0008A982 /* Realm in Frameworks */,
7A1D80E627F20FCB007BD64F /* DifferenceKit in Frameworks */,
7AE32D6E27F06D2D004EF6E0 /* SwiftDate in Frameworks */,
);
@ -523,6 +531,22 @@
path = Testing;
sourceTree = "<group>";
};
7A39D0782998FF1800A6F6FC /* Extensions */ = {
isa = PBXGroup;
children = (
7A39D0792998FF3500A6F6FC /* RealmDecoding.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
7A39D07B2999049F00A6F6FC /* Sidebar */ = {
isa = PBXGroup;
children = (
7A39D07C299904B700A6F6FC /* SidebarView.swift */,
);
path = Sidebar;
sourceTree = "<group>";
};
7A4951B5288D3A6100C644B6 /* AutoCat2SUI */ = {
isa = PBXGroup;
children = (
@ -550,6 +574,7 @@
7A4951C3288D3AF000C644B6 /* Screens */ = {
isa = PBXGroup;
children = (
7A39D07B2999049F00A6F6FC /* Sidebar */,
7AE9F20228CDFCAB00ABF6DF /* VehicleDetail */,
7A163BEF28BBE9DA0005A0A4 /* VehiclesList */,
7AF1D0D628BB5768004E19F7 /* CheckNumber */,
@ -582,6 +607,7 @@
7A054FD728C4BB700002C386 /* PlateView */,
7A4951B8288D3A6100C644B6 /* RootView.swift */,
7A4951D0288D5C4300C644B6 /* ACProgressView.swift */,
7A39D0762998F25600A6F6FC /* PlaceholderView.swift */,
);
path = Views;
sourceTree = "<group>";
@ -663,9 +689,9 @@
7A49F4D827D4064500AEAAE0 /* AutoCatCore */ = {
isa = PBXGroup;
children = (
7A39D0782998FF1800A6F6FC /* Extensions */,
7A36E55A27FB54610025AACB /* Testing */,
7AE32D6F27F06D87004EF6E0 /* DataSource */,
7A49F51327D40C6100AEAAE0 /* AutoCat2.xcdatamodeld */,
7A49F50427D406CB00AEAAE0 /* Models */,
7A49F4FF27D406C300AEAAE0 /* Services */,
7A49F4FC27D406BA00AEAAE0 /* ThirdParty */,
@ -973,6 +999,8 @@
packageProductDependencies = (
7AE32D6D27F06D2D004EF6E0 /* SwiftDate */,
7A1D80E527F20FCB007BD64F /* DifferenceKit */,
7A91E8102986DE9C0008A982 /* Realm */,
7A91E8122986DE9C0008A982 /* RealmSwift */,
);
productName = AutoCatCore;
productReference = 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */;
@ -1070,6 +1098,7 @@
7AE32D6C27F06D2D004EF6E0 /* XCRemoteSwiftPackageReference "SwiftDate" */,
7A1D80DE27F1F275007BD64F /* XCRemoteSwiftPackageReference "DifferenceKit" */,
7A1D80E427F20FCB007BD64F /* XCRemoteSwiftPackageReference "DifferenceKit" */,
7A91E80F2986DE9C0008A982 /* XCRemoteSwiftPackageReference "realm-swift" */,
);
productRefGroup = 7A49F4A027D4061900AEAAE0 /* Products */;
projectDirPath = "";
@ -1157,6 +1186,7 @@
buildActionMask = 2147483647;
files = (
7A4951D3288D5E2800C644B6 /* AuthVM.swift in Sources */,
7A39D07D299904B700A6F6FC /* SidebarView.swift in Sources */,
7A054FDA28C4C24B0002C386 /* CenterTextLayer.swift in Sources */,
7A163BF128BBE9ED0005A0A4 /* VehiclesListView.swift in Sources */,
7A054FD928C4BE560002C386 /* PlateView.swift in Sources */,
@ -1164,6 +1194,7 @@
7A4951C7288D3BDD00C644B6 /* MainView.swift in Sources */,
7AE9F20428CDFCC600ABF6DF /* VehicleDetailView.swift in Sources */,
7AF1D0D828BB577E004E19F7 /* CheckNumber.swift in Sources */,
7A39D0772998F25600A6F6FC /* PlaceholderView.swift in Sources */,
7A4951B7288D3A6100C644B6 /* AutoCat2SUIApp.swift in Sources */,
7A9471BD28C52DD80054CB0A /* PlateViewItem.swift in Sources */,
7AF1D0DA28BB5BF5004E19F7 /* View.swift in Sources */,
@ -1253,9 +1284,9 @@
7A49F50F27D406CB00AEAAE0 /* Response.swift in Sources */,
929EDE8127F8A75E00E55F65 /* VPhoto.swift in Sources */,
7A49F4FB27D406B200AEAAE0 /* Api.swift in Sources */,
7A49F51527D40C6100AEAAE0 /* AutoCat2.xcdatamodeld in Sources */,
929EDE7F27F89C3000E55F65 /* VEngine.swift in Sources */,
929EDE8727F8E11E00E55F65 /* VOsago.swift in Sources */,
7A39D07A2998FF3500A6F6FC /* RealmDecoding.swift in Sources */,
7A2B6CD427FCE93C00519F1E /* TestSettings.swift in Sources */,
7A49F50E27D406CB00AEAAE0 /* User.swift in Sources */,
7A36E56127FB5A330025AACB /* ApiMethodMockProtocol.swift in Sources */,
@ -1974,6 +2005,14 @@
minimumVersion = 5.0.0;
};
};
7A91E80F2986DE9C0008A982 /* XCRemoteSwiftPackageReference "realm-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/realm/realm-swift.git";
requirement = {
branch = master;
kind = branch;
};
};
7AE32D6C27F06D2D004EF6E0 /* XCRemoteSwiftPackageReference "SwiftDate" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/malcommac/SwiftDate";
@ -2005,6 +2044,16 @@
package = 7A48B26527D9442A004D1A4B /* XCRemoteSwiftPackageReference "PKHUD" */;
productName = PKHUD;
};
7A91E8102986DE9C0008A982 /* Realm */ = {
isa = XCSwiftPackageProductDependency;
package = 7A91E80F2986DE9C0008A982 /* XCRemoteSwiftPackageReference "realm-swift" */;
productName = Realm;
};
7A91E8122986DE9C0008A982 /* RealmSwift */ = {
isa = XCSwiftPackageProductDependency;
package = 7A91E80F2986DE9C0008A982 /* XCRemoteSwiftPackageReference "realm-swift" */;
productName = RealmSwift;
};
7A9FD4192857AF8D0057ECFA /* DifferenceKit */ = {
isa = XCSwiftPackageProductDependency;
package = 7A1D80DE27F1F275007BD64F /* XCRemoteSwiftPackageReference "DifferenceKit" */;
@ -2016,19 +2065,6 @@
productName = SwiftDate;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */
7A49F51327D40C6100AEAAE0 /* AutoCat2.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
7A49F51427D40C6100AEAAE0 /* Shared.xcdatamodel */,
);
currentVersion = 7A49F51427D40C6100AEAAE0 /* Shared.xcdatamodel */;
path = AutoCat2.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
/* End XCVersionGroup section */
};
rootObject = 7A49F49727D4061900AEAAE0 /* Project object */;
}

View File

@ -18,6 +18,24 @@
"version" : "5.4.0"
}
},
{
"identity" : "realm-core",
"kind" : "remoteSourceControl",
"location" : "https://github.com/realm/realm-core.git",
"state" : {
"revision" : "b77443ca7fa25407869ca537bf3ae912b1427bff",
"version" : "12.13.0"
}
},
{
"identity" : "realm-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/realm/realm-swift.git",
"state" : {
"branch" : "master",
"revision" : "f5aaf456ea725ac2f352b0b3decc78a7094adbd8"
}
},
{
"identity" : "swiftdate",
"kind" : "remoteSourceControl",

View File

@ -12,17 +12,17 @@
<key>AutoCat2.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>2</integer>
</dict>
<key>AutoCat2Mac.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
<integer>3</integer>
</dict>
<key>AutoCat2SUI.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>0</integer>
</dict>
<key>AutoCat2UITests.testExample.xcscheme</key>
<dict>
@ -41,14 +41,14 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>3</integer>
<integer>8</integer>
</dict>
<key>DifferenceKit (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>4</integer>
<integer>9</integer>
</dict>
<key>DifferenceKit (Playground) 3.xcscheme</key>
<dict>
@ -76,21 +76,42 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>2</integer>
<integer>7</integer>
</dict>
<key>GettingStarted (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>10</integer>
</dict>
<key>GettingStarted (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>11</integer>
</dict>
<key>GettingStarted (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>SwiftDate (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>6</integer>
<integer>5</integer>
</dict>
<key>SwiftDate (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>7</integer>
<integer>6</integer>
</dict>
<key>SwiftDate (Playground) 3.xcscheme</key>
<dict>
@ -118,11 +139,16 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>5</integer>
<integer>4</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>7A4951B3288D3A6100C644B6</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>7A49F4D627D4064500AEAAE0</key>
<dict>
<key>primary</key>

View File

@ -86,12 +86,8 @@ class VehicleCell: UITableViewCell {
extension VehicleCell: ConfigurableCell {
func configure(with vehicle: CDVehicle) {
guard let number = vehicle.number else {
return
}
plateView.number = PlateNumber(number)
func configure(with vehicle: Vehicle) {
plateView.number = PlateNumber(vehicle.number)
nameLabel.text = vehicle.brand?.name?.original ?? "<Unknown>"
if vehicle.unrecognized {

View File

@ -18,7 +18,7 @@ class HistoryController: UIViewController, UITableViewDelegate {
return table
}()
private var dataSource: CoreDataSource<CDVehicle, VehicleCell>?
private var dataSource: CoreDataSource<Vehicle, VehicleCell>?
override func viewDidLoad() {
super.viewDidLoad()

View File

@ -14,16 +14,12 @@ struct AutoCat2SUIApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
let storageService = StorageService.sharedNotWait
var body: some Scene {
WindowGroup {
if Testing.isUITesting {
RootView(settings: getTestSettings())
.environment(\.managedObjectContext, storageService.context)
} else {
RootView(settings: getSettings())
.environment(\.managedObjectContext, storageService.context)
}
}
}

View File

@ -29,7 +29,7 @@ struct CheckNumber: View {
}
.disabled(showProgress)
Button("Check") {
Task.init {
Task { @MainActor in
showProgress = true
do {
try await VehicleService.shared.check(plateNumber: plateNumber.uppercased(), force: false)

View File

@ -7,125 +7,33 @@
import SwiftUI
import AutoCatCore
struct SidebarSection: Identifiable {
let name: String
let filters: [Filter]
let id = UUID()
func contains(filter id: UUID) -> Bool {
filters.contains { $0.id == id }
}
}
import RealmSwift
struct MainView: View {
@FetchRequest(entity: CDVehicle.entity(), sortDescriptors: []) var vehicles: FetchedResults<CDVehicle>
@State private var selectedFilterId: UUID?
@State var selectedVehicle: CDVehicle?
@State private var checkSheetPresented = false
@State private var searchText = ""
@State var columnVisibility = NavigationSplitViewVisibility.all
private let sections: [SidebarSection] = [
SidebarSection(name: "History", filters: [
.allLocal,
.unrecognized,
.outdated
]),
SidebarSection(name: "Remote", filters: [
.allRemote
])
]
var selectedFilter: Filter? {
guard let filterId = selectedFilterId else {
return nil
}
return sections.first(where: { $0.contains(filter: filterId) })?
.filters.first(where: { $0.id == filterId })
}
@State var selectedFilter: Filter?
@State var selectedVehicle: Vehicle?
var body: some View {
NavigationSplitView(columnVisibility: $columnVisibility) {
List(selection: $selectedFilterId) {
ForEach(sections) { section in
Section(section.name) {
ForEach(section.filters) { filter in
Label(filter.name, systemImage: filter.iconName)
.badge(vehicles.filter(filter.match).count)
}
}
}
}
.navigationSplitViewColumnWidth(180)
.toolbar {
ToolbarItemGroup(placement: .primaryAction) {
Spacer()
Button {
checkSheetPresented = true
} label: {
Image(systemName: "plus")
}
.sheet(isPresented: $checkSheetPresented) {
CheckNumber()
}
}
}
SidebarView(selectedFilter: $selectedFilter)
} content: {
if let filter = selectedFilter {
let filtered = vehicles.filter(filter.match)
if filtered.isEmpty {
VStack {
Image(systemName: filter.iconName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 64)
.foregroundColor(.secondary)
Text("Nothing here")
.font(.system(size: 18))
.foregroundColor(.secondary)
}
} else {
VehiclesListView(vehicles: filtered, selection: $selectedVehicle)
.toolbar {
ToolbarItem {
Button(action: {}) {
Image(systemName: "line.3.horizontal.decrease.circle")
}
}
}
.navigationTitle(filter.name)
.navigationSubtitle("\(filtered.count) vehicles")
}
VehiclesListView(filter: filter, selection: $selectedVehicle)
} else {
EmptyView()
}
} detail: {
if let vehicle = selectedVehicle {
VehicleDetailView(vehicle: vehicle)
.toolbar {
ToolbarItem(placement: .automatic) {
Button(action: {}) {
Image(systemName: "square.and.arrow.up")
}
}
}
.navigationTitle(vehicle.brand?.name?.original ?? "")
.navigationSubtitle("\(vehicle.year)")
.searchable(text: $searchText, placement: .toolbar)
} else {
EmptyView()
}
}
.navigationSplitViewStyle(.balanced)
}
}

View File

@ -0,0 +1,74 @@
//
// SidebarView.swift
// AutoCat2SUI
//
// Created by Selim Mustafaev on 12.02.2023.
//
import SwiftUI
import AutoCatCore
import RealmSwift
struct SidebarSection: Identifiable {
let name: String
let filters: [Filter]
let id = UUID()
func contains(filter id: UUID) -> Bool {
filters.contains { $0.id == id }
}
}
struct SidebarView: View {
@ObservedResults(Vehicle.self) var vehicles
@Binding var selectedFilter: Filter?
@State private var checkSheetPresented = false
private let sections: [SidebarSection] = [
SidebarSection(name: "History", filters: [
.allLocal,
.unrecognized,
.outdated
]),
SidebarSection(name: "Remote", filters: [
.allRemote
])
]
var body: some View {
List(selection: $selectedFilter) {
ForEach(sections) { section in
Section(section.name) {
ForEach(section.filters, id: \.self) { filter in
Label(filter.name, systemImage: filter.iconName)
.badge(vehicles.filter(filter.match).count)
}
}
}
}
.navigationSplitViewColumnWidth(180)
.toolbar {
ToolbarItemGroup(placement: .primaryAction) {
Spacer()
Button {
checkSheetPresented = true
} label: {
Image(systemName: "plus")
}
.sheet(isPresented: $checkSheetPresented) {
CheckNumber()
}
}
}
}
}
struct SidebarView_Previews: PreviewProvider {
static var previews: some View {
SidebarView(selectedFilter: .constant(nil))
}
}

View File

@ -26,16 +26,16 @@ struct ReportTextItem: View {
struct VehicleDetailView: View {
let vehicle: CDVehicle
let vehicle: Vehicle
var body: some View {
Form {
Section(header: Text("General")) {
ReportTextItem(name: "Year", value: String(vehicle.year))
ReportTextItem(name: "Year", value: String(vehicle.year ?? 0))
ReportTextItem(name: "Color", value: vehicle.color)
ReportTextItem(name: "Category", value: vehicle.category)
ReportTextItem(name: "Steering wheel position", value: vehicle.isRightWheel ? "Right": "Left")
ReportTextItem(name: "Japanese", value: vehicle.isJapanese ? "Yes" : "No")
ReportTextItem(name: "Steering wheel position", value: vehicle.isRightWheel == true ? "Right": "Left")
ReportTextItem(name: "Japanese", value: vehicle.isJapanese == true ? "Yes" : "No")
}
Section(header: Text("Identifiers")) {
ReportTextItem(name: "Plate number", value: vehicle.number)
@ -52,6 +52,15 @@ struct VehicleDetailView: View {
}
}
.formStyle(.grouped)
.toolbar {
ToolbarItem(placement: .automatic) {
Button(action: {}) {
Image(systemName: "square.and.arrow.up")
}
}
}
.navigationTitle(vehicle.brand?.name?.original ?? "")
.navigationSubtitle("\(vehicle.year ?? 0)")
}
}
@ -64,7 +73,6 @@ struct VehicleDetailView_Previews: PreviewProvider {
addedDate: Date().timeIntervalSince1970,
updatedDate: Date().timeIntervalSince1970)
return VehicleDetailView(vehicle: CDVehicle(vehicle: test1,
context: NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)))
return VehicleDetailView(vehicle: test1)
}
}

View File

@ -7,27 +7,43 @@
import SwiftUI
import AutoCatCore
import RealmSwift
struct VehiclesListView: View {
var vehicles: [CDVehicle]
@Binding var selection: CDVehicle?
var filter: Filter
@Binding var selection: Vehicle?
@ObservedResults(Vehicle.self) var vehicles
var body: some View {
List(selection: $selection) {
ForEach(vehicles, id: \.self) { vehicle in
PlateViewItem(vehicle: vehicle)
let filtered: [Vehicle] = vehicles.filter(filter.match)
if filtered.isEmpty {
PlaceholderView(imageName: filter.iconName)
} else {
List(selection: $selection) {
ForEach(filtered, id: \.self) { vehicle in
PlateViewItem(vehicle: vehicle)
}
}
.toolbar {
ToolbarItem {
Button(action: {}) {
Image(systemName: "line.3.horizontal.decrease.circle")
}
}
}
.navigationTitle(filter.name)
.navigationSubtitle("\(filtered.count) vehicles")
}
}
}
struct VehiclesListView_Previews: PreviewProvider {
@State var selection: CDVehicle?
@State var selection: Vehicle?
static var previews: some View {
VehiclesListView(vehicles: [
], selection: .constant(nil))
VehiclesListView(filter: .allLocal, selection: .constant(nil))
}
}

View File

@ -0,0 +1,39 @@
//
// PlaceholderView.swift
// AutoCat2SUI
//
// Created by Selim Mustafaev on 12.02.2023.
//
import SwiftUI
struct PlaceholderView: View {
let imageName: String
let text: String
var body: some View {
VStack {
Image(systemName: imageName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 64)
.foregroundColor(.secondary)
Text(text)
.font(.system(size: 18))
.foregroundColor(.secondary)
}
}
init(imageName: String, text: String = "Nothing here") {
self.imageName = imageName
self.text = text
}
}
struct PlaceholderView_Previews: PreviewProvider {
static var previews: some View {
PlaceholderView(imageName: "eye", text: "Nothing here")
}
}

View File

@ -10,21 +10,21 @@ import AutoCatCore
struct PlateViewItem: View {
let vehicle: CDVehicle
let vehicle: Vehicle
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Text(vehicle.brand?.name?.original ?? "")
HStack {
PlateNumberView(number: PlateNumber(vehicle.number ?? ""),
PlateNumberView(number: PlateNumber(vehicle.number),
unrecognized: vehicle.unrecognized,
outdated: vehicle.outdated,
fontSize: 30)
VStack {
if let upDate = vehicle.updatedDate {
}
// if let upDate = vehicle.updatedDate {
//
// }
}
}
}
@ -41,7 +41,6 @@ struct PlateViewItem_Previews: PreviewProvider {
addedDate: Date().timeIntervalSince1970,
updatedDate: Date().timeIntervalSince1970)
return PlateViewItem(vehicle: CDVehicle(vehicle: test1,
context: NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)))
return PlateViewItem(vehicle: test1)
}
}

View File

@ -1,8 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Shared.xcdatamodel</string>
</dict>
<dict/>
</plist>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="20G80" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<entity name="DebugInfo" representedClassName=".CDDebugInfo" syncable="YES" codeGenerationType="class">
<relationship name="autocod" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DebugInfoEntry" inverseName="autocodInfo" inverseEntity="DebugInfoEntry"/>
<relationship name="nomerogram" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DebugInfoEntry" inverseName="nomerogramInfo" inverseEntity="DebugInfoEntry"/>
@ -131,19 +131,4 @@
<attribute name="url" optional="YES" attributeType="String"/>
<relationship name="vehicle" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Vehicle" inverseName="photos" inverseEntity="Vehicle"/>
</entity>
<elements>
<element name="VBrand" positionX="118.7528686523438" positionY="-209.1094360351562" width="128" height="74"/>
<element name="Vehicle" positionX="-145.9427490234375" positionY="-363.4805297851562" width="128" height="404"/>
<element name="VModel" positionX="-9" positionY="-207" width="128" height="59"/>
<element name="VName" positionX="-54" positionY="9" width="128" height="89"/>
<element name="VEngine" positionX="-9" positionY="-207" width="128" height="119"/>
<element name="VPhoto" positionX="0" positionY="-198" width="128" height="104"/>
<element name="VOwnershipPeriod" positionX="9" positionY="-189" width="128" height="209"/>
<element name="VEvent" positionX="18" positionY="-180" width="128" height="149"/>
<element name="VOsago" positionX="27" positionY="-171" width="128" height="194"/>
<element name="VAd" positionX="36" positionY="-162" width="128" height="179"/>
<element name="VNote" positionX="45" positionY="-153" width="128" height="104"/>
<element name="DebugInfoEntry" positionX="-9" positionY="-207" width="128" height="149"/>
<element name="DebugInfo" positionX="0" positionY="-198" width="128" height="119"/>
</elements>
</model>

View File

@ -0,0 +1,16 @@
//
// RealmDecoding.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 12.02.2023.
//
import RealmSwift
extension KeyedDecodingContainer {
func decode<T: Decodable>(_ type: Persisted<List<T>>.Type, forKey key: Key) throws -> Persisted<List<T>> {
// Use decode if present, falling back to an empty list
try decodeIfPresent(type, forKey: key) ?? Persisted<List<T>>(wrappedValue: List<T>())
}
}

View File

@ -26,7 +26,7 @@ public enum DataSource: CaseIterable, Identifiable {
}
}
public struct Filter: Hashable, Identifiable {
public final class Filter: Hashable, Identifiable {
public let id = UUID()
public let name: String
@ -49,9 +49,19 @@ public struct Filter: Hashable, Identifiable {
self.outdated = outdated
}
public func match(vehicle: CDVehicle) -> Bool {
public func match(vehicle: Vehicle) -> Bool {
return unrecognized == vehicle.unrecognized
&& outdated == vehicle.outdated
&& outdated == vehicle.outdated
}
public static func == (lhs: Filter, rhs: Filter) -> Bool {
lhs.dataSource == rhs.dataSource
&& lhs.unrecognized == rhs.unrecognized
&& lhs.outdated == rhs.outdated
}
public func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}

View File

@ -1,109 +1,46 @@
import Foundation
import CoreData
import RealmSwift
import DifferenceKit
public struct Vehicle: Decodable {
public final class Vehicle: Object, Decodable {
public var number: String
public var currentNumber: String?
public var brand: VBrand?
public var model: VModel?
public var addedDate: TimeInterval
public var updatedDate: TimeInterval
public var color: String?
public var year: Int64?
public var category: String?
public var vin1: String?
public var vin2: String?
public var sts: String?
public var pts: String?
public var isRightWheel: Bool?
public var isJapanese: Bool?
public var addedBy: String?
public var engine: VEngine?
public var photos: [VPhoto]?
public var ownershipPeriods: [VOwnershipPeriod]?
public var events: [VEvent]?
public var osagoContracts: [VOsago]?
public var ads: [VAd]?
public var notes: [VNote]?
public var debugInfo: DebugInfo?
@Persisted(primaryKey: true) public var number: String
@Persisted public var currentNumber: String?
@Persisted public var brand: VBrand?
@Persisted public var model: VModel?
@Persisted public var addedDate: TimeInterval
@Persisted public var updatedDate: TimeInterval
@Persisted public var color: String?
@Persisted public var year: Int64?
@Persisted public var category: String?
@Persisted public var vin1: String?
@Persisted public var vin2: String?
@Persisted public var sts: String?
@Persisted public var pts: String?
@Persisted public var isRightWheel: Bool?
@Persisted public var isJapanese: Bool?
@Persisted public var addedBy: String?
@Persisted public var engine: VEngine?
@Persisted public var photos: List<VPhoto>
@Persisted public var ownershipPeriods: List<VOwnershipPeriod>
@Persisted public var events: List<VEvent>
@Persisted public var osagoContracts: List<VOsago>
@Persisted public var ads: List<VAd>
@Persisted public var notes: List<VNote>
@Persisted public var debugInfo: DebugInfo?
public init(number: String, brandName: String, addedDate: TimeInterval, updatedDate: TimeInterval) {
public convenience init(number: String, brandName: String, addedDate: TimeInterval, updatedDate: TimeInterval) {
self.init()
self.number = number
self.currentNumber = number
self.addedDate = addedDate
self.updatedDate = updatedDate
self.brand = VBrand(name: VName(original: brandName))
}
}
extension CDVehicle: Dated {
public var updatedAt: Date {
Date(timeIntervalSince1970: updatedDate)
}
public var addedAt: Date {
Date(timeIntervalSince1970: addedDate)
}
}
extension CDVehicle: Differentiable {
public var differenceIdentifier: String { number! }
public func isContentEqual(to source: CDVehicle) -> Bool {
return number == source.number &&
addedDate == source.addedDate &&
updatedDate == source.updatedDate
}
}
extension CDVehicle {
public convenience init(vehicle: Vehicle, context: NSManagedObjectContext) {
let name = VName()
name.original = brandName
self.init(context: context)
self.number = vehicle.number
self.currentNumber = vehicle.currentNumber
self.addedDate = vehicle.addedDate/1000
self.updatedDate = vehicle.updatedDate/1000
self.color = vehicle.color
self.brand = CDVBrand(vbrand: vehicle.brand, context: context)
self.model = CDVModel(model: vehicle.model, context: context)
self.year = vehicle.year ?? 0
self.category = category
self.vin1 = vehicle.vin1
self.vin2 = vehicle.vin2
self.sts = vehicle.sts
self.pts = vehicle.pts
self.isRightWheel = vehicle.isRightWheel ?? false
self.isJapanese = vehicle.isJapanese ?? false
self.addedBy = vehicle.addedBy
self.synchronized = true
self.engine = CDVEngine(model: vehicle.engine, context: context)
let photos = vehicle.photos?.map { CDVPhoto(model: $0, context: context) } ?? []
self.photos = NSSet(array: photos)
let ownershipPeriods = vehicle.ownershipPeriods?.map { CDVOwnershipPeriod(model: $0, context: context) } ?? []
self.ownershipPeriods = NSSet(array: ownershipPeriods)
let events = vehicle.events?.map { CDVEvent(model: $0, context: context) } ?? []
self.events = NSSet(array: events)
let osagoContracts = vehicle.osagoContracts?.map { CDVOsago(model: $0, context: context) } ?? []
self.osagoContracts = NSSet(array: osagoContracts)
let ads = vehicle.ads?.map { CDVAd(model: $0, context: context) } ?? []
self.ads = NSSet(array: ads)
let notes = vehicle.notes?.map { CDVNote(model: $0, context: context) } ?? []
self.notes = NSSet(array: notes)
self.debugInfo = CDDebugInfo(model: vehicle.debugInfo, context: context)
let brand = VBrand()
brand.name = name
self.brand = brand
}
public var unrecognized: Bool {
@ -117,5 +54,32 @@ extension CDVehicle {
return false
}
}
}
extension Vehicle: Dated {
public var updatedAt: Date {
Date(timeIntervalSince1970: updatedDate)
}
public var addedAt: Date {
Date(timeIntervalSince1970: addedDate)
}
}
extension Vehicle: Differentiable {
public var differenceIdentifier: String { number }
public func isContentEqual(to source: Vehicle) -> Bool {
return number == source.number &&
addedDate == source.addedDate &&
updatedDate == source.updatedDate
}
}
extension Vehicle: Identifiable {
public var id: String { number }
}

View File

@ -1,31 +1,10 @@
//
// DebugInfo.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 03.04.2022.
//
import RealmSwift
import Foundation
import CoreData
public struct DebugInfo: Decodable {
public final class DebugInfo: Object, Decodable {
let autocod: DebugInfoEntry?
let vin01vin: DebugInfoEntry?
let vin01base: DebugInfoEntry?
let vin01history: DebugInfoEntry?
let nomerogram: DebugInfoEntry?
}
extension CDDebugInfo {
convenience init(model: DebugInfo?, context: NSManagedObjectContext) {
self.init(context: context)
self.autocod = CDDebugInfoEntry(model: model?.autocod, context: context)
self.vin01vin = CDDebugInfoEntry(model: model?.vin01vin, context: context)
self.vin01base = CDDebugInfoEntry(model: model?.vin01base, context: context)
self.vin01history = CDDebugInfoEntry(model: model?.vin01history, context: context)
self.nomerogram = CDDebugInfoEntry(model: model?.nomerogram, context: context)
}
@Persisted var autocod: DebugInfoEntry?
@Persisted var vin01vin: DebugInfoEntry?
@Persisted var vin01base: DebugInfoEntry?
@Persisted var vin01history: DebugInfoEntry?
@Persisted var nomerogram: DebugInfoEntry?
}

View File

@ -1,38 +1,15 @@
//
// DebugInfoEntry.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 03.04.2022.
//
import RealmSwift
import Foundation
import CoreData
public enum DebugInfoStatus: Int64 {
public enum DebugInfoStatus: Int64, Decodable, PersistableEnum {
case success = 0
case error = 1
case warning = 2
}
public struct DebugInfoEntry: Decodable {
public final class DebugInfoEntry: Object, Decodable {
let fields: Int64
let error: String?
let status: Int64
}
extension CDDebugInfoEntry {
convenience init(model: DebugInfoEntry?, context: NSManagedObjectContext) {
self.init(context: context)
self.fields = model?.fields ?? 0
self.error = model?.error
self.status = model?.status ?? 0
}
public var statusEnum: DebugInfoStatus {
get { DebugInfoStatus(rawValue: self.status)! }
set { self.status = newValue.rawValue }
}
@Persisted var fields: Int64
@Persisted var error: String?
@Persisted var status: DebugInfoStatus?
}

View File

@ -1,39 +1,15 @@
//
// VAd.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 02.04.2022.
//
import RealmSwift
import Foundation
import CoreData
public struct VAd: Decodable {
public final class VAd: Object, Decodable {
let id: Int64
let url: String?
let price: String?
let date: Double
let mileage: String?
let region: String?
let city: String?
let adDescription: String?
let photos: [String]
@Persisted var id: Int64
@Persisted var url: String?
@Persisted var price: String?
@Persisted var date: Double
@Persisted var mileage: String?
@Persisted var region: String?
@Persisted var city: String?
@Persisted var adDescription: String?
@Persisted var photos: List<String>
}
extension CDVAd {
convenience init(model: VAd?, context: NSManagedObjectContext) {
self.init(context: context)
self.id = model?.id ?? 0
self.url = model?.url
self.price = model?.price
self.date = model?.date ?? 0
self.mileage = model?.mileage
self.region = model?.region
self.city = model?.city
self.adDescription = model?.adDescription
self.photos = model?.photos
}
}

View File

@ -1,19 +1,7 @@
import Foundation
import CoreData
import RealmSwift
public struct VBrand: Decodable {
public var logo: String?
public var name: VName?
}
extension CDVBrand {
convenience init(vbrand: VBrand?, context: NSManagedObjectContext) {
self.init(context: context)
self.logo = vbrand?.logo
self.name = CDVName(vname: vbrand?.name, context: context)
}
public final class VBrand: Object, Decodable {
@Persisted public var logo: String?
@Persisted public var name: VName?
}

View File

@ -1,31 +1,10 @@
//
// VEngine.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 02.04.2022.
//
import RealmSwift
import Foundation
import CoreData
public struct VEngine: Decodable {
public final class VEngine: Object, Decodable {
let number: String?
let volume: Int64?
let powerHp: Float?
let powerKw: Float?
let fuelType: String?
}
extension CDVEngine {
convenience init(model: VEngine?, context: NSManagedObjectContext) {
self.init(context: context)
self.number = model?.number
self.volume = model?.volume ?? 0
self.powerHp = model?.powerHp ?? 0
self.powerKw = model?.powerKw ?? 0
self.fuelType = model?.fuelType
}
@Persisted public var number: String?
@Persisted public var volume: Int64?
@Persisted public var powerHp: Float?
@Persisted public var powerKw: Float?
@Persisted public var fuelType: String?
}

View File

@ -1,35 +1,10 @@
//
// VEvent.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 02.04.2022.
//
import RealmSwift
import Foundation
import CoreData
public struct VEvent: Decodable {
public final class VEvent: Object, Decodable {
let id: String
let date: Double
let latitude: Double
let longitude: Double
let speed: Double
let direction: Double
let address: String?
}
extension CDVEvent {
convenience init(model: VEvent?, context: NSManagedObjectContext) {
self.init(context: context)
self.id = model?.id
self.date = model?.date ?? 0
self.latitude = model?.latitude ?? 0
self.longitude = model?.longitude ?? 0
self.speed = model?.speed ?? 0
self.direction = model?.direction ?? 0
self.address = model?.address
}
@Persisted var id: String
@Persisted var date: Double
@Persisted var latitude: Double
@Persisted var longitude: Double
@Persisted var address: String?
}

View File

@ -1,22 +1,6 @@
//
// VModel.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 29.03.2022.
//
import RealmSwift
import Foundation
import CoreData
public struct VModel: Decodable {
public final class VModel: Object, Decodable {
let name: VName?
}
extension CDVModel {
convenience init(model: VModel?, context: NSManagedObjectContext) {
self.init(context: context)
self.name = CDVName(vname: model?.name, context: context)
}
@Persisted var name: VName?
}

View File

@ -1,19 +1,7 @@
import Foundation
import CoreData
import RealmSwift
public struct VName: Decodable {
public var normalized: String?
public var original: String?
}
extension CDVName {
convenience init(vname: VName?, context: NSManagedObjectContext) {
self.init(context: context)
self.normalized = vname?.normalized
self.original = vname?.original
}
public final class VName: Object, Decodable {
@Persisted public var normalized: String?
@Persisted public var original: String?
}

View File

@ -1,29 +1,9 @@
//
// VNote.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 02.04.2022.
//
import RealmSwift
import Foundation
import CoreData
public struct VNote: Decodable {
public final class VNote: Object, Decodable {
let id: String?
let user: String?
let date: Double
let text: String?
}
extension CDVNote {
convenience init(model: VNote?, context: NSManagedObjectContext) {
self.init(context: context)
self.id = model?.id
self.user = model?.user
self.date = model?.date ?? 0
self.text = model?.text
}
@Persisted var id: String?
@Persisted var user: String?
@Persisted var date: Double
@Persisted var text: String?
}

View File

@ -1,41 +1,15 @@
//
// VOsago.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 02.04.2022.
//
import RealmSwift
import Foundation
import CoreData
public struct VOsago: Decodable {
public final class VOsago: Object, Decodable {
let date: Double
let number: String?
let vin: String?
let plateNumber: String?
let name: String?
let status: String?
let restrictions: String?
let insurant: String?
let owner: String?
let usageRegion: String?
}
extension CDVOsago {
convenience init(model: VOsago?, context: NSManagedObjectContext) {
self.init(context: context)
self.date = model?.date ?? 0
self.number = model?.number
self.vin = model?.vin
self.plateNumber = model?.plateNumber
self.name = model?.name
self.status = model?.status
self.restrictions = model?.restrictions
self.insurant = model?.insurant
self.owner = model?.owner
self.usageRegion = model?.usageRegion
}
@Persisted var date: Double
@Persisted var number: String?
@Persisted var vin: String?
@Persisted var plateNumber: String?
@Persisted var name: String?
@Persisted var status: String?
@Persisted var restrictions: String?
@Persisted var insurant: String?
@Persisted var owner: String?
@Persisted var usageRegion: String?
}

View File

@ -1,43 +1,16 @@
//
// VOwnershipPeriod.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 02.04.2022.
//
import RealmSwift
import Foundation
import CoreData
public struct VOwnershipPeriod: Decodable {
public final class VOwnershipPeriod: Object, Decodable {
let lastOperation: String?
let ownerType: String?
let from: Int64?
let to: Int64?
let region: String?
let registrationRegion: String?
let locality: String?
let code: String?
let street: String?
let building: String?
let inn: String?
}
extension CDVOwnershipPeriod {
convenience init(model: VOwnershipPeriod?, context: NSManagedObjectContext) {
self.init(context: context)
self.lastOperation = model?.lastOperation
self.ownerType = model?.ownerType
self.from = model?.from ?? 0
self.to = model?.to ?? 0
self.region = model?.region
self.registrationRegion = model?.registrationRegion
self.locality = model?.locality
self.code = model?.locality
self.street = model?.street
self.building = model?.building
self.inn = model?.inn
}
@Persisted var lastOperation: String?
@Persisted var ownerType: String?
@Persisted var from: Int64?
@Persisted var to: Int64?
@Persisted var region: String?
@Persisted var registrationRegion: String?
@Persisted var locality: String?
@Persisted var code: String?
@Persisted var street: String?
@Persisted var building: String?
@Persisted var inn: String?
}

View File

@ -1,29 +1,9 @@
//
// VPhoto.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 02.04.2022.
//
import RealmSwift
import Foundation
import CoreData
public struct VPhoto: Decodable {
public final class VPhoto: Object, Decodable {
let brand: String?
let model: String?
let date: Double
let url: String?
}
extension CDVPhoto {
convenience init(model: VPhoto?, context: NSManagedObjectContext) {
self.init(context: context)
self.brand = model?.brand
self.model = model?.model
self.date = model?.date ?? 0
self.url = model?.url
}
@Persisted var brand: String?
@Persisted var model: String?
@Persisted var date: Double
@Persisted var url: String?
}

View File

@ -1,91 +1,57 @@
import Foundation
import CoreData
import RealmSwift
protocol StorageServiceProtocol {
var context: NSManagedObjectContext { get }
func store(vehicle: Vehicle) throws -> CDVehicle
func store(vehicle: Vehicle) throws
}
public class StorageService: StorageServiceProtocol {
public enum StorageError: String, LocalizedError {
case openDatabaseError = "Ошибка открытия БД"
public var errorDescription: String? {
rawValue
}
}
private static var instance: StorageService?
private let container: NSPersistentCloudKitContainer
private let realm: Realm
public static var shared: StorageService {
get async throws {
get throws {
if let instance = StorageService.instance {
return instance
} else {
let service = StorageService(inMemory: Testing.isUITesting)
try await service.loadPersistentStores()
StorageService.instance = service
return service
}
}
}
public static var sharedNotWait: StorageService {
if let instance = StorageService.instance {
return instance
} else {
let service = StorageService(inMemory: Testing.isUITesting)
Task {
try? await service.loadPersistentStores()
}
StorageService.instance = service
return service
}
}
init(inMemory: Bool = false) {
let bundle = Bundle(for: Self.self)
let url = bundle.url(forResource: "AutoCat2", withExtension: "momd")
let mom = NSManagedObjectModel(contentsOf: url!)
container = NSPersistentCloudKitContainer(name: "AutoCat2", managedObjectModel: mom!)
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
}
private func loadPersistentStores() async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
continuation.resume(throwing: error)
if let service = StorageService(inMemory: Testing.isUITesting) {
StorageService.instance = service
return service
} else {
continuation.resume(returning: ())
throw StorageError.openDatabaseError
}
})
}
}
}
private func save() throws {
guard context.hasChanges else {
return
init?(inMemory: Bool = false) {
let config = Realm.Configuration(inMemoryIdentifier: inMemory ? "memory" : nil)
guard let realm = try? Realm(configuration: config) else {
return nil
}
do {
try context.save()
} catch {
context.rollback()
throw error
}
self.realm = realm
}
// MARK: - StorageServiceProtocol
public var context: NSManagedObjectContext {
container.viewContext
}
public func store(vehicle: Vehicle) throws -> CDVehicle {
public func store(vehicle: Vehicle) throws {
let cdVehicle = CDVehicle(vehicle: vehicle, context: context)
try save()
return cdVehicle
try realm.write {
realm.add(vehicle, update: .all)
}
}
}

View File

@ -2,7 +2,7 @@ import Foundation
protocol VehicleServiceProtocol {
func check(plateNumber: String, force: Bool) async throws -> CDVehicle
func check(plateNumber: String, force: Bool) async throws -> Vehicle
}
public class VehicleService: VehicleServiceProtocol {
@ -11,8 +11,8 @@ public class VehicleService: VehicleServiceProtocol {
private let storage: StorageServiceProtocol
public static var shared: VehicleService {
get async throws {
let storageService = try await StorageService.shared
get throws {
let storageService = try StorageService.shared
return VehicleService(api: Api.shared, storage: storageService)
}
}
@ -25,11 +25,13 @@ public class VehicleService: VehicleServiceProtocol {
// MARK: - VehicleServiceProtocol
@MainActor
@discardableResult
public func check(plateNumber: String, force: Bool) async throws -> CDVehicle {
public func check(plateNumber: String, force: Bool) async throws -> Vehicle {
let vehicle = try await api.check(plateNumber: plateNumber, force: force)
return try storage.store(vehicle: vehicle)
try storage.store(vehicle: vehicle)
return vehicle
}
}