mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00

* New LICENSE-COMMERCIAL file * Apply dual licenses: AGPL + Element Commercial to file headers * Update README with dual licensing
90 lines
2.9 KiB
Swift
90 lines
2.9 KiB
Swift
//
|
|
// Copyright 2023, 2024 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
import CoreLocation
|
|
import Foundation
|
|
|
|
/// A structure that parses a geo URI (i.e. geo:53.99803101552848,-8.25347900390625;u=10) and constructs their constituent parts.
|
|
struct GeoURI: Hashable {
|
|
// MARK: - Properties
|
|
|
|
let latitude: Double
|
|
let longitude: Double
|
|
let uncertainty: Double?
|
|
|
|
var bodyMessage: String {
|
|
"Location was shared at \(string)"
|
|
}
|
|
|
|
// MARK: - Setup
|
|
|
|
init?(string: String) {
|
|
guard let parsedURI = Self.parseGeoURI(from: string) else {
|
|
MXLog.warning("\(Self.self) failed to parse the string: \(string)")
|
|
return nil
|
|
}
|
|
self = parsedURI
|
|
}
|
|
|
|
init(latitude: Double, longitude: Double, uncertainty: Double? = nil) {
|
|
self.latitude = latitude
|
|
self.longitude = longitude
|
|
self.uncertainty = uncertainty
|
|
}
|
|
|
|
var string: String {
|
|
if let uncertainty {
|
|
return "geo:\(string(for: latitude)),\(string(for: longitude));u=\(string(for: uncertainty))"
|
|
} else {
|
|
return "geo:\(string(for: latitude)),\(string(for: longitude))"
|
|
}
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
// Parse a geo URI string like "geo:53.99803101552848,-8.25347900390625;u=10"
|
|
private static func parseGeoURI(from string: String) -> GeoURI? {
|
|
guard
|
|
let matchOutput = try? RegexGeoURI.standard.wholeMatch(in: string)?.output,
|
|
let latitude = Double(matchOutput.latitude),
|
|
let longitude = Double(matchOutput.longitude)
|
|
else {
|
|
return nil
|
|
}
|
|
|
|
let uncertainty = matchOutput.uncertainty.flatMap(Double.init)
|
|
return .init(latitude: latitude, longitude: longitude, uncertainty: uncertainty)
|
|
}
|
|
|
|
private func string(for number: Double) -> String {
|
|
NumberFormatter.decimal.string(from: .init(floatLiteral: number)) ?? "\(number)"
|
|
}
|
|
}
|
|
|
|
// swiftlint:disable:next large_tuple
|
|
private typealias RegexGeoURI = Regex<(Substring, latitude: Substring, longitude: Substring, uncertainty: Substring?)>
|
|
|
|
private extension RegexGeoURI {
|
|
static let standard: Self = /geo:(?<latitude>-?\d+(?:\.\d+)?),(?<longitude>-?\d+(?:\.\d+)?)(?:;u=(?<uncertainty>\d+(?:\.\d+)?))?/
|
|
}
|
|
|
|
extension GeoURI {
|
|
init(coordinate: CLLocationCoordinate2D, uncertainty: CLLocationAccuracy?) {
|
|
self.init(latitude: coordinate.latitude, longitude: coordinate.longitude, uncertainty: uncertainty)
|
|
}
|
|
}
|
|
|
|
private extension NumberFormatter {
|
|
static let decimal: NumberFormatter = {
|
|
let numberFormatter = NumberFormatter()
|
|
numberFormatter.locale = Locale(identifier: "en_US_POSIX")
|
|
numberFormatter.numberStyle = .decimal
|
|
numberFormatter.maximumFractionDigits = 30
|
|
return numberFormatter
|
|
}()
|
|
}
|