Swift/Xcode

[Swift] CLLocationManager

yeggrrr๐Ÿผ 2024. 5. 31. 12:04

๊ถŒํ•œ ์„ค์ •ํ•˜๊ธฐ (info.plist)


- ํ•„์š”ํ•œ ๊ถŒํ•œ๋งŒ ์„ค์ •ํ•˜๊ธฐ
- ๊ถŒํ•œ์— ๋Œ€ํ•œ ํ•„์š”์„ฑ Description ๊ตฌ์ฒด์ ์œผ๋กœ ์ž‘์„ฑํ•˜๊ธฐ

โœ”๏ธ Privacy - Location When In User Usage Description
foreground์—์„œ ์‹คํ–‰ ์ค‘์ผ ๋•Œ๋งŒ ์•ฑ์ด ์œ„์น˜ ์ •๋ณด์— ์—‘์„ธ์Šคํ•˜๋Š” ๊ฒฝ์šฐ ( iOS 11+ )

โœ”๏ธ Privacy - Location Always and When In Use Usage Description
background์—์„œ ์‹คํ–‰๋˜๋Š” ๋™์•ˆ ์•ฑ์ด ์œ„์น˜ ์ •๋ณด์— ์—‘์„ธ์Šคํ•˜๋Š” ๊ฒฝ์šฐ ( iOS 11+ )

โœ”๏ธ Privacy - Location Always Usage Description
background์—์„œ ์•ฑ์ด ์œ„์น˜ ์ •๋ณด์— ์—‘์„ธ์Šคํ•˜๋Š” ๊ฒฝ์šฐ ( iOS 11- )

โœ”๏ธ Privacy - Location Default Accuracy Reduced
์œ„์น˜ ์ •ํ™•๋„์— ๋Œ€ํ•œ ์•ฑ์˜ ๊ธฐ๋ณธ ๋™์ž‘ ์„ค์ • ์‹œ, ์ •๋ณด ์†์„ฑ ๋ชฉ๋ก์— ์ด ํ‚ค๋ฅผ ํฌํ•จ ( iOS 14+ )
→ true: ์ •ํ™•๋„๊ฐ€ ๋–จ์–ด์ง€๋Š” ์ง€ ๋ฌป๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ํ‘œ์‹œ๋˜๋„๋ก ํ‚ค ๊ฐ’์„ ์„ค์ •
→ false: ์ „์ฒด ์œ„์น˜ ์ •ํ™•๋„๋ฅผ ๋ฌป๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ํ‘œ์‹œ๋˜๋กœ๋ก ์„ค์ • (๊ธฐ๋ณธ๊ฐ’)

โœ”๏ธ Privacy - Location Temporary Usage Description Dictionary
์•ฑ์˜ ํ•œ ๋ถ€๋ถ„์—์„œ ์‚ฌ์šฉ์ž ์œ„์น˜์— ๋Œ€ํ•œ ์ž„์‹œ ์—‘์„ธ์Šค๋ฅผ ์š”์ฒญํ•˜๋Š” ์ด์œ  ์„ค๋ช…

โœ”๏ธ Privacy - Location Usage Description
MacOS ์•ฑ์ด ์‚ฌ์šฉ์ž์˜ ์œ„์น˜ ์ •๋ณด์— ์—‘์„ธ์Šคํ•˜๋Š” API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ํ•„์š”


์ด๋ ‡๊ฒŒ ์ถ”๊ฐ€ํ•ด์„œ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค. (์œ„์น˜๋ฅผ ํ—ˆ์šฉํ•ด์ฃผ์„ธ์š”.)
์›๋ž˜๋Š” ๋ชฉ์ ๊ณผ ์ด์œ ์™€ ์–ด๋””์— ์‚ฌ์šฉ์ด ๋˜๋Š”์ง€ ๊ผญ ๊ตฌ์ฒด์ ์œผ๋กœ ์ž‘์„ฑํ•ด์•ผํ•œ๋‹ค. (์•„๋‹ˆ๋ฉด ๋ฆฌ์ ๋  ์ˆ˜ ์žˆ์Œ)



