Adding initial LocationService (reverse geocoding) and its tests
This commit is contained in:
parent
e3cf3aa7cf
commit
299ee23992
@ -64,6 +64,9 @@
|
|||||||
7A5D84C22C1AE5C900C2209B /* VehicleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84C12C1AE5C900C2209B /* VehicleModel.swift */; };
|
7A5D84C22C1AE5C900C2209B /* VehicleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84C12C1AE5C900C2209B /* VehicleModel.swift */; };
|
||||||
7A5D84C42C1AE65C00C2209B /* VehicleBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84C32C1AE65C00C2209B /* VehicleBrand.swift */; };
|
7A5D84C42C1AE65C00C2209B /* VehicleBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84C32C1AE65C00C2209B /* VehicleBrand.swift */; };
|
||||||
7A5D84C62C1AE72E00C2209B /* VehicleName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84C52C1AE72E00C2209B /* VehicleName.swift */; };
|
7A5D84C62C1AE72E00C2209B /* VehicleName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84C52C1AE72E00C2209B /* VehicleName.swift */; };
|
||||||
|
7A60D24D2C5A9D4900D13F7B /* LocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A60D24C2C5A9D4900D13F7B /* LocationService.swift */; };
|
||||||
|
7A60D24F2C5A9DA800D13F7B /* LocationServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A60D24E2C5A9DA800D13F7B /* LocationServiceProtocol.swift */; };
|
||||||
|
7A60D2512C5A9E4200D13F7B /* GeocoderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A60D2502C5A9E4200D13F7B /* GeocoderProtocol.swift */; };
|
||||||
7A61FF8B2575A2CD00D905D5 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7A61FF892575A2CD00D905D5 /* Localizable.strings */; };
|
7A61FF8B2575A2CD00D905D5 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7A61FF892575A2CD00D905D5 /* Localizable.strings */; };
|
||||||
7A61FF912575A5B300D905D5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7A61FF8F2575A5B300D905D5 /* InfoPlist.strings */; };
|
7A61FF912575A5B300D905D5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7A61FF8F2575A5B300D905D5 /* InfoPlist.strings */; };
|
||||||
7A61FFA0257D3CFC00D905D5 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 7A61FFA2257D3CFC00D905D5 /* Localizable.stringsdict */; };
|
7A61FFA0257D3CFC00D905D5 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 7A61FFA2257D3CFC00D905D5 /* Localizable.stringsdict */; };
|
||||||
@ -301,6 +304,9 @@
|
|||||||
7A5D84C12C1AE5C900C2209B /* VehicleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleModel.swift; sourceTree = "<group>"; };
|
7A5D84C12C1AE5C900C2209B /* VehicleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleModel.swift; sourceTree = "<group>"; };
|
||||||
7A5D84C32C1AE65C00C2209B /* VehicleBrand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleBrand.swift; sourceTree = "<group>"; };
|
7A5D84C32C1AE65C00C2209B /* VehicleBrand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleBrand.swift; sourceTree = "<group>"; };
|
||||||
7A5D84C52C1AE72E00C2209B /* VehicleName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleName.swift; sourceTree = "<group>"; };
|
7A5D84C52C1AE72E00C2209B /* VehicleName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleName.swift; sourceTree = "<group>"; };
|
||||||
|
7A60D24C2C5A9D4900D13F7B /* LocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationService.swift; sourceTree = "<group>"; };
|
||||||
|
7A60D24E2C5A9DA800D13F7B /* LocationServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationServiceProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
7A60D2502C5A9E4200D13F7B /* GeocoderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeocoderProtocol.swift; sourceTree = "<group>"; };
|
||||||
7A61FF8325759DE700D905D5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
|
7A61FF8325759DE700D905D5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
|
||||||
7A61FF8A2575A2CD00D905D5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
7A61FF8A2575A2CD00D905D5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
7A61FF8D2575A2F900D905D5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
7A61FF8D2575A2F900D905D5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
@ -657,6 +663,7 @@
|
|||||||
7A45FB362C2706D000618694 /* Services */ = {
|
7A45FB362C2706D000618694 /* Services */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7A60D24B2C5A9D2700D13F7B /* LocationService */,
|
||||||
7AB5873D2C42FF4000FA7B66 /* ApiService */,
|
7AB5873D2C42FF4000FA7B66 /* ApiService */,
|
||||||
7AB587302C42D35900FA7B66 /* StorageService */,
|
7AB587302C42D35900FA7B66 /* StorageService */,
|
||||||
);
|
);
|
||||||
@ -705,6 +712,16 @@
|
|||||||
path = Realm;
|
path = Realm;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
7A60D24B2C5A9D2700D13F7B /* LocationService */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
7A60D24C2C5A9D4900D13F7B /* LocationService.swift */,
|
||||||
|
7A60D24E2C5A9DA800D13F7B /* LocationServiceProtocol.swift */,
|
||||||
|
7A60D2502C5A9E4200D13F7B /* GeocoderProtocol.swift */,
|
||||||
|
);
|
||||||
|
path = LocationService;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
7A64A2012C19D99D00284124 /* DTO */ = {
|
7A64A2012C19D99D00284124 /* DTO */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -1303,6 +1320,8 @@
|
|||||||
7A5D84C62C1AE72E00C2209B /* VehicleName.swift in Sources */,
|
7A5D84C62C1AE72E00C2209B /* VehicleName.swift in Sources */,
|
||||||
7A64A2122C19E2A100284124 /* VehicleModelDto.swift in Sources */,
|
7A64A2122C19E2A100284124 /* VehicleModelDto.swift in Sources */,
|
||||||
7AF6D21F2677C1680086EA64 /* Response.swift in Sources */,
|
7AF6D21F2677C1680086EA64 /* Response.swift in Sources */,
|
||||||
|
7A60D24D2C5A9D4900D13F7B /* LocationService.swift in Sources */,
|
||||||
|
7A60D24F2C5A9DA800D13F7B /* LocationServiceProtocol.swift in Sources */,
|
||||||
7A761C07267E8E7F0005F28F /* AnyEncodable.swift in Sources */,
|
7A761C07267E8E7F0005F28F /* AnyEncodable.swift in Sources */,
|
||||||
7A64A2032C19DA1000284124 /* VehicleDto.swift in Sources */,
|
7A64A2032C19DA1000284124 /* VehicleDto.swift in Sources */,
|
||||||
7AB587322C42D38E00FA7B66 /* StorageServiceProtocol.swift in Sources */,
|
7AB587322C42D38E00FA7B66 /* StorageServiceProtocol.swift in Sources */,
|
||||||
@ -1310,6 +1329,7 @@
|
|||||||
7A5D84BC2C1AD81000C2209B /* VehicleOwnershipPeriod.swift in Sources */,
|
7A5D84BC2C1AD81000C2209B /* VehicleOwnershipPeriod.swift in Sources */,
|
||||||
7A64A2202C19E93500284124 /* VehicleNoteDto.swift in Sources */,
|
7A64A2202C19E93500284124 /* VehicleNoteDto.swift in Sources */,
|
||||||
7AF6D21A2677C1680086EA64 /* User.swift in Sources */,
|
7AF6D21A2677C1680086EA64 /* User.swift in Sources */,
|
||||||
|
7A60D2512C5A9E4200D13F7B /* GeocoderProtocol.swift in Sources */,
|
||||||
7A64A21C2C19E87B00284124 /* OsagoDto.swift in Sources */,
|
7A64A21C2C19E87B00284124 /* OsagoDto.swift in Sources */,
|
||||||
7AF6D21D2677C1680086EA64 /* Osago.swift in Sources */,
|
7AF6D21D2677C1680086EA64 /* Osago.swift in Sources */,
|
||||||
7AF6D2152677C1680086EA64 /* Settings.swift in Sources */,
|
7AF6D2152677C1680086EA64 /* Settings.swift in Sources */,
|
||||||
|
|||||||
16
AutoCatCore/Services/LocationService/GeocoderProtocol.swift
Normal file
16
AutoCatCore/Services/LocationService/GeocoderProtocol.swift
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// GeocoderProtocol.swift
|
||||||
|
// AutoCatCore
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 31.07.2024.
|
||||||
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
public protocol GeocoderProtocol {
|
||||||
|
|
||||||
|
func reverseGeocodeLocation(_ location: CLLocation) async throws -> [CLPlacemark]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension CLGeocoder: GeocoderProtocol { }
|
||||||
31
AutoCatCore/Services/LocationService/LocationService.swift
Normal file
31
AutoCatCore/Services/LocationService/LocationService.swift
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// LocationService.swift
|
||||||
|
// AutoCatCore
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 31.07.2024.
|
||||||
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
public final class LocationService: LocationServiceProtocol {
|
||||||
|
|
||||||
|
private var geocoder: GeocoderProtocol
|
||||||
|
|
||||||
|
public init(geocoder: GeocoderProtocol) {
|
||||||
|
self.geocoder = geocoder
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getAddressForLocation(latitude: Double, longitude: Double) async throws -> String {
|
||||||
|
|
||||||
|
let location = CLLocation(latitude: latitude, longitude: longitude)
|
||||||
|
let placemarks = try await geocoder.reverseGeocodeLocation(location)
|
||||||
|
|
||||||
|
if let name = placemarks.first?.name {
|
||||||
|
return name
|
||||||
|
} else {
|
||||||
|
throw LocationError.reverseGeocode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// LocationServiceProtocol.swift
|
||||||
|
// AutoCatCore
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 31.07.2024.
|
||||||
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol LocationServiceProtocol {
|
||||||
|
|
||||||
|
func getAddressForLocation(latitude: Double, longitude: Double) async throws -> String
|
||||||
|
}
|
||||||
@ -2,13 +2,13 @@ import Foundation
|
|||||||
import CoreLocation
|
import CoreLocation
|
||||||
import SwiftLocation
|
import SwiftLocation
|
||||||
|
|
||||||
enum LocationError: LocalizedError {
|
public enum LocationError: LocalizedError {
|
||||||
|
|
||||||
case generic
|
case generic
|
||||||
case permission
|
case permission
|
||||||
case reverseGeocode
|
case reverseGeocode
|
||||||
|
|
||||||
var errorDescription: String? {
|
public var errorDescription: String? {
|
||||||
switch self {
|
switch self {
|
||||||
case .generic: "Location error"
|
case .generic: "Location error"
|
||||||
case .permission: "Location permission error"
|
case .permission: "Location permission error"
|
||||||
|
|||||||
61
AutoCatCoreTests/LocationServiceTests.swift
Normal file
61
AutoCatCoreTests/LocationServiceTests.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// LocationServiceTests.swift
|
||||||
|
// AutoCatCoreTests
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 01.08.2024.
|
||||||
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Testing
|
||||||
|
import CoreLocation
|
||||||
|
import AutoCatCore
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
struct LocationServiceTests {
|
||||||
|
|
||||||
|
let latitude: CLLocationDegrees = 10
|
||||||
|
let longitude: CLLocationDegrees = 10
|
||||||
|
let address = "Test Address"
|
||||||
|
|
||||||
|
let geocoder = GeocoderMock()
|
||||||
|
let locationService: LocationService
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.locationService = LocationService(geocoder: geocoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func getValidAddress() async throws {
|
||||||
|
|
||||||
|
geocoder.addLocation(latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
address: address)
|
||||||
|
|
||||||
|
let result = try await locationService.getAddressForLocation(latitude: latitude,
|
||||||
|
longitude: longitude)
|
||||||
|
|
||||||
|
#expect(result == address)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func getNilAddress() async throws {
|
||||||
|
|
||||||
|
geocoder.addLocation(latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
address: nil)
|
||||||
|
|
||||||
|
await #expect(throws: LocationError.reverseGeocode) {
|
||||||
|
_ = try await locationService.getAddressForLocation(latitude: latitude,
|
||||||
|
longitude: longitude)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func addressNotFound() async throws {
|
||||||
|
|
||||||
|
await #expect(throws: LocationError.reverseGeocode) {
|
||||||
|
_ = try await locationService.getAddressForLocation(latitude: latitude,
|
||||||
|
longitude: longitude)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
AutoCatCoreTests/Mocks/GeocoderMock.swift
Normal file
51
AutoCatCoreTests/Mocks/GeocoderMock.swift
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// GeocoderMock.swift
|
||||||
|
// AutoCatCoreTests
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 31.07.2024.
|
||||||
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreLocation
|
||||||
|
import AutoCatCore
|
||||||
|
import Intents
|
||||||
|
import Contacts
|
||||||
|
|
||||||
|
final class GeocoderMock {
|
||||||
|
|
||||||
|
struct Location {
|
||||||
|
|
||||||
|
let latitude: CLLocationDegrees
|
||||||
|
let longitude: CLLocationDegrees
|
||||||
|
let address: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
var locations: [Location] = []
|
||||||
|
|
||||||
|
func addLocation(latitude: CLLocationDegrees, longitude: CLLocationDegrees, address: String?) {
|
||||||
|
|
||||||
|
locations.append(Location(latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
address: address))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension GeocoderMock: GeocoderProtocol {
|
||||||
|
|
||||||
|
func reverseGeocodeLocation(_ location: CLLocation) async throws -> [CLPlacemark] {
|
||||||
|
|
||||||
|
let first = locations.first {
|
||||||
|
$0.latitude == location.coordinate.latitude && $0.longitude == location.coordinate.longitude
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let first else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
let placemark = CLPlacemark(location: CLLocation(latitude: first.latitude, longitude: first.longitude),
|
||||||
|
name: first.address,
|
||||||
|
postalAddress: nil)
|
||||||
|
|
||||||
|
return [placemark]
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user