
Location is one of those features that feels complicated until you actually do it then you realize SwiftUI and CoreLocation make it surprisingly easy. In this quick tutorial we'll build a tiny app that finds your location and tells you how far you are from a famous landmark. It's a great first taste of CoreLocation, and the whole thing is about 60 lines of code.
Here's what we're building: tap a button, grant location access, and the app shows your coordinates plus your distance in miles from the Golden Gate Bridge.
What you'll need
- Xcode 15 or later
- An iOS 17+ project (we use the
@Observablemacro) - A few minutes
Step 1: Create the project
Open Xcode and choose File → New → Project, then pick the iOS App template. Give it a name like HowFarAmI, and make sure the Interface is set to SwiftUI and the Language is Swift. Click through to create it, and Xcode will hand you a starter project with a ContentView.swift file already in place — we'll come back to that in a moment.
Step 2: Add the privacy key (don't skip this!)
This is the step that trips everyone up. iOS will silently refuse to ask for location permission unless you tell it why you need it.
In your project, select your target → Info tab, then in the "Custom iOS Target Properties" table Hover over any existing row in that table (any of the rows already listed). A small + and – button appear at the end of the row when you hover over it. Click the +. Xcode inserts a new blank row with the key field ready to edit. Enter the following:
- Key:
NSLocationWhenInUseUsageDescription - Value:
We use your location to show how far you are from a landmark.
Xcode uses friendly display names in this table, so as you start typing it'll autocomplete to "Privacy - Location When In Use Usage Description" that's the same thing as NSLocationWhenInUseUsageDescription, just shown in plain English. Pick it from the dropdown.Newer Xcode projects don't show a separate Info.plist file — the Info tab under your target settings is where this lives now.
Step 3: Build the LocationManager
This class is the brains of the operation. It talks to CoreLocation, asks for permission, and hands us back a location. Create a new file called LocationManager.swift: File → New → File from Template… → (iOS tab) → Swift File → Next → name it LocationManager → Create.
import CoreLocation
@Observable
class LocationManager: NSObject, CLLocationManagerDelegate {
private let manager = CLLocationManager()
var location: CLLocation?
var authorizationStatus: CLAuthorizationStatus = .notDetermined
override init() {
super.init()
manager.delegate = self
}
func requestLocation() {
manager.requestWhenInUseAuthorization()
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
authorizationStatus = manager.authorizationStatus
if authorizationStatus == .authorizedWhenInUse {
manager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
location = locations.last
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Failed to get location: \(error.localizedDescription)")
}
}
A few things worth understanding here:
requestLocation()kicks off the permission prompt.locationManagerDidChangeAuthorizationfires when the user responds. As soon as they say yes, we ask for an actual location reading.didUpdateLocationsdelivers the result, and we store the most recent one.
Because the class is marked @Observable, SwiftUI automatically refreshes the screen whenever location changes.
Step 4: Build the screen
Now the fun part. Open ContentView.swift and replace it with this:
import SwiftUI
import CoreLocation
struct ContentView: View {
@State private var locationManager = LocationManager()
let landmark = CLLocation(latitude: 37.8199, longitude: -122.4783) // Golden Gate Bridge
let landmarkName = "the Golden Gate Bridge"
var body: some View {
VStack(spacing: 24) {
Image(systemName: "location.circle.fill")
.font(.system(size: 70))
.foregroundStyle(.blue)
Text("How Far Am I?")
.font(.largeTitle.bold())
if let location = locationManager.location {
Text("You're here:")
.foregroundStyle(.secondary)
Text(String(format: "%.4f, %.4f",
location.coordinate.latitude,
location.coordinate.longitude))
.font(.headline)
let miles = location.distance(from: landmark) / 1609.34
Text(String(format: "%.1f miles", miles))
.font(.system(size: 44, weight: .bold))
.foregroundStyle(.blue)
Text("from \(landmarkName)")
.foregroundStyle(.secondary)
} else {
Text("Tap below to find your location")
.foregroundStyle(.secondary)
}
Button("Find My Location") {
locationManager.requestLocation()
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
}
.padding()
}
}
The magic line is this one:
let miles = location.distance(from: landmark) / 1609.34
CoreLocation's distance(from:) does all the heavy lifting — it calculates the real-world distance between two coordinates and returns it in meters. We divide by 1609.34 to convert to miles. Want kilometers instead? Divide by 1000.
Step 5: Run it (and fake your location)
If you run this in the Simulator and the screen stays blank, don't panic — the Simulator has no real GPS. You need to give it a location:
In the Simulator menu, go to Features → Location → Custom Location and enter any coordinates, or pick one of the built-in cities. Now tap Find My Location and watch the distance appear.
On a real device, you'll see the permission prompt the first time, then your actual distance from the bridge.
Make it your own
A few easy tweaks to experiment with:
- Change the landmark. Swap in the coordinates of your hometown, the Eiffel Tower, or anywhere else. Just grab the latitude and longitude.
- Switch to kilometers. Divide by 1000 instead of 1609.34.
- Add more landmarks. Show distances to a whole list of places at once.
That's the entire app. In under 60 lines you've handled permissions, fetched a live location, and done real geographic math — the same foundation behind every maps, weather, and delivery app you use.
Happy coding!