목차
CoreLocation
CLLocation을 활용한 위치정보 받아오기
- import CoreLocation
- CLLocationManager() : 위치에 대한 대부분의 로직을 담당하는 매니저 객체를 생성.
- CLLocationManagerDelegate 프로토콜 채택 및 기능 구현.
- didUpdateLocations: 사용자의 위치를 성공적으로 가져온 경우, 위치 업데이트 시점마다 호출됨.
- didFailWithError: 사용자의 위치를 가져오지 못한 경우.
- locationManagerDidChangeAuthorization: 사용자의 권한 상태가 바뀌면 알려줌(iOS14+, 14 이전은 didChangeAuthorization)
- locationManager.delegate = self: 위치 프로토콜 delegate 연결
- info.plist에서 Privacy - Location When In Use Usage Description 을 추가하고 위치정보 요청 관련 안내 문구 작성
➡ 이 안내 문구가 부실하면 앱이 리젝될 가능성이 높다. 명확히 해당 권한이 왜 필요한지 명시해야 한다. - locationManager.requestWhenInUseAuthorization(): info.plist에서 명시한 것과 매칭되는 메서드여야 함.
0. LocationManager 타입 구현
네트워크 통신을 위한 매니저 타입을 구현하듯, CoreLocation 로직을 담당하는 LocationManager 타입을 구현했음.
import CoreLocation
import UIKit
protocol LocationManagerDelegate {
func presentAuthorizationAlert(alert: UIAlertController)
}
final class LocationManager: NSObject {
static let shared = LocationManager()
private let locationManager = CLLocationManager()
private(set) var currenCoordinate: CLLocationCoordinate2D?
var delegate: LocationManagerDelegate?
private override init() {
super.init()
locationManager.delegate = self
}
}
- 디바이스 위치 서비스, 위치 정보 권한 등 다양한 상황에 필요한 얼럿 호출을 위해 delegate 패턴 활용.
➡ 싱글턴으로 구현했으므로, delegate 연결에 주의해야함. - private(set) 프로퍼티를 통해 수정에는 닫혀있지만 접근은 가능한 현재 위치(currentCoordinate) 변수.
➡ 현재 위치가 업데이트 되는 시점에 이를 뷰에 반영하고 싶으면, delegate 패턴을 사용하는게 안전하당
➡ 데이터 바인딩을 하는게 아니라면요
✅ NSObject 를 채택한 이유?
CLLocationManagerDelegate는 NSObjectProtocol인데, 이를 LocationManager가 채택하기 위해서는 NSObject를 상속받은 객체이여야 함. 만약 NSObject를 상속받지 않으면 다음과 같은 에러가 발생할 것이다.
"Cannot declare conformance to 'NSObjectProtocol' in Swift; 'LocationManager' should inherit 'NSObject' instead"
1. 위치서비스 허용 여부 확인 및 권한 가져오기
- 위치 서비스를 사용할 수 있는지 체크
- 사용 가능할 경우 → authorizationStatus 를 받아서 권한 체크 로직 수행
- 사용 불가능할 경우 → 얼럿으로 사용자에게 안내 및 설정에서 허용으로 변경 유도
➡ 여기서는 delegate 패턴으로 얼럿 호출 로직 구현
// 1. 기기 위치 서비스 허용 여부 확인
func checkDeviceLocationAuthorization() {
//main thread에서 권한 호출하면 안됨. global thread에서 호출할 것.
DispatchQueue.global().async { [weak self] in
guard let self else { return }
if CLLocationManager.locationServicesEnabled() {
//현재 사용자의 위치 권한 상태를 가지고 옴
let authorization: CLAuthorizationStatus
// notDetermined, restricted(자녀보호기능), denied, authorizedeAlways, authorizedWhenInUse
if #available(iOS 14.0, *) {
authorization = self.locationManager.authorizationStatus
} else {
authorization = CLLocationManager.authorizationStatus()
}
// 위에서 권한 요청하고, 밑에서 권한 확인 로직으로 진입
DispatchQueue.main.async { [self] in
self.checkCurrentLocationAuthorization(status: authorization)
}
} else {
let alert = UIAlertController(
title: "위치 서비스가 꺼져있어요",
message: "'설정>개인정보 보호'에서 위치 서비스를 켜주세요",
preferredStyle: .alert
)
let goSetting = UIAlertAction(title: "설정으로 이동", style: .default) { _ in
if let appSetting = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(appSetting)
}
}
let cancel = UIAlertAction(title: "취소", style: .cancel)
alert.addAction(goSetting)
alert.addAction(cancel)
delegate?.presentAuthorizationAlert(alert: alert)
}
}
}
2. 권한 체크하기
- CLAuthorizationStatus → 사용자의 권한 상태
- .notDetermined: 아직 권한을 선택하지 않은 것
- desiredAccuracy: 위치 정확도 기준 → kCLLocationAccuracyBest
- requsetWhenInUseAuthorization() → 권한 요청 얼럿 띄움!
- .denied, .restricted: 권한을 거부하거나 제한당한 상태
➡ 만약 default 위치를 앱에서 표시하려면, 여기서 직접 location 값을 정해줄 수 있겠다.
➡ 마찬가지로 얼럿을 통해 사용자에게 안내하고 설정에서 권한을 변경하도록 유도! - startUpdatingLocation: 기기의 위치를 받아오기 시작
// 2. 권한 상태 확인
func checkCurrentLocationAuthorization(status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager.desiredAccuracy = kCLLocationAccuracyBest // 정확도
locationManager.requestWhenInUseAuthorization() // 권한 요청 얼럿이 호출됨, infoplist
case .restricted:
print("restricted") // 자녀모드,,
currenCoordinate = .init(latitude: 37.517829, longitude: 126.886270)
case .denied:
print("denied")
currenCoordinate = .init(latitude: 37.517829, longitude: 126.886270)
let alert = UIAlertController(
title: "위치 정보에 접근할 수 없어요.",
message: "'설정>개인정보 보호>위치 서비스'에서 위치 접근 권한을 허용으로 변경해주세요!",
preferredStyle: .alert
)
let goSetting = UIAlertAction(title: "설정으로 이동", style: .default) { _ in
if let appSetting = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(appSetting)
}
}
let cancel = UIAlertAction(title: "취소", style: .cancel)
alert.addAction(goSetting)
alert.addAction(cancel)
delegate?.presentAuthorizationAlert(alert: alert)
case .authorizedAlways:
print("authorizedAlways")
locationManager.startUpdatingLocation()
case .authorizedWhenInUse:
print("authorizedWhenInUse")
locationManager.startUpdatingLocation()
case .authorized:
print("authorized")
default: print("default") // 위치 권한 종류가 더 생길 가능성 대비
}
}
3. 위치 받아오기
- startUpdatingLocation()를 호출하면 계속 알아서 위치를 받아오기 때문에, 한번만 위치 업데이트를 하고 싶으면 꼭 stopUpdatingLocation()를 호출한다.
- 예를들면 러닝앱의 경우 didUpdateLocations 가 계속 호출될 것이고, 날씨 앱의 경우 stopUpdatingLocation을 통해 한번만 호출할 것임.
//3. 사용자의 위치를 성공적으로 가지고 온 경우
// 한번만 실행되지 않는다, iOS 위치 업데이트가 필요한 시점에 알아서 여러번 호출
extension LocationManager: CLLocationManagerDelegate {
func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
if let coordinate = locations.last?.coordinate {
currenCoordinate = coordinate
locationManager.stopUpdatingLocation()
}
}
}
4. 기타 CLLocationManagerDelegate 메서드
- didFailWithError: 위치정보를 가져오지 못할때 호출하는 메서드
- locationManagerDidChangeAuthorization: 위치정보 권한을 변경했을때 호출하는 메서드
- 앱 권한을 다시 체크하고 그에 맞는 로직을 수행하는게 좋다(ex- 얼럿, 위치 정보 업데이트 등)
- iOS 14 미만은 didChangeAuthorization
extension LocationManager: CLLocationManagerDelegate {
//사용자의 위치를 가지고 오지 못한 경우
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
//사용자의 권한 상태가 바뀔 때를 알려줌
//거부했다가 설정에서 변경을 했거나, 혹은 notDetermined 상태에서 허용을 했거나
//허용해서 위치를 가지고 오는 도중에, 설정에서 거부를 하고 앱으로 다시 돌아올 때 등
//iOS14 이상
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print(#function)
checkDeviceLocationAuthorization()
}
//사용자의 권한 상태가 바뀔 때를 알려줌
//iOS14 미만
func locationManager(
_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus
) {
print(#function)
checkDeviceLocationAuthorization()
}
}
5. ViewController에서 Delegate 구현 및 연결
LocationManager에서 얼럿을 생성하고 delegate에게 이를 넘겨주면, MapViewController가 대신 띄우는 형태.
⭐️ LocationManager는 Singleton 객체이므로(하나의 인스턴스로 앱이 종료될 때까지 남아있음), LocationManagerDelegate를 연결한 ViewController는 delegate에 nil을 할당하기 전까지는 메모리에서 해제되지 않을것!!!
따라서 viewDidDisappear에서 LocationManager.shared.delegate에 직접 nil을 할당해야한다.
import UIKit
import MapKit
final class MapViewController: UIViewController {
typealias Coordinate = (Double, Double)
...
override func viewDidLoad() {
super.viewDidLoad()
LocationManager.shared.delegate = self
...
configureUI()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
LocationManager.shared.delegate = nil
}
...
}
// MARK: - LocationManagerDelegate 구현부
extension MapViewController: LocationManagerDelegate {
func presentAuthorizationAlert(alert: UIAlertController) {
present(alert, animated: true)
}
}
LocationManager 전체코드
import CoreLocation
import UIKit
protocol LocationManagerDelegate {
func presentAuthorizationAlert(alert: UIAlertController)
}
final class LocationManager: NSObject {
static let shared = LocationManager()
private let locationManager = CLLocationManager()
private(set) var currenCoordinate: CLLocationCoordinate2D?
var delegate: LocationManagerDelegate?
private override init() {
super.init()
locationManager.delegate = self
}
}
// MARK: - Methods
extension LocationManager {
// 1. 기기 위치 서비스 허용 여부 확인
func checkDeviceLocationAuthorization() {
DispatchQueue.global().async { [weak self] in
guard let self else { return }
if CLLocationManager.locationServicesEnabled() {
let authorization: CLAuthorizationStatus
if #available(iOS 14.0, *) {
authorization = self.locationManager.authorizationStatus
} else {
authorization = CLLocationManager.authorizationStatus()
}
// 위에서 권한 요청하고, 밑에서 권한 확인 로직으로 진입
DispatchQueue.main.async { [self] in
self.checkCurrentLocationAuthorization(status: authorization)
}
} else {
let alert = UIAlertController(
title: "위치 서비스가 꺼져있어요",
message: "'설정>개인정보 보호'에서 위치 서비스를 켜주세요",
preferredStyle: .alert
)
let goSetting = UIAlertAction(title: "설정으로 이동", style: .default) { _ in
if let appSetting = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(appSetting)
}
}
let cancel = UIAlertAction(title: "취소", style: .cancel)
alert.addAction(goSetting)
alert.addAction(cancel)
delegate?.presentAuthorizationAlert(alert: alert)
}
}
}
// 2. 앱의 위치 정보 권한 상태 확인
private func checkCurrentLocationAuthorization(status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization() // 얼럿, infoplist
case .restricted:
print("restricted") // 자녀모드,,
currenCoordinate = .init(latitude: 37.517829, longitude: 126.886270)
case .denied:
print("denied")
currenCoordinate = .init(latitude: 37.517829, longitude: 126.886270)
let alert = UIAlertController(
title: "위치 정보에 접근할 수 없어요.",
message: "'설정>개인정보 보호>위치 서비스'에서 위치 접근 권한을 허용으로 변경해주세요!",
preferredStyle: .alert
)
let goSetting = UIAlertAction(title: "설정으로 이동", style: .default) { _ in
if let appSetting = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(appSetting)
}
}
let cancel = UIAlertAction(title: "취소", style: .cancel)
alert.addAction(goSetting)
alert.addAction(cancel)
delegate?.presentAuthorizationAlert(alert: alert)
case .authorizedAlways:
print("authorizedAlways")
locationManager.startUpdatingLocation()
case .authorizedWhenInUse:
print("authorizedWhenInUse")
locationManager.startUpdatingLocation()
case .authorized:
print("authorized")
default: print("default") // 위치 권한 종류가 더 생길 가능성 대비
}
}
}
// MARK: - CLLocationManagerDelegate 구현부
extension LocationManager: CLLocationManagerDelegate {
func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
if let coordinate = locations.last?.coordinate {
currenCoordinate = coordinate
locationManager.stopUpdatingLocation()
}
}
//사용자의 위치를 가지고 오지 못한 경우
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
//사용자의 권한 상태가 바뀔 때를 알려줌
//거부했다가 설정에서 변경을 했거나, 혹은 notDetermined 상태에서 허용을 했거나
//허용해서 위치를 가지고 오는 도중에, 설정에서 거부를 하고 앱으로 다시 돌아올 때 등
//iOS14 이상
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print(#function)
checkDeviceLocationAuthorization()
}
//사용자의 권한 상태가 바뀔 때를 알려줌
//iOS14 미만
func locationManager(
_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus
) {
print(#function)
checkDeviceLocationAuthorization()
}
}
'SeSAC' 카테고리의 다른 글
8.24 ~ 8.25 TIL(SeSAC iOS 3기) (0) | 2023.08.28 |
---|---|
8.23 TIL(SeSAC iOS 3기), MapKit + frozen Enumeration (1) | 2023.08.27 |
8.21 ~ 8.22 TIL(SeSAC iOS 3기) (0) | 2023.08.27 |
8.14 ~ 8.18 TIL (1) | 2023.08.22 |
8.7 ~ 8.11 TIL (0) | 2023.08.15 |
CoreLocation
CLLocation을 활용한 위치정보 받아오기
- import CoreLocation
- CLLocationManager() : 위치에 대한 대부분의 로직을 담당하는 매니저 객체를 생성.
- CLLocationManagerDelegate 프로토콜 채택 및 기능 구현.
- didUpdateLocations: 사용자의 위치를 성공적으로 가져온 경우, 위치 업데이트 시점마다 호출됨.
- didFailWithError: 사용자의 위치를 가져오지 못한 경우.
- locationManagerDidChangeAuthorization: 사용자의 권한 상태가 바뀌면 알려줌(iOS14+, 14 이전은 didChangeAuthorization)
- locationManager.delegate = self: 위치 프로토콜 delegate 연결
- info.plist에서 Privacy - Location When In Use Usage Description 을 추가하고 위치정보 요청 관련 안내 문구 작성
➡ 이 안내 문구가 부실하면 앱이 리젝될 가능성이 높다. 명확히 해당 권한이 왜 필요한지 명시해야 한다. - locationManager.requestWhenInUseAuthorization(): info.plist에서 명시한 것과 매칭되는 메서드여야 함.
0. LocationManager 타입 구현
네트워크 통신을 위한 매니저 타입을 구현하듯, CoreLocation 로직을 담당하는 LocationManager 타입을 구현했음.
import CoreLocation
import UIKit
protocol LocationManagerDelegate {
func presentAuthorizationAlert(alert: UIAlertController)
}
final class LocationManager: NSObject {
static let shared = LocationManager()
private let locationManager = CLLocationManager()
private(set) var currenCoordinate: CLLocationCoordinate2D?
var delegate: LocationManagerDelegate?
private override init() {
super.init()
locationManager.delegate = self
}
}
- 디바이스 위치 서비스, 위치 정보 권한 등 다양한 상황에 필요한 얼럿 호출을 위해 delegate 패턴 활용.
➡ 싱글턴으로 구현했으므로, delegate 연결에 주의해야함. - private(set) 프로퍼티를 통해 수정에는 닫혀있지만 접근은 가능한 현재 위치(currentCoordinate) 변수.
➡ 현재 위치가 업데이트 되는 시점에 이를 뷰에 반영하고 싶으면, delegate 패턴을 사용하는게 안전하당
➡ 데이터 바인딩을 하는게 아니라면요
✅ NSObject 를 채택한 이유?
CLLocationManagerDelegate는 NSObjectProtocol인데, 이를 LocationManager가 채택하기 위해서는 NSObject를 상속받은 객체이여야 함. 만약 NSObject를 상속받지 않으면 다음과 같은 에러가 발생할 것이다.
"Cannot declare conformance to 'NSObjectProtocol' in Swift; 'LocationManager' should inherit 'NSObject' instead"
1. 위치서비스 허용 여부 확인 및 권한 가져오기
- 위치 서비스를 사용할 수 있는지 체크
- 사용 가능할 경우 → authorizationStatus 를 받아서 권한 체크 로직 수행
- 사용 불가능할 경우 → 얼럿으로 사용자에게 안내 및 설정에서 허용으로 변경 유도
➡ 여기서는 delegate 패턴으로 얼럿 호출 로직 구현
// 1. 기기 위치 서비스 허용 여부 확인
func checkDeviceLocationAuthorization() {
//main thread에서 권한 호출하면 안됨. global thread에서 호출할 것.
DispatchQueue.global().async { [weak self] in
guard let self else { return }
if CLLocationManager.locationServicesEnabled() {
//현재 사용자의 위치 권한 상태를 가지고 옴
let authorization: CLAuthorizationStatus
// notDetermined, restricted(자녀보호기능), denied, authorizedeAlways, authorizedWhenInUse
if #available(iOS 14.0, *) {
authorization = self.locationManager.authorizationStatus
} else {
authorization = CLLocationManager.authorizationStatus()
}
// 위에서 권한 요청하고, 밑에서 권한 확인 로직으로 진입
DispatchQueue.main.async { [self] in
self.checkCurrentLocationAuthorization(status: authorization)
}
} else {
let alert = UIAlertController(
title: "위치 서비스가 꺼져있어요",
message: "'설정>개인정보 보호'에서 위치 서비스를 켜주세요",
preferredStyle: .alert
)
let goSetting = UIAlertAction(title: "설정으로 이동", style: .default) { _ in
if let appSetting = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(appSetting)
}
}
let cancel = UIAlertAction(title: "취소", style: .cancel)
alert.addAction(goSetting)
alert.addAction(cancel)
delegate?.presentAuthorizationAlert(alert: alert)
}
}
}
2. 권한 체크하기
- CLAuthorizationStatus → 사용자의 권한 상태
- .notDetermined: 아직 권한을 선택하지 않은 것
- desiredAccuracy: 위치 정확도 기준 → kCLLocationAccuracyBest
- requsetWhenInUseAuthorization() → 권한 요청 얼럿 띄움!
- .denied, .restricted: 권한을 거부하거나 제한당한 상태
➡ 만약 default 위치를 앱에서 표시하려면, 여기서 직접 location 값을 정해줄 수 있겠다.
➡ 마찬가지로 얼럿을 통해 사용자에게 안내하고 설정에서 권한을 변경하도록 유도! - startUpdatingLocation: 기기의 위치를 받아오기 시작
// 2. 권한 상태 확인
func checkCurrentLocationAuthorization(status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager.desiredAccuracy = kCLLocationAccuracyBest // 정확도
locationManager.requestWhenInUseAuthorization() // 권한 요청 얼럿이 호출됨, infoplist
case .restricted:
print("restricted") // 자녀모드,,
currenCoordinate = .init(latitude: 37.517829, longitude: 126.886270)
case .denied:
print("denied")
currenCoordinate = .init(latitude: 37.517829, longitude: 126.886270)
let alert = UIAlertController(
title: "위치 정보에 접근할 수 없어요.",
message: "'설정>개인정보 보호>위치 서비스'에서 위치 접근 권한을 허용으로 변경해주세요!",
preferredStyle: .alert
)
let goSetting = UIAlertAction(title: "설정으로 이동", style: .default) { _ in
if let appSetting = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(appSetting)
}
}
let cancel = UIAlertAction(title: "취소", style: .cancel)
alert.addAction(goSetting)
alert.addAction(cancel)
delegate?.presentAuthorizationAlert(alert: alert)
case .authorizedAlways:
print("authorizedAlways")
locationManager.startUpdatingLocation()
case .authorizedWhenInUse:
print("authorizedWhenInUse")
locationManager.startUpdatingLocation()
case .authorized:
print("authorized")
default: print("default") // 위치 권한 종류가 더 생길 가능성 대비
}
}
3. 위치 받아오기
- startUpdatingLocation()를 호출하면 계속 알아서 위치를 받아오기 때문에, 한번만 위치 업데이트를 하고 싶으면 꼭 stopUpdatingLocation()를 호출한다.
- 예를들면 러닝앱의 경우 didUpdateLocations 가 계속 호출될 것이고, 날씨 앱의 경우 stopUpdatingLocation을 통해 한번만 호출할 것임.
//3. 사용자의 위치를 성공적으로 가지고 온 경우
// 한번만 실행되지 않는다, iOS 위치 업데이트가 필요한 시점에 알아서 여러번 호출
extension LocationManager: CLLocationManagerDelegate {
func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
if let coordinate = locations.last?.coordinate {
currenCoordinate = coordinate
locationManager.stopUpdatingLocation()
}
}
}
4. 기타 CLLocationManagerDelegate 메서드
- didFailWithError: 위치정보를 가져오지 못할때 호출하는 메서드
- locationManagerDidChangeAuthorization: 위치정보 권한을 변경했을때 호출하는 메서드
- 앱 권한을 다시 체크하고 그에 맞는 로직을 수행하는게 좋다(ex- 얼럿, 위치 정보 업데이트 등)
- iOS 14 미만은 didChangeAuthorization
extension LocationManager: CLLocationManagerDelegate {
//사용자의 위치를 가지고 오지 못한 경우
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
//사용자의 권한 상태가 바뀔 때를 알려줌
//거부했다가 설정에서 변경을 했거나, 혹은 notDetermined 상태에서 허용을 했거나
//허용해서 위치를 가지고 오는 도중에, 설정에서 거부를 하고 앱으로 다시 돌아올 때 등
//iOS14 이상
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print(#function)
checkDeviceLocationAuthorization()
}
//사용자의 권한 상태가 바뀔 때를 알려줌
//iOS14 미만
func locationManager(
_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus
) {
print(#function)
checkDeviceLocationAuthorization()
}
}
5. ViewController에서 Delegate 구현 및 연결
LocationManager에서 얼럿을 생성하고 delegate에게 이를 넘겨주면, MapViewController가 대신 띄우는 형태.
⭐️ LocationManager는 Singleton 객체이므로(하나의 인스턴스로 앱이 종료될 때까지 남아있음), LocationManagerDelegate를 연결한 ViewController는 delegate에 nil을 할당하기 전까지는 메모리에서 해제되지 않을것!!!
따라서 viewDidDisappear에서 LocationManager.shared.delegate에 직접 nil을 할당해야한다.
import UIKit
import MapKit
final class MapViewController: UIViewController {
typealias Coordinate = (Double, Double)
...
override func viewDidLoad() {
super.viewDidLoad()
LocationManager.shared.delegate = self
...
configureUI()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
LocationManager.shared.delegate = nil
}
...
}
// MARK: - LocationManagerDelegate 구현부
extension MapViewController: LocationManagerDelegate {
func presentAuthorizationAlert(alert: UIAlertController) {
present(alert, animated: true)
}
}
LocationManager 전체코드
import CoreLocation
import UIKit
protocol LocationManagerDelegate {
func presentAuthorizationAlert(alert: UIAlertController)
}
final class LocationManager: NSObject {
static let shared = LocationManager()
private let locationManager = CLLocationManager()
private(set) var currenCoordinate: CLLocationCoordinate2D?
var delegate: LocationManagerDelegate?
private override init() {
super.init()
locationManager.delegate = self
}
}
// MARK: - Methods
extension LocationManager {
// 1. 기기 위치 서비스 허용 여부 확인
func checkDeviceLocationAuthorization() {
DispatchQueue.global().async { [weak self] in
guard let self else { return }
if CLLocationManager.locationServicesEnabled() {
let authorization: CLAuthorizationStatus
if #available(iOS 14.0, *) {
authorization = self.locationManager.authorizationStatus
} else {
authorization = CLLocationManager.authorizationStatus()
}
// 위에서 권한 요청하고, 밑에서 권한 확인 로직으로 진입
DispatchQueue.main.async { [self] in
self.checkCurrentLocationAuthorization(status: authorization)
}
} else {
let alert = UIAlertController(
title: "위치 서비스가 꺼져있어요",
message: "'설정>개인정보 보호'에서 위치 서비스를 켜주세요",
preferredStyle: .alert
)
let goSetting = UIAlertAction(title: "설정으로 이동", style: .default) { _ in
if let appSetting = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(appSetting)
}
}
let cancel = UIAlertAction(title: "취소", style: .cancel)
alert.addAction(goSetting)
alert.addAction(cancel)
delegate?.presentAuthorizationAlert(alert: alert)
}
}
}
// 2. 앱의 위치 정보 권한 상태 확인
private func checkCurrentLocationAuthorization(status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization() // 얼럿, infoplist
case .restricted:
print("restricted") // 자녀모드,,
currenCoordinate = .init(latitude: 37.517829, longitude: 126.886270)
case .denied:
print("denied")
currenCoordinate = .init(latitude: 37.517829, longitude: 126.886270)
let alert = UIAlertController(
title: "위치 정보에 접근할 수 없어요.",
message: "'설정>개인정보 보호>위치 서비스'에서 위치 접근 권한을 허용으로 변경해주세요!",
preferredStyle: .alert
)
let goSetting = UIAlertAction(title: "설정으로 이동", style: .default) { _ in
if let appSetting = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(appSetting)
}
}
let cancel = UIAlertAction(title: "취소", style: .cancel)
alert.addAction(goSetting)
alert.addAction(cancel)
delegate?.presentAuthorizationAlert(alert: alert)
case .authorizedAlways:
print("authorizedAlways")
locationManager.startUpdatingLocation()
case .authorizedWhenInUse:
print("authorizedWhenInUse")
locationManager.startUpdatingLocation()
case .authorized:
print("authorized")
default: print("default") // 위치 권한 종류가 더 생길 가능성 대비
}
}
}
// MARK: - CLLocationManagerDelegate 구현부
extension LocationManager: CLLocationManagerDelegate {
func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
if let coordinate = locations.last?.coordinate {
currenCoordinate = coordinate
locationManager.stopUpdatingLocation()
}
}
//사용자의 위치를 가지고 오지 못한 경우
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
//사용자의 권한 상태가 바뀔 때를 알려줌
//거부했다가 설정에서 변경을 했거나, 혹은 notDetermined 상태에서 허용을 했거나
//허용해서 위치를 가지고 오는 도중에, 설정에서 거부를 하고 앱으로 다시 돌아올 때 등
//iOS14 이상
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print(#function)
checkDeviceLocationAuthorization()
}
//사용자의 권한 상태가 바뀔 때를 알려줌
//iOS14 미만
func locationManager(
_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus
) {
print(#function)
checkDeviceLocationAuthorization()
}
}
'SeSAC' 카테고리의 다른 글
8.24 ~ 8.25 TIL(SeSAC iOS 3기) (0) | 2023.08.28 |
---|---|
8.23 TIL(SeSAC iOS 3기), MapKit + frozen Enumeration (1) | 2023.08.27 |
8.21 ~ 8.22 TIL(SeSAC iOS 3기) (0) | 2023.08.27 |
8.14 ~ 8.18 TIL (1) | 2023.08.22 |
8.7 ~ 8.11 TIL (0) | 2023.08.15 |