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.
TheGeoSupport.swiftis a peace of code a widly helpful in accord withPGK.
Approbation:
Business:
- Swift macOS app
The Dark Moon
- In brief
- Requirements
- First-party software
- Third-party software
- Installation
- Usage
- Account points
- License
- Credits
- Contributing
- Author
| Acronym | Stands for |
|---|---|
| CPL | Console_Perseus_Logger |
| PDM | Perseus_Dark_Mode |
| PGK | Perseus_Geo_Kit |
| A3 | Apple_Apps_Approbation |
| T3 | The_Technological_Tree |
| P2P | Person_to_Person |
The great home-made product for easily managed Location Services API. PGK is a single author and personale solution developed in P2P relationship paradigm.
| macOS app | iOS app |
|---|---|
![]() The Current Location Dialog ![]() System Services and The Redirect Dialog
|
![]() The Current Location Dialog ![]() The Redirect Dialog ![]() System Services
|
Important
The macOS App scenes taken from the motion picture The Hobbit based on the novel by J.R.R. Tolkien.
The iOS App scenes taken from the motion picture The Lord of The Rings based on the novel by J.R.R. Tolkien.
To build:
| Type | Name |
|---|---|
| Star | ConsolePerseusLogger / 1.7.1 |
| Type | Name |
|---|---|
| Style | SwiftLint / v0.57.0 for Monterey+ |
| Script | SwiftLint Shell Script to run SwiftLint |
| Action | cirruslabs/swiftlint-action@v1 |
| Type | Name |
|---|---|
| Action | mxcl/xcodebuild@v3 |
Step 1: Import PGK either with SPM or standalone.
Standalone: the single source code file TheGeoStar.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. |
Note
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)Important
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
- Changelog
- A3 environment specification
- SwiftLint shell script
MIT License, see LICENSE for details.
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. For instance, "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
| Balance and Control | Mikhail Zhigulin |
| Source Code | Mikhail Zhigulin |
| Documentation | Mikhail Zhigulin |
| Approbation | Mikhail Zhigulin |
| English | Mikhail Zhigulin |
- Language support: Reverso
- Git clients: SmartGit and GitHub Desktop
Note
The product is constructed in P2P relationship paradigm that means the only one single and the same face in the product team during all development process stages.
Bug reports are welcome, create an issue and give details.
© Mikhail A. Zhigulin of Novosibirsk






