This is the great home-made product in Swift for easily managed
Location Services API.
1:Be awared of Location Services Status.
2:Request permission for Location Services.
3:Redirect to System Settings (Preferences).
4:Get Current Location.
5:Request Location Updates.
PGKis a single author and personale solution developed inP2Prelationship paradigm.
PGKSupportingStar.swiftis a peace of code a widly helpful in accord with PGK.
PGKSupportingStar.swiftgoes as an external part of PGK.
CPLstands forConsolePerseusLogger.
PGKstands forPerseusGeoKit.
PDMstands forPerseusDarkMode.
P2Pstands forPerson-to-Person.
A3stands forAppleAppsApprobation.
T3stands forTheTechnologicalTree.
In approbation:iOS appmacOS app
In business:The Dark Moon
For details:Approbation and A3 Environment/CHANGELOG
- In brief
- Build requirements
- First-party software
- Third-party software
- Installation
- Usage
- Points taken into account
- License
- Credits
- Author
HAVE A DEAL WITH WHERE YOU ARE
| Approbation App: iOS 16.2 | Approbation App: macOS Monterey |
|---|---|
![]() The Current Location Dialog ![]() The Redirect Dialog ![]() System Services
|
![]() The Current Location Dialog ![]() System Services and The Redirect Dialog
|
Note
The iOS App scenes taken from the motion picture The Lord of The Rings based on the novel by J.R.R. Tolkien.
The macOS App scenes taken from the motion picture The Hobbit based on the novel by J.R.R. Tolkien.
| Type | Name | License |
|---|---|---|
| Star | ConsolePerseusLogger / 1.7.0 | MIT |
| Type | Name | License |
|---|---|---|
| Style | SwiftLint / v0.57.0 for Monterey+ | MIT |
| Script | SwiftLint Shell Script to run SwiftLint | MIT |
| Action | mxcl/xcodebuild@v3 | Unlicense |
| Action | cirruslabs/swiftlint-action@v1 | MIT |
Step 1: Import PGK either with SPM or standalone.
Standalone: the single source code file PGKStar.swift
Swift Package Manager:
https://github.com/perseusrealdeal/PerseusGeoKit
Step 2: Change Info.plist, add the following items:
| iOS | macOS |
|---|---|
| NSLocationAlwaysAndWhenInUseUsageDescription | NSLocationUsageDescription |
| NSLocationWhenInUseUsageDescription |
Step 3, macOS: Tap Location in App Sandbox of your project target.
Location Services Status is calculated as a unified value for both iOS and macOS.
let status = GeoAgent.currentStatus
let statusInDetail = GeoAgent.aboutLocationServices().inDetail| Status | Status in Detail | Description |
|---|---|---|
| .notDetermined | .notDetermined | Not Authorized. Neither restricted nor the app denided. |
| .notAllowed | .deniedForAllAndRestricted | Location Services turned off and the app restricted. |
| .notAllowed | .restricted | Location Services turned on and the app restricted. |
| .notAllowed | .deniedForAllApps | Location Services turned off but the app not restricted. |
| .notAllowed | .deniedForTheApp | Location Services turned on but the app not restricted. |
| .allowed | .allowed | Authorized. |
Important
To be awared of Location Services Status changes, register:
GeoAgent.register(self, #selector(locationStatusHandler(_:)), .locationStatus)@objc private func locationStatusHandler(_ notification: Notification) {
// Location Status Change Handler.
}Statement GeoAgent.requestPermission() can be combined with the action to be called if status has already determined.
For instance, to open The Redirect Dialog.
GeoAgent.shared.requestPermission { status in
// Run if status not .notDetermined.
if status != .allowed {
GeoAgent.showRedirectAlert() // The Redirect Dialog.
}
}Note
Custom Text for The Redirect Dialog:
let REDIRECT_ALERT_TITLES = ActionAlertText(title: "The Redirect Dialog",
message: "Custom Message",
buttonCancel: "OK",
buttonFunction: "System Services")
GeoAgent.showRedirectAlert(REDIRECT_ALERT_TITLES)Note
iOS: The Redirect Dialog by GeoAgent.showRedirectAlert(vc) requires the parent ViewController.
GeoAgent.shared.requestPermission { status in
if status != .allowed, let vc = self.parentViewController() {
GeoAgent.showRedirectAlert(vc)
}
}Warning
Statement GeoAgent.shared.requestCurrentLocation() causes stop updating location.
Step 1: Register for Geo events both error and current location.
GeoAgent.register(self, #selector(locationErrorHandler(_:)), .locationError)
GeoAgent.register(self, #selector(currentLocationHandler(_:)), .currentLocation)Step 2: Set required accuracy up.
GeoAgent.currentAccuracy = .threeKilometersStep 3: Use GeoAgent.shared.requestCurrentLocation().
do {
try GeoAgent.shared.requestCurrentLocation()
} catch LocationError.permissionRequired(let status) { // Permission required.
if status == .notDetermined {
GeoAgent.shared.requestPermission() // Request permission.
} else {
GeoAgent.showRedirectAlert() // Offer redirect.
}
} catch {
// Something went wrong.
}Step 1: Register for Geo events both error and updating location.
GeoAgent.register(self, #selector(locationErrorHandler(_:)), .locationError)
GeoAgent.register(self, #selector(locationUpdatesHandler(_:)), .locationUpdates)Step 2: Set required accuracy up.
GeoAgent.currentAccuracy = .threeKilometersStep 3: Use GeoAgent.shared.requestUpdatingLocation().
do {
try GeoAgent.shared.requestUpdatingLocation()
} catch LocationError.permissionRequired(let status) { // Permission required.
if status == .notDetermined {
GeoAgent.shared.requestPermission() // Request permission.
} else {
GeoAgent.showRedirectAlert() // Offer redirect.
}
} catch {
// Something went wrong.
}Important
Statement GeoAgent.shared.stopUpdatingLocation() to stop updating location.
GeoAgent.shared.stopUpdatingLocation()Register first.
GeoAgent.register(self, #selector(locationErrorHandler(_:)), .locationError)Handle event.
@objc private func locationErrorHandler(_ notification: Notification) {
log.message("[\(type(of: self))].\(#function) [EVENT]")
var errtext = ""
guard let error = notification.object as? LocationError else {
errtext = "nothing is about error"
log.message("[\(type(of: self))].\(#function) \(errtext)", .error)
return
}
switch error {
case .failedRequest(let desc, let domain, let code):
let domaincode = "domain: \(domain), code: \(code)"
if desc.contains("[NOTKNOWN]") {
errtext = "\(desc), \(domaincode)"
} else {
switch code {
case 0:
errtext = "hardware issue: try to tap Wi-Fi in system tray, \(domaincode)"
case 1:
errtext = "permission required, \(domaincode)"
default:
break
}
}
default:
break
}
log.message("[\(type(of: self))].\(#function) \(errtext)", .error)
// Any changes.
}Register first.
GeoAgent.register(self, #selector(locationStatusHandler(_:)), .locationStatus)Handle event.
@objc private func locationStatusHandler(_ notification: Notification) {
let currentStatus = GeoAgent.currentStatus
log.message("[\(type(of: self))].\(#function) status: \(status) [EVENT]")
// Current Status is here, it's always actual value. Any changes.
}Register first.
GeoAgent.register(self, #selector(currentLocationHandler(_:)), .currentLocation)Handle event.
@objc private func currentLocationHandler(_ notification: Notification) {
log.message("[\(type(of: self))].\(#function) [EVENT]")
var errtext = ""
var location: GeoPoint?
guard let result = notification.object as? Result<GeoPoint, LocationError> else {
errtext = "nothing is about location"
log.message("[\(type(of: self))].\(#function) \(errtext)", .error)
return
}
switch result {
case .success(let point):
location = point
case .failure(let error):
errtext = "\(error)"
}
if let current = location {
// Current location is here! Any changes.
} else if !errtext.isEmpty {
log.message("[\(type(of: self))].\(#function) \(errtext)", .error)
}
}Register first.
GeoAgent.register(self, #selector(locationUpdatesHandler(_:)), .locationUpdates)Handle event.
@objc private func locationUpdatesHandler(_ notification: Notification) {
log.message("[\(type(of: self))].\(#function) [EVENT]")
var errtext = ""
var updates: [GeoPoint]?
guard let result = notification.object as? Result<[GeoPoint], LocationError> else {
errtext = "nothing is about location updates"
log.message("[\(type(of: self))].\(#function) \(errtext)", .error)
return
}
switch result {
case .success(let points):
updates = points
case .failure(let error):
errtext = "\(error)"
}
if let locations = updates {
// Location updates are here! Any changes.
} else if !errtext.isEmpty {
log.message("[\(type(of: self))].\(#function) \(errtext)", .error)
}
}- Preconfigured Swift Package manifest Package.swift
- Preconfigured SwiftLint config .swiftlint.yml
- Preconfigured SwiftLint CI swiftlint.yml
- Preconfigured GitHub config .gitignore
- Preconfigured GitHub CI main.yml
License: MIT
Copyright © 7531 - 7534 Mikhail A. Zhigulin of Novosibirsk
Copyright © 7533 - 7534 PerseusRealDeal
- The year starts from the creation of the world according to a Slavic calendar.
- September, the 1st of Slavic year. It means that "Sep 01, 2025" is the beginning of 7534.
© 2025 The SwiftLint Contributors for SwiftLint
© GitHub for GitHub Action cirruslabs/swiftlint-action@v1
© 2021 Alexandre Colucci, geteimy.com for Shell Script SucceedsPostAction.sh
LICENSE for details.
| Balance and Control | kept by | Mikhail Zhigulin |
| Source Code | written by | Mikhail Zhigulin |
| Documentation | prepared by | Mikhail Zhigulin |
| Product Approbation | tested by | Mikhail Zhigulin |
- Language support: Reverso
- Git clients: SmartGit and GitHub Desktop
© Mikhail A. Zhigulin of Novosibirsk.






