#9 Distance Calculation using CoreLocation - RxSwift MVVM Coordinator iOS App
CoreLocation
을 통한 현재 위치 및 거리 측정 로직 구현final class LocationService: NSObject {
static let shared = LocationService()
private let manager = CLLocationManager()
private let currentLocationRelay: BehaviorRelay<(lat: Double, lon: Double)?> = .init(value: nil)
lazy var currentLocation: Observable<(lat: Double, lon: Double)?> = self.currentLocationRelay.asObservable().share(replay: 1, scope: .forever)
private override init() {
super.init()
setLocationService()
}
private func setLocationService() {
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
manager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
manager.startUpdatingLocation()
}
}
deinit {
manager.stopUpdatingLocation()
}
}
extension LocationService: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
let currentLocation = (lat: location.coordinate.latitude, lon: location.coordinate.longitude)
currentLocationRelay.accept(currentLocation)
manager.stopUpdatingLocation()
}
}
currentLocationRelay
는 BehaviorRelay
타입으로 지역을 표현하는 튜플을 담아두는 릴레이Observable
을 통해 해당 지역 튜플 데이터 관찰 가능didUpdateLocations
를 통해 건네받은 현재 위치를 currentLocationRelay
로 넘기기override func start() {
let vc = AirportsViewController()
let locationService = LocationService.shared
vc.viewModelBuilder = { [models] in
let title = models.first?.city ?? ""
return AirportsViewModel(input: $0, dependencies: (title: title, models: models, currentLocation: locationService.currentLocation))
}
self.navigationController.pushViewController(vc, animated: true)
}
AirportsCoordinator
에서 start()
를 통해 다른 뷰 컨트롤러를 시동하는 구조dependencies
를 통해 뷰 모델을 빌드하고 있는데, 현 시점의 currentLocation
을 읽어오는 곳이 바로 현 시점.private static func getDistance(airportLocation: (lat: Double?, lon: Double?),
currentLocation: (lat: Double, lon: Double)) -> Double? {
guard
let lat = airportLocation.lat,
let lon = airportLocation.lon else { return nil }
let current = CLLocation(latitude: currentLocation.lat, longitude: currentLocation.lon)
let airport = CLLocation(latitude: lat, longitude: lon)
return current.distance(from: airport)
}
static
함수self.distance = AirportViewModel.getDistance(airportLocation: (lat: Double(model.lat), lon: Double(model.lon)), currentLocation: currentLocation)
distance
값이 자동으로 계산extension AirportViewModel: Comparable {
static func < (lhs: AirportViewModel, rhs: AirportViewModel) -> Bool {
return lhs.distance ?? 0 < rhs.distance ?? 0
}
static func == (lhs: AirportViewModel, rhs: AirportViewModel) -> Bool {
return lhs.code == rhs.code
}
}
Comparable
프로토콜을 준수하는 뷰 모델은 <
, ==
함수를 적용했기 때문에 이후 여러 개의 거리를 가진 뷰 모델이 테이블 뷰에 들어오기 전 sorted
로 정렬 가능한 형태