Skip to content

PerseusRealDeal/PerseusGeoKit

Repository files navigation

PerseusGeoKit — Xcode 14.2+

Actions Status Style Version Platforms Xcode 14.2 Swift 5.7 License

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.

PGK is a single author and personale solution developed in P2P relationship paradigm.

Integration Capabilities

Standalone Swift Package Manager compatible

Dependencies

ConsolePerseusLogger

Support Code

Standalone License

PGKSupportingStar.swift is a peace of code a widly helpful in accord with PGK.
PGKSupportingStar.swift goes as an external part of PGK.

Our Terms

CPL stands for Console Perseus Logger.
PGK stands for Perseus Geo Kit.
PDM stands for Perseus Dark Mode.
P2P stands for Person-to-Person.
A3 stands for Apple Apps Approbation.
T3 stands for The Technological Tree.

PGK in Use

In approbation: iOS app macOS app
In business: The Dark Moon

For details: Approbation and A3 Environment / CHANGELOG

Contents

In brief

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.

Build system requirements

First-party software

Type Name License
Star ConsolePerseusLogger / 1.7.0 MIT

Third-party software

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

Installation

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.

Usage

Get location services status

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. 
}

Request permission

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)
    }
}

Request current location

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 = .threeKilometers

Step 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.
}

Request updating location

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 = .threeKilometers

Step 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()

Geo events processing

Location error

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.
}

Location status

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.
}

Current location

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)
    }
}

Location updates

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)
    }
}

Points taken into account

License

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.

Other required license notices

© 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.

Credits

Balance and Control kept by Mikhail Zhigulin
Source Code written by Mikhail Zhigulin
Documentation prepared by Mikhail Zhigulin
Product Approbation tested by Mikhail Zhigulin

Author

© Mikhail A. Zhigulin of Novosibirsk.