CLAuthorizationStatus (๊ถŒํ•œ ์Šน์ธ ์ƒํƒœ)

โœ”๏ธ case notDetermined
- ์•ฑ์˜ ์œ„์น˜ ์„œ๋น„์Šค ์‚ฌ์šฉ ์—ฌ๋ถ€์— ๋Œ€ํ•ด์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ

โœ”๏ธ case restricted
- ์•ฑ์˜ ์œ„์น˜ ์„œ๋น„์Šค ์‚ฌ์šฉ ๊ถŒํ•œ์ด ์—†์–ด ์ƒํƒœ ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ (ex. ์ž๋…€ ๋ณดํ˜ธ ๊ธฐ๋Šฅ)

โœ”๏ธ case denied
- ์•ฑ์˜ ์œ„์น˜ ์„œ๋น„์Šค ์‚ฌ์šฉ ๊ถŒํ•œ์— Alert์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฑฐ๋ถ€ํ•œ ๊ฒฝ์šฐ
- '์„ค์ • > ์œ„์น˜ ์„œ๋น„์Šค'์—์„œ ์„œ๋น„์Šค๋ฅผ ์ค‘์ง€ํ•œ ๊ฒฝ์šฐ
- ๋น„ํ–‰๊ธฐ ๋ชจ๋“œ์ธ ๊ฒฝ์šฐ

โœ”๏ธ case authorizedAlways
- ์•ฑ์˜ ์œ„์น˜ ์„œ๋น„์Šค ๊ถŒํ•œ์„ ์Šน์ธํ•œ ๊ฒฝ์šฐ (์–ธ์ œ๋“ ์ง€ Always)

โœ”๏ธ case authorizedWhenInUse
- ์•ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๋™์•ˆ ์œ„์น˜ ์„œ๋น„์Šค ๊ถŒํ•œ์„ ์Šน์ธํ•œ ๊ฒฝ์šฐ

When In Use๋Š” ์•ฑ์ด ์‚ฌ์šฉ๋˜๋Š” ๋™์•ˆ ๋ชจ๋“  ์œ„์น˜ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ iOS ์•ฑ์€ foreground์— ์žˆ๊ฑฐ๋‚˜ background์—์„œ ์‹คํ–‰ ์ค‘์ธ ๊ฒฝ์šฐ ์‚ฌ์šฉ ์ค‘์ธ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•œ๋‹ค.

Always๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ์ด ์‹คํ–‰๋˜๊ณ  ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ์•ฑ์˜ ๋ชจ๋“  ์œ„์น˜ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์•ฑ์ด ์‹คํ–‰๋˜๊ณ  ์žˆ์ง€ ์•Š์•„๋„ ์‹œ์Šคํ…œ์ด ์•ฑ์„ ์‹คํ–‰ํ•˜๊ณ  ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.


โš ๏ธ ์œ„์น˜ ์„œ๋น„์Šค์— ๋Œ€ํ•œ ์Šน์ธ ์š”์ฒญ ๋ฉ”์„œ๋“œ๋Š” ์œ„์—์„œ ์–ธ๊ธ‰ํ–ˆ๋˜ info.plist์— ๊ฐ™์€ ํ•ญ๋ชฉ์ด ์ถ”๊ฐ€๋˜์–ด ์žˆ์–ด์•ผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ โš ๏ธ


 

import MapKit
let mapView = MKMapView()


์šฐ์„  MapKit ์ž„ํฌํŠธ ํ•ด์ฃผ๊ณ , mapView ์ƒ์„ฑํ•ด์„œ ์ž์œ ๋กญ๊ฒŒ UI ์„ค์ •ํ•˜๊ธฐ

