아이폰의 현재 위도 및 경도 정보 추출 및 상세주소 정보 추출 

 

info.plist 퍼미션 입력

Privacy - Location When In Use Usage Description - 앱이 사용중일때만 가져오기

Privacy - Location Always and When In Use Usage Description - 앱을 사용안해도 항상 가져오기

 

import UIKit
import CoreLocation

class LocationService: NSObject {
    
    static let shared = LocationService()
    let locationManager = CLLocationManager()
    var locationClosur: ((_ latitude: Double, _ longitude: Double) -> Void)!
    
    deinit {
        log(direction: .ETC, ofType: self, datas: "deinit")
    }
    
    override init() {
        super.init()
        
        locationManager.delegate = self
    }
    
    ///didUpdateLocations Delegate 실행
    func startLocationUpdate() {
        locationManager.startUpdatingLocation()
    }
    
    ///didUpdateLocations Delegate 정지
    func stopLocationUpdate() {
        locationManager.stopUpdatingLocation()
    }
    
    /// location 정보로 주소 추출
    func getLocationAddress(completed: @escaping (String?) -> Void){
        if let latitude = locationManager.location?.coordinate.latitude,
           let longitude = locationManager.location?.coordinate.longitude {
            
            //참고자료 : https://velog.io/@rose6649/iOS-CLGeocoder-%EC%9C%84%EB%8F%84%EA%B2%BD%EB%8F%84-%EC%A0%95%EB%B3%B4%EB%A1%9C-%EC%A7%80%EC%97%AD%EB%AA%85-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0
            
            let findLocation: CLLocation = CLLocation(latitude: latitude, longitude: longitude)
            
            let geocoder = CLGeocoder.init()
            let local: Locale = Locale(identifier: "Ko-kr") // Korea
            
            geocoder.reverseGeocodeLocation(findLocation, preferredLocale: local) { (placemarks, error) in
                if error != nil {
                    return
                }
                
                var address: String = ""
                
                if let placemark = placemarks?.last {

                    log(direction: .OBSERVER, ofType: self, datas: 
                        "국적 : \(String(describing: placemark.country))",
                        "시/도 : \(String(describing: placemark.administrativeArea))",
                        "시 : \(String(describing: placemark.locality))",
                        "구 : \(String(describing: placemark.subLocality))",
                        "동 / 가 : \(String(describing: placemark.thoroughfare))",
                        "번지 : \(String(describing: placemark.subThoroughfare))")
                    
                    // 국적 (ex : 대한민국)
                    if let country = placemark.country {
                        address += country + " "
                    }
                    
                    // 시/도 (ex : 광주광역시 / 경기도)
                    if let administrativeArea = placemark.administrativeArea {
                        address += administrativeArea + " "
                        
                        // 시 (ex : 평택시) - administrativeArea (경기도) 일 경우 locality 시가 들어옴.
                        // 이 외 administrativeArea 시 일 경우 동일한 데이터 표시됨.
                        // administrativeArea와 locality 같을 경우 패스시킴
                        if let locality = placemark.locality {
                            if administrativeArea.compare(locality, options: .caseInsensitive) != .orderedSame {
                                address += locality + " "
                            }
                        }
                    }
                    
                    // 구 (ex : 북구)
                    if let subLocality = placemark.subLocality {
                        address += subLocality + " "
                    }
                    
                    // 동 / 가 (ex : 매곡동 / 북문로3가)
                    if let thoroughfare = placemark.thoroughfare {
                        address += thoroughfare + " "
                    }
                    
                    // 번지 (ex : 89번지)
                    if let subThoroughfare = placemark.subThoroughfare {
                        address += subThoroughfare
                    }
                    
                    completed(address)
                }
            }
        }
        completed(nil)
    }
    
    private func checkUserDeviceLocationServiceAuthorization() {
        
        let authorizationStatus: CLAuthorizationStatus
            
        // 앱의 권한 상태 가져오는 코드
        if #available(iOS 14.0, *) {
            authorizationStatus = locationManager.authorizationStatus
        }else {
            authorizationStatus = CLLocationManager.authorizationStatus()
        }
            
