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 */; };
|
||||
7A5D84C42C1AE65C00C2209B /* VehicleBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84C32C1AE65C00C2209B /* VehicleBrand.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 */; };
|
||||
7A61FF912575A5B300D905D5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7A61FF8F2575A5B300D905D5 /* InfoPlist.strings */; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -657,6 +663,7 @@
|
||||
7A45FB362C2706D000618694 /* Services */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A60D24B2C5A9D2700D13F7B /* LocationService */,
|
||||
7AB5873D2C42FF4000FA7B66 /* ApiService */,
|
||||
7AB587302C42D35900FA7B66 /* StorageService */,
|
||||
);
|
||||
@ -705,6 +712,16 @@
|
||||
path = Realm;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7A60D24B2C5A9D2700D13F7B /* LocationService */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A60D24C2C5A9D4900D13F7B /* LocationService.swift */,
|
||||
7A60D24E2C5A9DA800D13F7B /* LocationServiceProtocol.swift */,
|
||||
7A60D2502C5A9E4200D13F7B /* GeocoderProtocol.swift */,
|
||||
);
|
||||
path = LocationService;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7A64A2012C19D99D00284124 /* DTO */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1303,6 +1320,8 @@
|
||||
7A5D84C62C1AE72E00C2209B /* VehicleName.swift in Sources */,
|
||||
7A64A2122C19E2A100284124 /* VehicleModelDto.swift in Sources */,
|
||||
7AF6D21F2677C1680086EA64 /* Response.swift in Sources */,
|
||||
7A60D24D2C5A9D4900D13F7B /* LocationService.swift in Sources */,
|
||||
7A60D24F2C5A9DA800D13F7B /* LocationServiceProtocol.swift in Sources */,
|
||||
7A761C07267E8E7F0005F28F /* AnyEncodable.swift in Sources */,
|
||||
7A64A2032C19DA1000284124 /* VehicleDto.swift in Sources */,
|
||||
7AB587322C42D38E00FA7B66 /* StorageServiceProtocol.swift in Sources */,
|
||||
@ -1310,6 +1329,7 @@
|
||||
7A5D84BC2C1AD81000C2209B /* VehicleOwnershipPeriod.swift in Sources */,
|
||||
7A64A2202C19E93500284124 /* VehicleNoteDto.swift in Sources */,
|
||||
7AF6D21A2677C1680086EA64 /* User.swift in Sources */,
|
||||
7A60D2512C5A9E4200D13F7B /* GeocoderProtocol.swift in Sources */,
|
||||
7A64A21C2C19E87B00284124 /* OsagoDto.swift in Sources */,
|
||||
7AF6D21D2677C1680086EA64 /* Osago.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 SwiftLocation
|
||||
|
||||
enum LocationError: LocalizedError {
|
||||
public enum LocationError: LocalizedError {
|
||||
|
||||
case generic
|
||||
case permission
|
||||
case reverseGeocode
|
||||
|
||||
var errorDescription: String? {
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .generic: "Location 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