// 1. ์œ„์น˜ ํ”„๋ ˆ์ž„ ์›Œํฌ ์ž„ํฌํŠธํ•˜๊ธฐ
import CoreLocation
// 2. ์œ„์น˜ ๋งค๋‹ˆ์ € ์ƒ์„ฑํ•˜๊ธฐ
let locationManager = CLLocationManager()
// 3. ์œ„์น˜ ๊ด€๋ จ ํ”„๋กœํ† ์ฝœ ์„ ์–ธํ•˜๊ธฐ
extension LocationViewController: CLLocationManagerDelegate {

}
// 4. ํด๋ž˜์Šค์™€ ํ”„๋กœํ† ์ฝœ ์—ฐ๊ฒฐํ•˜๊ธฐ
locationManager.delegate = self
extension LocationViewController: CLLocationManagerDelegate {
    // 5. ์‚ฌ์šฉ์ž ์œ„์น˜๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๊ฐ€์ง€๊ณ  ์˜จ ๊ฒฝ์šฐ (์ฝ”๋“œ ๊ตฌ์„ฑ์— ๋”ฐ๋ผ ์—ฌ๋Ÿฌ๋ฒˆ ํ˜ธ์ถœ์ด ๋  ์ˆ˜๋„ ์žˆ์Œ)
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {        
        if let coordinate = locations.last?.coordinate {
            setRegionAndAnnotation(center: coordinate)
        }
        
        // stopUpdatingLocation์„ ํ–ˆ์œผ๋ฉด, ๋”์ด์ƒ ์œ„์น˜๋ฅผ ์•ˆ๋ฐ›์•„๋„ ๋˜๋Š” ์‹œ์ ์—์„œ๋Š” stop์„ ์™ธ์ณ์•ผํ•จ.
        locationManager.stopUpdatingLocation() // "๋กœ์ง์ด ๋๋‚˜๋ฉด stopํ•ด์ค˜." (์˜ˆ์™ธ์ ์œผ๋กœ ๋Ÿฌ๋‹์•ฑ๊ฐ™์€ ๊ฒฝ์šฐ์—” ์ด๊ฑฐ ์‚ฌ์šฉX)
    }
    // 6. ์‚ฌ์šฉ์ž ์œ„์น˜๋ฅผ ๊ฐ€์ง€๊ณ  ์˜ค์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ
    // didFailWithError
    func locationManager(_ manager: CLLocationManager, didFailWithError error: any Error) {
        print(#function, "iOS14+")
    }
    
    // 7. ์‚ฌ์šฉ์ž ๊ถŒํ•œ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ์ด ๋  ๋•Œ(iOS14) - ์ฒ˜์Œ์—” ํ—ˆ์šฉํ–ˆ๋‹ค๊ฐ€ ์•„์ดํฐ ์„ค์ •์—์„œ ๊ฑฐ๋ถ€ํ•œ ๊ฒฝ์šฐ
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        print(#function, "iOS14+")
        checkDeviceLocationAuthorization()
    }
    
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        print(#function, "iOS14-")
    }
}
extension LocationViewController {
    // 1) ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ถŒํ•œ ์š”์ฒญ์„ ํ•˜๊ธฐ ์œ„ํ•ด, iOS ์œ„์น˜ ์„œ๋น„์Šค ํ™œ์„ฑํ™” ์—ฌ๋ถ€ ์ฒดํฌ
    func checkDeviceLocationAuthorization() {
    	// ์ผœ์ ธ์žˆ๋Š” ๊ฒฝ์šฐ
        if CLLocationManager.locationServicesEnabled() {
            // 2) ํ˜„์žฌ ์‚ฌ์šฉ์ž ์œ„์น˜ ๊ถŒํ•œ ์ƒํƒœ ํ™•์ธ
            checkCurrentLocationAuthorization()
        // ๊บผ์ ธ์žˆ๋Š” ๊ฒฝ์šฐ
        } else {
		"์œ„์น˜ ์„œ๋น„์Šค๊ฐ€ ๊บผ์ ธ ์žˆ์–ด์„œ, ์œ„์น˜ ๊ถŒํ•œ ์š”์ฒญ์„ ํ•  ์ˆ˜ ์—†์–ด์š”!!" -> Alert ๊ธฐ๋Šฅ
        }
    }
    