        // 권한 상태값에 따라 분기처리를 수행하는 메서드 실행
        checkUserCurrentLocationAuthorization(authorizationStatus)
    }
    
    private func checkUserCurrentLocationAuthorization(_ status: CLAuthorizationStatus) {
        switch status {
        case .notDetermined:
            // 사용자가 권한에 대한 설정을 선택하지 않은 상태
            
            // 권한 요청을 보내기 전에 desiredAccuracy 설정 필요
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            
            // 권한 요청을 보낸다.
            locationManager.requestWhenInUseAuthorization()
                
        case .denied, .restricted:
            // 사용자가 명시적으로 권한을 거부했거나, 위치 서비스 활성화가 제한된 상태
            // 시스템 설정에서 설정값을 변경하도록 유도한다.
            // 시스템 설정으로 유도하는 커스텀 얼럿
            showRequestLocationServiceAlert()
            
        case .authorizedWhenInUse:
            // 앱을 사용중일 때, 위치 서비스를 이용할 수 있는 상태
            // manager 인스턴스를 사용하여 사용자의 위치를 가져온다.
            locationManager.startUpdatingLocation()
            
        default:
            print("Default")
        }
    }
    
    private func showRequestLocationServiceAlert() {
        let requestLocationServiceAlert = UIAlertController(title: "위치 정보 이용", message: "위치 서비스를 사용할 수 없습니다.\n디바이스의 '설정 > 개인정보 보호'에서 위치 서비스를 켜주세요.", preferredStyle: .alert)
        
        let goSetting = UIAlertAction(title: "설정으로 이동", style: .destructive) { _ in
            if let appSetting = URL(string: UIApplication.openSettingsURLString) {
                UIApplication.shared.open(appSetting)
            }
        }
        let cancel = UIAlertAction(title: "취소", style: .default) {  _ in
//            async { await self?.reloadData() }
        }
        requestLocationServiceAlert.addAction(cancel)
        requestLocationServiceAlert.addAction(goSetting)
        
        guard let topVC = UIApplication.currentTopViewController() else {
            return
        }
        
        topVC.present(requestLocationServiceAlert, animated: true)
    }
}

//MARK: CLLocationManagerDelegate
extension LocationService: CLLocationManagerDelegate {
    
    // 사용자의 위치를 성공적으로 가져왔을 때 호출
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        // 위치 정보를 배열로 입력받는데, 마지막 index값이 가장 정확하다고 한다.
        if let coordinate = locations.last?.coordinate {
            // ⭐️ 사용자 위치 정보 사용(위도, 경도 가져오기)
            log(direction: .ETC, ofType: self, datas: "위도 : \(coordinate.latitude)","경도 : \(coordinate.longitude)")
            
            self.locationClosur?(coordinate.latitude, coordinate.longitude)
            
        }
    }

    // 사용자가 GPS 사용이 불가한 지역에 있는 등 위치 정보를 가져오지 못했을 때 호출
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        log(direction: .ERROR, ofType: self, datas: error.localizedDescription)
    }
    
    // 앱에 대한 권한 설정이 변경되면 호출 (iOS 14 이상)
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        // 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 호출
        checkUserDeviceLocationServiceAuthorization()
    }
    
    // 앱에 대한 권한 설정이 변경되면 호출 (iOS 14 미만)
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        // 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 호출
        checkUserDeviceLocationServiceAuthorization()
    }
}

사용방법

// 아이폰 위도 및 경도 정보를 주기적으로 받을 수 있는 Closer 
LocationService.shared.locationClosur = { [weak self] (latitude, longitude) in
 	  log(direction: .ETC, ofType: self, datas: "위도 : \(latitude)","경도 : \(longitude)")
      // 계속 정보를 받을 경우 주석 처리하면 된다.       
      LocationService.shared.stopLocationUpdate()
}

// 현채 위치의 위도 및 경도를 이용해서 상세 주소를 가져온다.
LocationService.shared.getLocationAddress {[weak self] address in
            
      guard let address = address else {
           return
      }
      
      log(direction: .ETC, ofType: self, datas: "상세주소 : \(address)")
}

+ Recent posts