참고 사이트
https://velog.io/@maddie/iOS-UIKit-CoreLocation-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%A0%95%EB%A6%AC
[iOS] UIKit + CoreLocation 사용법 정리
CoreLocation을 사용해보자 대부분의 프레임워크들은 매니저와 같은 중심부가 구현되어있음 locations - 배열로 들어옴(무슨 해양,, 그런거까지 정보가 많음) Privacy - Location When In Use Usage Description ->
velog.io
https://co-dong.tistory.com/73
iOS) 위치 정보 사용하기 (CoreLocation)
iOS에서 앱을 사용하다 보면 아래와 같은 권한 요청 메시지를 자주 보게 되는데, 보안을 중요하게 생각하는 애플이 위치를 포함한 카메라, 연락처 등 민감한 개인정보에 대해 사용자에게 먼저 권
co-dong.tistory.com
전체적인 흐름
- 앱 시작 또는 위치 서비스가 필요한 기능 활성화 시:
- checkUserDeviceLocationServiceAuthorization() 호출하여 위치 서비스가 활성화되어 있는지 확인하고 권한을 요청합니다.
- 권한 상태가 변경될 때:
- locationManagerDidChangeAuthorization(_:) 호출되어 권한 상태에 따라 추가 작업을 수행합니다.
- 위치 서비스가 활성화되고 권한이 부여된 경우:
- locationManager(_:didUpdateLocations:)에서 위치를 업데이트하고, 주소로 변환하며, 관광지 데이터를 가져옵니다.
- 위치 서비스가 비활성화된 경우:
- showRequestLocationServiceAlert() 호출하여 사용자에게 위치 서비스를 활성화하도록 안내합니다.
info.plist에 필요한 권한 추가 & Description 작성
CLLocationManagerDelegate 채택 및 위치 정보에 사용될 인스턴스 생성
- locationManager: CLLocationManager 타입의 인스턴스입니다.
- CLLocationManager는 iOS에서 위치 서비스를 관리하는 객체로, 앱이 사용자의 위치를 얻거나 지속적으로 추적할 수 있도록 해줍니다.
- locationManager를 사용하여 사용자의 현재 위치, 권한 상태, 위치 업데이트 등을 요청하고 처리할 수 있습니다.
- 예를 들어, locationManager.startUpdatingLocation()을 호출하면 현재 위치를 추적하기 시작하고, 사용자의 위치가 변경될 때마다 locationManager(_:didUpdateLocations:) 델리게이트 메서드가 호출됩니다.
- geocoder: CLGeocoder 타입의 인스턴스입니다.
- CLGeocoder는 지오코딩과 리버스 지오코딩을 수행하는 객체로, 위도와 경도를 주소로 변환하거나 그 반대로 주소를 위도와 경도로 변환할 수 있습니다.
- geocoder.reverseGeocodeLocation(location)을 사용하여 CLLocation 객체를 전달하면 해당 좌표에 대한 주소 정보를 반환할 수 있습니다.
- 이를 통해 사용자의 위치를 텍스트 주소로 변환하여 UI에 표시할 수 있습니다.
class HomeViewController: UIViewController, CLLocationManagerDelegate {
// 위치 정보
let locationManager = CLLocationManager()
let geocoder = CLGeocoder()
checkUserDeviceLocationServiceAuthorization() 실행
- 사용자의 위치 서비스가 활성화되어 있는지 확인하고, 권한을 요청하는 메서드입니다.
- 이 메서드는 앱이 시작되거나 위치 서비스가 필요한 기능을 호출하기 전에 먼저 호출되어야 합니다.
override func viewDidLoad() {
super.viewDidLoad()
...
checkUserDeviceLocationServiceAuthorization()
func checkUserDeviceLocationServiceAuthorization() {
// 3.1 디바이스 자체에 위치 서비스가 활성화 상태인지 확인한다.
DispatchQueue.global().async {
guard CLLocationManager.locationServicesEnabled() else {
// 시스템 설정으로 유도하는 커스텀 얼럿
self.showRequestLocationServiceAlert()
return
}
}
// 위치 서비스가 활성화 상태라면 권한 오청
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
}
locationManagerDidChangeAuthorization(_:)
- 위치 서비스 권한 상태가 변경될 때 호출되는 메서드입니다.
- 위치 서비스 권한이 변경될 때 필요한 추가 작업을 처리합니다.
- checkUserDeviceLocationAuthorization() 메서드에서 권한 요청 후 이 메서드가 호출되므로, 권한 상태에 따른 처리가 필요합니다.
// iOS 14 이상에서는 권한 상태를 델리게이트 메서드에서 처리
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
// 3.2 사용자 디바이스의 위치 서비스가 활성화 상태라면, 앱에 대한 권한 상태를 확인해야 한다.
let authorizationStatus: CLAuthorizationStatus
// 앱의 권한 상태 가져오는 코드 (iOS 버전에 따라 분기처리)
if #available(iOS 14.0, *) {
authorizationStatus = manager.authorizationStatus
}else {
authorizationStatus = CLLocationManager.authorizationStatus()
}
// 권한 상태값에 따라 분기처리를 수행하는 메서드 실행
checkUserCurrentLocationAuthorization(authorizationStatus)
}
checkUserCurrentLocationAuthorization(_:)
- locationManagerDidChangeAuthorization(_:)에서 호출되어 앱에 대한 위치 권한 상태를 확인하고 처리합니다.
- 사용자의 권한 상태에 따라 위치 서비스를 시작하거나, 권한 요청을 하거나, 위치 서비스가 비활성화된 경우 얼럿을 표시합니다.
func checkUserCurrentLocationAuthorization(_ status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
// 사용자가 권한에 대한 설정을 선택하지 않은 상태
print("Not determained")
// 권한 요청을 보내기 전에 desiredAccuracy 설정 필요
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
// 권한 요청을 보낸다.
case .denied, .restricted:
// 사용자가 명시적으로 권한을 거부했거나, 위치 서비스 활성화가 제한된 상태
// 시스템 설정에서 설정값을 변경하도록 유도한다.
// 시스템 설정으로 유도하는 커스텀 얼럿
print("Restricted or denied")
showRequestLocationServiceAlert()
case .authorizedWhenInUse:
// 앱을 사용중일 때, 위치 서비스를 이용할 수 있는 상태
// manager 인스턴스를 사용하여 사용자의 위치를 가져온다.
print("Authorized")
locationManager.startUpdatingLocation()
default:
print("Default")
}
}
showRequestLocationServiceAlert()
- 위치 서비스가 비활성화된 경우 사용자에게 설정으로 이동하도록 안내하는 얼럿을 보여주는 메서드입니다.
- checkUserCurrentLocationAuthorization(_:) 메서드에서 위치 서비스가 비활성화된 경우 호출됩니다.
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
self?.reloadData() // 여기에 await는 필요하지 않습니다.
}
requestLocationServiceAlert.addAction(cancel)
requestLocationServiceAlert.addAction(goSetting)
present(requestLocationServiceAlert, animated: true)
}
reverseGeocode(location:completion:)
- 위도와 경도를 주소로 변환하는 메서드입니다.
- 위치 업데이트를 받았을 때 (locationManager(_:didUpdateLocations:)) 호출됩니다.
// 3. Reverse Geocoding을 사용하여 위도와 경도를 주소로 변환하는 메서드
// 외부에서 호출할 때 userLocation이 설정된 후 실행할 동작을 정의할 수 있도록 completion handler를 추가합니다.
func reverseGeocode(location: CLLocation, completion: @escaping (String?) -> Void) {
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
if let error = error {
print("Reverse geocoding failed: \(error.localizedDescription)")
completion(nil) // 에러가 발생한 경우 nil을 반환
return
}
guard let placemark = placemarks?.first else {
print("No placemark found")
completion(nil) // placemark가 없는 경우 nil을 반환
return
}
// 지번 주소 구성
// let country = placemark.country ?? ""
let administrativeArea = placemark.administrativeArea ?? ""
let locality = placemark.locality ?? ""
let subLocality = placemark.subLocality ?? ""
// thoroughfare와 subThoroughfare는 생략
let jibunAddress = "\(administrativeArea) \(locality) \(subLocality)"
// userLocation에 값을 할당
self.userLocation = jibunAddress
// 완료된 후 jibunAddress를 completion handler로 전달
completion(jibunAddress)
}
}
locationManager(_:didUpdateLocations:)
- 위치 업데이트를 받을 때 호출되는 메서드입니다.
- 이 메서드에서 현재 위치를 가져와 주소로 변환하고, 관광지 데이터를 요청하며, UI를 업데이트합니다.
- 이 메서드에서 reverseGeocode(location:completion:) 메서드를 호출하여 주소로 변환한 후, NetworkManager.shared.getSpotDataFromLocation()을 호출하여 관광지 데이터를 가져옵니다.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
self.userLatitude = "\(location.coordinate.latitude)"
self.userLongitude = "\(location.coordinate.longitude)"
// 경도와 위도를 통해 지번/도로명 주소 변환
reverseGeocode(location: location) { userLocation in
if let userLocation = userLocation {
print("User location: \(userLocation)")
// 여기서 userLocation을 사용하여 추가 작업 수행 가능
// 메인 스레드에서 UI 업데이트
DispatchQueue.main.async {
self.getHomSubTitleView(main: "동동이님, 근처에는 말이에요 😄", sub: "현재 위치: \(userLocation)")
// 관광지 데이터 가져오기
NetworkManager.shared.getSpotDataFromLocation(mapX: self.userLongitude, mapY: self.userLatitude, contentTypeId: self.selectedContentTypeId) { [weak self] result in
switch result {
case .success(let item):
// 데이터를 받아온 후 첫 번째 아이템을 사용하여 configureData 호출
self?.locationReceivedItems = item
DispatchQueue.main.async {
self?.homeView.getHomeContentView().placeTableView.customPlaceTableView.reloadData() // 여기서 테이블 뷰를 다시 로드합니다.
}
case .failure(let error):
print("Failed to fetch attraction data: \(error)")
}
}
}
} else {
print("Failed to retrieve user location")
}
}
}
'Swift' 카테고리의 다른 글
compactMap - 새로운 배열 생성 (1) | 2024.09.21 |
---|---|
공공 API를 통해 데이터를 가져오는 중에 발생한 오류 해결 (1) | 2024.09.21 |
heightAnchor랑 bottomAnchor는 뭐가 다를까? (0) | 2024.09.06 |
외부 API를 받는 데이터 함수의 데이터 타입 관리 (0) | 2024.09.06 |
데이터 모델 이름을 바꿔야 한다면? (0) | 2024.09.06 |