    // 2) ํ˜„์žฌ ์‚ฌ์šฉ์ž ์œ„์น˜ ๊ถŒํ•œ ์ƒํƒœ ํ™•์ธ
    func checkCurrentLocationAuthorization() {
        var status = locationManager.authorizationStatus
        
        if #available(iOS 14.0, *) {
            status = locationManager.authorizationStatus
        } else {
            status = CLLocationManager.authorizationStatus()
        }
        
        switch status {
		// ๊ถŒํ•œ ๋ฌธ๊ตฌ ๋„์šธ ์ˆ˜ ์žˆ์Œ
        case .notDetermined:
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.requestWhenInUseAuthorization() // ์ด๊ฑฐ๋ž‘ info.plist์— ์„ค์ •ํ•ด ๋†“์€ ๊ฒŒ ๊ฐ™์€์ง€ ํ™•์ธ!
        case .denied:
            // iOS ์„ค์ • ์ฐฝ์œผ๋กœ ์ด๋™ํ•˜๋ผ๋Š” ์–ผ๋Ÿฟ์„ ๋„์›Œ์ฃผ๊ธฐ
        case .authorizedWhenInUse:
			// ์œ„์น˜ ์ •๋ณด ์•Œ๋ ค๋‹ฌ๋ผ๊ณ  ๋กœ์ง ๊ตฌ์„ฑ
            locationManager.startUpdatingLocation()
        default:
            print("-")
        }
    }
    
    func setRegionAndAnnotation(center: CLLocationCoordinate2D) {
        // ๋งต๋ทฐ์— ์–ด๋…ธํ…Œ์ด์…˜
        let region = MKCoordinateRegion(center: center, latitudinalMeters: 500, longitudinalMeters: 500)
        mapView.setRegion(region, animated: true)
    }
}

์ด๋ ‡๊ฒŒ ํ•ด์ฃผ๊ณ , viewDidLoad ํ˜น์€ ๋ฒ„ํŠผ Action ๋“ฑ ์›ํ•˜๋Š” ๊ณณ -> checkCurrentLocationAuthorization() ์ถ”๊ฐ€ํ•ด์ฃผ๊ธฐ

์‹คํ–‰ํ•ด๋ณด๋ฉด, 


์ด๋ ‡๊ฒŒ ์ •์ƒ์ ์œผ๋กœ ์œ„์น˜ ๊ถŒํ•œ ๋ฌธ๊ตฌ๊ฐ€ ๋‚˜์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


๊ทผ๋ฐ! ์ด๋ ‡๊ฒŒ ๋นŒ๋“œํ•˜๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์€ ๋ณด๋ผ์ƒ‰ ๊ฒฝ๊ณ ๊ฐ€ ๋œฌ๋‹ค.


์ด ๋ฉ”์„œ๋“œ๋ฅผ ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ์—์„œ ํ˜ธ์ถœํ•˜๋ฉด, UI๊ฐ€ ์‘๋‹ตํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.

extension LocationViewController {
    DispatchQueue.global().async { // Concurrent
        func checkDeviceLocationAuthorization() {
            if CLLocationManager.locationServicesEnabled() {
                DispatchQueue.main.async { // UI ๊ด€๋ จ -> main์ด ์ˆ˜ํ–‰ (Serial)
                    self.checkCurrentLocationAuthorization()
                }
            } else {
                // Alert ๊ธฐ๋Šฅ (ex. "์œ„์น˜ ์„œ๋น„์Šค๊ฐ€ ๊บผ์ ธ ์žˆ์–ด์„œ, ์œ„์น˜ ๊ถŒํ•œ ์š”์ฒญ์„ ํ•  ์ˆ˜ ์—†์–ด์š”!!")
            }
        }
    }
    
    ...
}


๊ทธ๋ž˜์„œ ์œ„์™€ ๊ฐ™์ด UI ๊ด€๋ จ ์ž‘์—…์„ ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ์—์„œ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๋ฉด ์‚ฌ๋ผ์ง€๊ฒŒ ๋œ๋‹ค.

 


 

728x90