Skip to content

PerseusRealDeal/PerseusGeoKit

Repository files navigation

Perseus Geo Kit

HAVE A DEAL WITH WHERE YOU ARE

Actions Status Style Version Platforms Xcode 14.2 Swift 5.7

A3 EnvironmentCHANGELOGMIT License


Usages

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.

Integration

Standalone Swift Package Manager compatible

Dependencies

ConsolePerseusLogger

Support Code

Standalone License

TheGeoSupport.swift is a peace of code a widly helpful in accord with PGK.

PGK in Use

Approbation:

Business:

Contents

Our Terms

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

In brief

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.

Requirements

To build:

First-party software

MIT

Type Name
Star ConsolePerseusLogger / 1.7.1

Third-party software

MIT

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

Installation

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.

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.

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

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)

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

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

Account points

License

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.

Other required licenses details

© 2025 The SwiftLint Contributors for SwiftLint
© GitHub for GitHub Action cirruslabs/swiftlint-action@v1
© 2021 Alexandre Colucci, geteimy.com for Shell Script SucceedsPostAction.sh

Credits

Balance and Control Mikhail Zhigulin
Source Code Mikhail Zhigulin
Documentation Mikhail Zhigulin
Approbation Mikhail Zhigulin
English Mikhail Zhigulin

Contributing

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.

Author

© Mikhail A. Zhigulin of Novosibirsk

Contact