Core Location
- iOS 및 macOS 앱에서 위치 관련 서비스를 활용할 수 있는 프레임워크
- iPhone Device의 위치 정보를 얻을 수 있는 기능을 제공하는 프레임워크 (지리적 위치, 고도 및 방향)
- 이를 통해 위치 데이터를 쉽게 얻을 수 있으며 사용자의 위치, 이동 경로, 근접 지점 등 다양한 기능 구현 가능
- Wi-fi, GPS, Bluetooth 등을 이용하여 정보를 모음
위치 정보를 사용하기 이전 꼭 사용자에게 위치 정보를 사용할건지 물어야 됨!
→ Info.plist에서 위치 권한 허용 (필수 작업)
- Key 값을 Info.plist에 추가
1. CLLocationManger
- 필수 사용 클래스
- 위치 관리자 객체로, 위치 데이터를 수집하고 관리하는 핵심 클래스
- 위치 관리자는 사용자의 위치 업데이트 요청 및 업데이트를 받으면 델리게이트에게 알리는 역할을 함
import CoreLocation
let locationManager = CLLocationManager() // locationManager 인스턴스 생성
locationManager.delegate = self // delegate 설정
locationManager.requestWhenInUseAuthorization() // 앱을 사용할 때만 위치 정보를 허용할 경우 호출
locationManager.startUpdatingLocation() // 위치 정보를 지속적으로 받고 싶은 경우 이벤트 시작
기능
- 위치 업데이트 요청
- 권한 요청 및 확인
- 위치 서비스에 엑세스하기 위해서는 앱이 사용자의 권환을 요청해야 함
locationManager.requestWhenInUseAuthorization() // 앱을 사용할 때만 위치 정보 허용 locationManager.requestAlwaysAuthorization() // 항상 위치 정보 허용
- 정확도 설정
- 정확도가 높을수록 더 많은 배터리 소모
locationManager.desiredAccuracy = kCLLocationAccuracyBest // default 값 /* kCLLocatoinAccuracyBestForNavigation - 가장 높은 수준의 정확도, 외부 전원이 연결되어 있을 경우에만 사용 kCLLocatoinAccuracyBest - 베터리로 동작할 때 권장되는 가장 높은 수준의 정확도 kCLLocatoinAccuracyNearestTenMeters - 10미터 이내의 정확도 kCLLocatoinAccuracyHundredMeters - 100미터 이내의 정확도 kCLLocatoinAccuracyKilometer - 1키로미터 이내의 정확도 kCLLocatoinAccuracyThreeKilometers - 3키로미터 이내의 정확도 */
- 백그라운드에서의 위치 업데이트
- 백그라운드에서 위치 기반 서비스 실행 가능
locationManager.allowsBackgroundLocationUpdates = true
- 모니터링 지역 등록
- 특정 지역을 지정하고 해당 지역에 들어가거나 나오는 이벤트 감지 가능
let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), radius: 100, identifier: "SanFrancisco") locationManager.startMonitoring(for: region)
- CLLocationManagerDelegate 사용
- CLLocationManagerDelegate 프로토콜을 채택하여 위치 업데이트 및 에러 처리 관련 이벤트 다룸
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { // 위치 업데이트 처리 } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { // 에러 처리 }
2. CLLocation
- 위치 정보를 나타내는 클래스
- 위도(latitude), 경도(longitude), 고도(altitude), 정확도의 속성을 가짐
- 위치가 변경될 때마다 호출되며 가장 최근 위치 데이터를 배열의 마지막 객체에 포함하는 CLLocation 객체들의 배열이 인자로 전달됨
// 위치 정보 계속 업데이트 -> 위도 경도 받아옴
// 위치가 변경될 때마다 호출되며 가장 최근 위치 데이터를 배열의 마지막 객체에 포함하는 CLLocation 객체들의 배열이 인자로 전달됨
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("didUpdaterLocation")
if let location = locations.last {
print("위도 : \\(location.coordinate.latitude)")
print("경도 : \\(location.coordinate.longitude)")
print("고도(m 단위) : \\(location.altitude)")
print("수평 정확도(m 단위) : \\(location.verticalAccuracy)")
print("수직 정확도(m 단위) : \\(location.horizontalAccuracy)")
}
}
3. CLGeocoder
- 지오코딩 서비스를 제공하는 클래스
- 주소 문자열과 좌표 간 변환 수행
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
if let placemark = placemarks?.first {
let address = placemark.name
let city = placemark.locality
// 주소 정보 사용
}
}
위치 정보 받아오기
여러 권한 중 위치 정보를 받아오기 위해서는 다음과 같은 순서로 작업하면 됨
- info.plist에 필요한 권한 추가 & Description 작성
- import CoreLocation & CLLocation Manager 인스턴스 생성
- 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 추가
- 앱에 대한 위치 권한이 부여된 상태인지 확인하는 메서드 추가
- Delegate설정 & Delegate Protocol 채택
1. Info.plist에서 위치 권한 허용 (필수 작업)
Value : 권한이 필요한 사유
2. import CoreLocation & CLLocation Manager 인스턴스 생성
import CoreLocation
class CoreLocationEx : UIViewController {
// CLLocationManager 인스턴스 생성
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
}
}
3. 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 추가
3-1. 디바이스 자체에 위치 서비스가 활성화 상태인지 확인
- CLLocationManager의 타입 메서드인 locationServicesEnabled() 메서드를 통해 활성화 상태 여부를 Bool 값으로 반환 함
- 만약 사용자 디바이스의 위치 서비스가 비활성화 상태인 경우, 위치에 대한 권한 요청 자체가 불가하기 때문에 시스템 설정에서 사용자가 직접 설정 값을 변경하도록 유도해야 함
3-2. 사용자 디바이스의 위치 서비스가 활성화 상태라면, 앱에 대한 권한 상태 확인
- 앱에 대한 권한 상태는 CLAuthorizationStatus라는 열거형 타입으로 표현됨
- iOS 14를 기준으로 코드가 변경되어 분기 처리 필요
func checkUserDeviceLocationServiceAuthorization() {
// 3.1
guard CLLocationManager.locationServicesEnabled() else {
// 시스템 설정으로 유도하는 커스텀 얼럿
showRequestLocationServiceAlert()
return
}
// 3.2
let authorizationStatus: CLAuthorizationStatus
// 앱의 권한 상태 가져오는 코드 (iOS 버전에 따라 분기처리)
if #available(iOS 14.0, *) {
authorizationStatus = locationManager.authorizationStatus
}else {
authorizationStatus = CLLocationManager.authorizationStatus()
}
// 권한 상태값에 따라 분기처리를 수행하는 메서드 실행
checkUserCurrentLocationAuthorization(authorizationStatus)
}
4. 앱에 대한 위치 권한이 부여된 상태인지 확인하는 메서드 추가
- 사용자가 앱에 설정한 권한 상태를 매개변수로 입력받고 분기 처리하는 메서드 생성 (3번에서 작성한 메서드에서 호출)
- CLAuthorizationStatus (권한 상태를 나타내는 열거형 타입)
- .notDetermined : 사용자가 권한에 대한 설정을 선택하지 않은 상태
- .restricted : 위치 서비스에 대한 관한이 없는 상태 / 자녀 보호 기능과 같은 상황으로 디바이스 자체에 활성이 제한된 상태
- .denined
- 사용자가 앱에 대한 권한을 거부한 상태
- 권한을 승인했더라도 추후에 시스템 설정에서 비활성화 한 경우
- 사용자가 디바이스 전체에 대한 위치 서비스를 비활성화 한 경우
- 비행기 모드와 같은 상황으로, 위치 서비스를 이용할 수 없는 상황
- .authorizedAlways : 앱이 백그라운드 상태에서도 위치 서비스를 이용할 수 있도록 승인된 상태
- .authorizedWhenInUse : 앱이 포그라운드 상태에서만 위치 서비스를 이용할 수 있도록 승인된 상태
- desiredAccuracy (위치 데이터의 정확도 설정)
- kCLLocatoinAccuracyBestForNavigation - 가장 높은 수준의 정확도, 외부 전원이 연결되어 있을 경우에만 사용
- kCLLocatoinAccuracyBest - 베터리로 동작할 때 권장되는 가장 높은 수준의 정확도
- kCLLocatoinAccuracyNearestTenMeters - 10미터 이내의 정확도
- kCLLocatoinAccuracyHundredMeters - 100미터 이내의 정확도
- kCLLocatoinAccuracyKilometer - 1키로미터 이내의 정확도
- kCLLocatoinAccuracyThreeKilometers - 3키로미터 이내의 정확도
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")
}
}
사용자의 현재 위치를 가져오는 2가지 방법
- requestLocation() : 한 번만 위치를 요청
- startUpdatingLocation() : 현재 위치를 지속적으로 요청
- 더 이상 지속적으로 받아올 필요 없다면 stopUpdatingLocation() 사용하여 멈춤
5. Delegate설정 & Delegate Protocol 채택
5-1. 2단계에서 생성한 CLLocationManager의 인스턴스에 delegate 설정
class ViewController: UIViewController {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
}
}
5-2. CLLocationManagerDelegate 프로토콜을 채택하고, Delegate 메서드를 구현
CLLocationManagerDelegate Methods
- 사용자의 위치를 성공적으로 가져왔을 때 호출 : didUpdateLocations
- 사용자의 위치를 가져오지 못한 경우 호출 : didFailWithError
- 앱에 대한 권한 설정이 변경되면 호출
- iOS 14 이상 ) locationManagerDidChangeAuthorization
- iOS 14 미만 ) didChangeAuthorization
extension ViewController: CLLocationManagerDelegate {
// 사용자의 위치를 성공적으로 가져왔을 때 호출
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// 위치 정보를 배열로 입력받는데, 마지막 index값이 가장 정확하다고 한다.
if let coordinate = locations.last?.coordinate {
// ⭐️ 사용자 위치 정보 사용
}
// startUpdatingLocation()을 사용하여 사용자 위치를 가져왔다면
// 불필요한 업데이트를 방지하기 위해 stopUpdatingLocation을 호출
locationManager.stopUpdatingLocation()
}
// 사용자가 GPS 사용이 불가한 지역에 있는 등 위치 정보를 가져오지 못했을 때 호출
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(#function)
}
// 앱에 대한 권한 설정이 변경되면 호출 (iOS 14 이상)
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
// 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 호출
checkUserDeviceLocationServiceAuthorization()
}
// 앱에 대한 권한 설정이 변경되면 호출 (iOS 14 미만)
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// 사용자 디바이스의 위치 서비스가 활성화 상태인지 확인하는 메서드 호출
checkUserDeviceLocationServiceAuthorization()
}
}
디바이스의 시스템 설정으로 유도하는 커스텀 alert
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) { [weak self] _ in
async { await self?.reloadData() }
}
requestLocationServiceAlert.addAction(cancel)
requestLocationServiceAlert.addAction(goSetting)
present(requestLocationServiceAlert, animated: true)
}
SwiftUI 적용
1. 클래스 선언 및 프로퍼티 초기화
class CoreLocationEx: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var latitude: Double = 0.0
@Published var longitude: Double = 0.0
@Published var altitude: Double = 0.0
var locationManager = CLLocationManager()
override init() {
super.init()
self.setupLocationManager()
}
- 위도, 경도, 고도가 변경될 때마다 화면과 연결하기 위해 @Published 속성 사용
- 초기화 메서드에서 ‘setupLocationManager’ 메서드를 호출하여 locationManager의 설정 초기화
- NSObject 는 init을 위해 사용
2. setupLocationManager 메서드
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
print("위치 서비스 On 상태")
locationManager.startUpdatingLocation()
print(locationManager.location?.coordinate as Any)
} else {
print("위치 서비스 Off 상태")
}
}
- locationManager의 delegate를 현재 클래스로 설정
- locationManager.desiredAccuracy = kCLLocationAccuracyBest : 위치 정확도를 kCLLocationAccuracyBest로 설정
- locationManager.requestWhenInUseAuthorization() : 위치 서비스를 사용하기 위한 권한 요청
- 위치 서비스가 활성화 되어있으면 위치 업데이트 시작, 현재 위치 좌표 출력
3. locationManager(_ : didUpdateLocations: ) 메서드
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
latitude = location.coordinate.latitude
longitude = location.coordinate.longitude
altitude = location.altitude
}
}
- 위치 정보가 업데이트 될 때 호출되는 메서드
- 위도, 경도, 고도 업데이트
4. locationManagerDidChangeAuthorization 및 관련 메서드
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
DispatchQueue.global().async {
self.checkUserDeviceLocationServiceAuthorization()
}
}
func checkUserDeviceLocationServiceAuthorization() {
// ...
}
func checkUserCurrentLocationAuthorization(_ status: CLAuthorizationStatus) {
// ...
}
func showRequestLocationServiceAlert() {
// ...
}
- 위치 권한이 변경되었을 때 호출되는 메서드
- locationManagerDidChangeAuthorization 메서드가 메인 스레드에서 호출되면 UI 블록이 발생할 수 있음
- 이 메서드 내에서 checkUserDeviceLocationServiceAuthorization 메서드를 호출하는 것은 긴 시간이 걸려 UI가 응답하지 않는 문제가 발생할 수 있음 → 비동기 처리
- 비동기적으로 checkUserDeviceLocationServiceAuthorization 메서드를 호출
- checkUserDeviceLocationServiceAuthorization 메서드에서는 위치 서비스 활성화 여부를 확인하고 권한에 따라 적절한 조치를 취함
DispatchQueue.global().async
- Grand Central Dispatch(GCD)를 사용하여 비동기적으로 작업을 실행하는 방법
- DispatchQueue.global()은 전역(dispatch global) DispatchQueue 를 반환 함
- 이는 여러 스레드에서 작업을 동시에 실행할 수 있도록 하는 DispatchQueue
- async 메서드는 주어진 블록을 비동기적으로 실행할 수 있도록 스케줄링
- locationManagerDidChangeAuthorization 메서드 내에서 DispatchQueue.global().async 를 사용하면, checkUserDeviceLocationServiceAuthorization 메서드가 백그라운드 스레드에서 비동기적으로 실행되게 됨 → 현재 메인 스레드가 차단되지 않고 다른 작업 수행 가능
5. showRequestLocationServiceAlert 메서드
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)
}
}
requestLocationServiceAlert.addAction(goSetting)
// present(requestLocationServiceAlert, animated: true)
}
- 위치 서비스가 비활성화되어 있는 경우, 사용자가 위치 서비스를 활성화 할 수 있도록 알람 표시
- 설정으로 이동하는 버튼을 클릭하면 위치 서비스 부분으로 이동
'SwiftUI' 카테고리의 다른 글
SwiftUI - 리스트와 내비게이션 (0) | 2024.02.21 |
---|---|
SwiftUI - 스택 정렬과 정렬 가이드 (0) | 2024.02.02 |
SwiftUI - Observable 객체와 Environment 객체 튜토리얼 (2) | 2024.01.27 |
SwiftUI - 슬라이더 뷰(Slider View) 사용 (1) | 2024.01.22 |
SwiftUI - 상태 프로퍼티 (State), Observable 객체, Environment 객체 사용 (0) | 2024.01.15 |