코딩 배경
fireStore로부터 주소 데이터를 패치하여 CoreLocation 프래임워크의 메소드를 사용해 좌표 데이터로 변환하는 코드를 작성하였습니다.
trouble 정의
주소 데이터를 좌표 데이터를 변환하기 전 주소 데이터를 사전에 받아 놓고 있어야한다.
하지만 네트워크 문제로 인해 데이터를 패치하기 이전 UI를 변경하기 때문에 값이 없는 nil값으로 UI를 처리하게 되고 문제가 발생하게 됩니다.
trouble shooting
위의 문제를 해결하기 위해서 정상적으로 주소 데이터를 받은 후에 좌표 데이터로 변경하는 작업을 비동기적으로 처리해야합니다.
다시 말해, UI를 업데이트하는 시점 이전 정상적으로 주소 데이터를 패치 후에 진행합니다.
주소(String) 데이터를 좌표(CLLocationCoordinate2D)데이터로 변환
import CoreLocation
//CLGeocoder = 기능 인터페이스
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
if let error = error {
print("Geocoding error: \(error.localizedDescription)")
return
}
if let placemark = placemarks?.first {
let location = placemark.location
if let coordinate = location?.coordinate {
}
}
}
geocodeAddressString 메소드는 address String 타입의 주소 데이터를 인자로 위치 데이터가 CLPlacemark 타입 placemark에 변수를 전달 받게 됩니다.
CLPlacemark의 인스턴스 프로퍼티들은 "해당 지역에 속해있는 국가의 이름", "우편번호", "국가번호"등 해당 좌표에 관련된 정보를 가지고 있습니다.
저는 pin을 찍기 위해 좌표 데이터가 필요하였고 placemark 내 location 그리고 location 내 coordinate 좌표 데이터를 가지고 있는 프로퍼티에 접근했습니다.
RxSwift를 활용한 비동기 처리
Observable 생성 (coordinate를 관찰)
//convertAddressToCoordinates 메소드에서 얻게 되는 좌표 데이터를 관찰!!
private func convertAddressToCoordinates(address: String) -> Observable<CLLocationCoordinate2D> {
return Observable.create { observer in
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
if let error = error {
print("Geocoding error: \(error.localizedDescription)")
return
}
if let placemark = placemarks?.first {
let location = placemark.location
if let coordinate = location?.coordinate {
//주소 데이터에서 좌표 데이터로 변환된 데이터를 구독자들에게 emit!
observer.onNext(coordinate)
} else if let error = error {
observer.onError(error)
}
}
}
return Disposables.create()
}
}
Observable 구독하기(observer가 coordinate를 발견한다면 요렇게 대처하세요~)
private func enterCoordinate(){
for idx in 0..<newLocations.count {
var currentLocation = newLocations[idx]
var address = currentLocation.address
convertAddressToCoordinates(address: address)
//observer가 코드를 수행할 스케쥴러(main 스케줄러)지정
.observe(on: MainScheduler.instance)
.subscribe(onNext: {
//observer가 coordinate 발견!!
coordinate in
//발견 후 대처
//객체 내 longitude, latitude 프로퍼티에 해당 값을 할당합니다.
currentLocation.latitude = coordinate.latitude
currentLocation.longitude = coordinate.longitude
//업데이트된 해당 객체로 pin을 찍습니다.
self.makePin(currentLocation)
})
.disposed(by: disposeBag)
}
}
각각의 위치 정보가 newLocations 배열에 저장이 되어 있고 위치 정보를 가지고 있는 객체는 주소 데이터를 가지고 있습니다.
해당 주소 데이터로 좌표데이터 변환을 진행합니다. 그 과정에서 관찰하고 있던 coordinate 변수가 나타나게 되고 그것을 관찰하고 있던 observer는 객체 내에 해당 데이터를 저장하고 업데이트된 객체 내 좌표 데이터로 pin을 찍습니다.
@escaping 클로져를 활용한 비동기처리
private func convertAddressToCoordinates(address: String, completion : @escaping (CLLocationCoordinate2D?,Error?) -> Void){
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
//주소 데이터를 위치 데이터로 변환하는 과정에서 발생한 error를 출력
if let error = error {
completion(nil, error)
return
}
if let placemark = placemarks?.first {
let location = placemark.location
if let coordinate = location?.coordinate {
//coordinate 정보를 파리미터 클로져의 인자로 전달합니다.
completion(coordinate, nil)
}
}
}
}
private func enterCoordinate(){
for idx in 0..<newLocations.count {
var currentLocation = newLocations[idx]
var address = currentLocation.address
convertAddressToCoordinates(address: address){ (coordinate, error) in
if let error = error{
print("Geocoding error: \(error.localizedDescription)")
}
if let coordinate = coordinate{
currentLocation.latitude = coordinate.latitude
currentLocation.longitude = coordinate.longitude
DispatchQueue.main.async {
self.makePin(currentLocation)
}
}
}
}
}
위에 RxSwift로 coordinate 값을 가지고 수행되는 비동기 처리하는 코드와 구조가 유사한 것을 확인할 수 있습니다.
'Trouble Shooting🛠️' 카테고리의 다른 글
trouble shooting) 화면모드 전환으로 인한 텍스트 안보임 현상 (0) | 2024.03.29 |
---|