How to get Current Location using SwiftUI, without ViewControllers?
You could create a StateObject
of your LocationManager
by implementing the ObservableObject
protocol.
With the @Published
attribute you can create a publisher object which notify the observers (your view, in this case) when something changes inside that object.
That's why in my LocationManager I added the @Published
attribute to those var:
locationStatus: CLAuthorizationStatus?
it contains the value received fromdidChangeAuthorization
delegate methodlastLocation: CLLocation?
it contains the last location calculated by thedidUpdateLocations
delegate method
LocationManager
import Foundation
import CoreLocation
import Combine
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
@Published var locationStatus: CLAuthorizationStatus?
@Published var lastLocation: CLLocation?
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
var statusString: String {
guard let status = locationStatus else {
return "unknown"
}
switch status {
case .notDetermined: return "notDetermined"
case .authorizedWhenInUse: return "authorizedWhenInUse"
case .authorizedAlways: return "authorizedAlways"
case .restricted: return "restricted"
case .denied: return "denied"
default: return "unknown"
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
locationStatus = status
print(#function, statusString)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
lastLocation = location
print(#function, location)
}
}
View
In your view you need to create only an instance of LocationManager
marked as @StateObject
import SwiftUI
struct MyView: View {
@StateObject var locationManager = LocationManager()
var userLatitude: String {
return "\(locationManager.lastLocation?.coordinate.latitude ?? 0)"
}
var userLongitude: String {
return "\(locationManager.lastLocation?.coordinate.longitude ?? 0)"
}
var body: some View {
VStack {
Text("location status: \(locationManager.statusString)")
HStack {
Text("latitude: \(userLatitude)")
Text("longitude: \(userLongitude)")
}
}
}
}
struct MyView_Previews: PreviewProvider {
static var previews: some View {
MyView()
}
}
SwiftUI: How to get the current location of user?
Yes, it's been deprecated in favour of using a property of the same name instead of a function, try it without the parentheses...
if manager.authorizationStatus == .authorizedWhenInUse {
How to get Current Location with SwiftUI?
This code below works (Not production ready). Implementing the CLLocationManagerDelegate
works fine and the lastKnownLocation
is updated accordingly.
Don't forget to set the NSLocationWhenInUseUsageDescription
in your Info.plist
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
private let manager = CLLocationManager()
var lastKnownLocation: CLLocation?
func startUpdating() {
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations)
lastKnownLocation = locations.last
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
manager.startUpdatingLocation()
}
}
}
Retrieve user current location using SwiftUI and MapKit
Create instance of CLLocationManager
:
var locManager = CLLocationManager()
locManager.requestWhenInUseAuthorization()
then get the details:
var currentLocation: CLLocation!
if( CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == .authorizedAlways){
currentLocation = locManager.location
}
then to get longitude or latitude:
let longitude = currentLocation.coordinate.longitude
let latitude = currentLocation.coordinate.latitude
If you'd like to perform it inside a swiftUI view script, create instance:
@ObservedObject var locationManager = LocationManager()
Get them separately:
var userLatitude: String {
return "\(locationManager.lastLocation?.coordinate.latitude ?? 0)"
var userLongitude: String {
return "\(locationManager.lastLocation?.coordinate.longitude ?? 0)"
Automatically include user’s current location in a SMS (Swift)
You are mixing the code of UIKit and SwiftUI. First You have to create location Manager class and then assign that class to StateObject and use it in SWiftUI View.
Use following as Location manager : -
class LocationManager: NSObject, ObservableObject,CLLocationManagerDelegate {
let manager = CLLocationManager()
@Published var location: CLLocationCoordinate2D?
override init() {
super.init()
manager.delegate = self
}
func requestLocation() {
manager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
location = locations.first?.coordinate
}
}
Now Initialize locationManager View Do as Following : -
@StateObject var locationManager = LocationManager()
Then request location using following code whenever you want to access user location : -
locationManager.requestLocation()
And now you can access location Using following : -
locationManager.location.latitude
or
locationManager.location.longitude
SwiftUI mapkit set region to user's current location
Here's one possible solution to this:
final class LocationManager: NSObject, ObservableObject {
@Published var location: CLLocation?
@Published var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 38.898150, longitude: -77.034340),
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
)
private var hasSetRegion = false
private let locationManager = CLLocationManager()
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.distanceFilter = kCLDistanceFilterNone
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
self.location = location
if !hasSetRegion {
self.region = MKCoordinateRegion(center: location.coordinate,
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
hasSetRegion = true
}
}
}
}
struct ShowMapView: View {
@ObservedObject private var locationManager = LocationManager()
var homeLocation : [AnnotationItem] {
guard let location = locationManager.location?.coordinate else {
return []
}
return [.init(name: "Home", coordinate: location)]
}
var body: some View {
Map(coordinateRegion: $locationManager.region, annotationItems: homeLocation) {
MapPin(coordinate: $0.coordinate)
}
.frame(height: 300)
}
}
In this solution, the region is published by the location manager. As soon as a location is received, the region is centered on that spot (in didUpdateLocations
). Then, a boolean flag is set saying the region has been centered initially. After that boolean is set, it no longer updates the region. This will let the user still drag/zoom, etc.
I also changed your code for putting down the pin a little bit. You were force-unwrapping location
, which is nil until the first location is set by the location manager, causing a crash. In my edit, it just returns an empty array of annotation items if there isn't a location yet.
In widget using swiftui how to get current location?
class WidgetLocationManager: NSObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager?
private var handler: ((CLLocation) -> Void)?
override init() {
super.init()
DispatchQueue.main.async {
self.locationManager = CLLocationManager()
self.locationManager!.delegate = self
if self.locationManager!.authorizationStatus == .notDetermined {
self.locationManager!.requestWhenInUseAuthorization()
}
}
}
func fetchLocation(handler: @escaping (CLLocation) -> Void) {
self.handler = handler
self.locationManager!.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.handler!(locations.last!)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
}
call
var widgetLocationManager = WidgetLocationManager()
func getTimeline(for configuration: SelectPlaceIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
widgetLocationManager.fetchLocation(handler: { location in
print(location)
.......
})
}
How can I call an API with user's current location on app launch in SwiftUI?
As Joakim mentioned, you need to use .onAppear
. This is due to the fact that you cannot access a property before it is set in a struct.
For your case, the code would look like this:
struct ContentView: View {
@StateObject var locationManager = LocationManager()
var userLocation: String {
let latitude = "\(locationManager.lastLocation?.coordinate.latitude ?? 0)"
let longitude = "\(locationManager.lastLocation?.coordinate.longitude ?? 0)"
return "\(latitude),\(longitude)"
}
@ObservedObject var api: randomAPI?
var body: some View {
SomeView()
.onAppear {
api = randomAPI(location: userLocation)
}
...
However, I don't think setting an ObservedObject
right after the view appears is the best way to do this.
If I were you, I would remove the init method in the rapidAPI
class:
class randomAPI: ObservableObject {
func callAPI(userLocation: String) {
...
And call the callAPI
method when the view appears:
struct ContentView: View {
@StateObject var locationManager = LocationManager()
var userLocation: String {
let latitude = "\(locationManager.lastLocation?.coordinate.latitude ?? 0)"
let longitude = "\(locationManager.lastLocation?.coordinate.longitude ?? 0)"
return "\(latitude),\(longitude)"
}
@ObservedObject var api = randomAPI()
var body: some View {
SomeView()
.onAppear {
api.callAPI(location: userLocation)
}
...
Current Location is Nil In SwiftUI
I got the answer to my question and it is that you have to just add one line in case .authorizedAlways, .authorizedWhenInUse:
currentLocation = locManager.location
The whole code will:
let locManager = CLLocationManager()
locManager.requestWhenInUseAuthorization()
var currentLocation: CLLocation?
switch locManager.authorizationStatus {
case .notDetermined, .restricted, .denied:
print("lat long will nil")
case .authorizedAlways, .authorizedWhenInUse:
currentLocation = locManager.location
let lati = currentLocation?.coordinate.latitude
let long = currentLocation?.coordinate.longitude
print("lat: \(lati) long: \(long)")
@unknown default:
print("error getting location")
}
Related Topics
Swift: How to Fully Strip Internal/Inline Symbols
Prettier Debug Output Printing Swift Dictionary in Xcode
Swift Error When Trying to Access Dictionary: 'Could Not Find Member 'Subscript''
Enumerateobjectsusingblock in Swift
Xcode Autocomplete Does Not Work in Sources Folder of Swift Playgrounds
Difference Between Dispatchqueue Types in Swift
Swift3:How to Handle Precedencegroup Now Operator Should Be Declare with a Body
React Native Sending Events to JavaScript in Swift
Create PDF of Dynamic Size with Typography Using Uiview Template(S)
Get the Current Position of Scrollview in Swiftui
How to Record Video in Realitykit
Combining Two Conditions in Nspredicate
How to Perform a Curl Request in Swift
Uiimage Created from Mtkview Results in Color/Opacity Differences