diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/GeoHash.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/GeoHash.xcscheme deleted file mode 100644 index f34dd55..0000000 --- a/.swiftpm/xcode/xcshareddata/xcschemes/GeoHash.xcscheme +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Package.swift b/Package.swift index 37b881d..9a8a10b 100644 --- a/Package.swift +++ b/Package.swift @@ -5,6 +5,10 @@ import PackageDescription let package = Package( name: "GeoHash", + platforms: [ + .macOS(.v10_15), + .iOS(.v15), + ], products: [ // Products define the executables and libraries a package produces, making them visible to other packages. .library( diff --git a/Sources/GeoHashCLI/GeoHashCLI.swift b/Sources/GeoHashCLI/GeoHashCLI.swift index 49448ad..cf196c0 100644 --- a/Sources/GeoHashCLI/GeoHashCLI.swift +++ b/Sources/GeoHashCLI/GeoHashCLI.swift @@ -29,7 +29,7 @@ struct GeoHashCLI: ParsableCommand { ) let precision = GeoHashBitsPrecision.exact(digits: length * 5) - let geoHash: GeoHash + let geoHash: GeoHash? if let coordinate { let components = coordinate.split(separator: ",") @@ -53,6 +53,6 @@ struct GeoHashCLI: ParsableCommand { } else { throw ValidationError("Coordinate or latitude and longitude must be provided.") } - print(geoHash.geoHash) + print(geoHash?.geoHash ?? "Invalid GeoHash") } -} \ No newline at end of file +} diff --git a/Sources/GeoHashFramework/GeoHash.swift b/Sources/GeoHashFramework/GeoHash.swift index 22d690a..8bb0293 100644 --- a/Sources/GeoHashFramework/GeoHash.swift +++ b/Sources/GeoHashFramework/GeoHash.swift @@ -3,6 +3,8 @@ import Foundation +internal let runtimeWarning = RuntimeWarning() + public struct GeoHash: Sendable, Hashable { public private(set) var precision: GeoHashBitsPrecision public private(set) var coordinate: GeoHashCoordinate2D @@ -18,20 +20,31 @@ public struct GeoHash: Sendable, Hashable { /// Assure that all characters in the string are "0" or "1". /// /// - Parameter value: A string that contains only "0" or "1". - public init( + public init?( binary: String, precision: GeoHashBitsPrecision = .mid ) { - precondition( - binary.allSatisfy({ - ["0", "1"].contains($0) - }) - ) + let isValidBinary = binary.allSatisfy({ + ["0", "1"].contains($0) + }) + if !isValidBinary { + Task { + await runtimeWarning.log( + message: "Binary string must contain only '0' or '1'." + ) + } + return nil + } if binary.count != precision.rawValue { - precondition( - binary.count == precision.rawValue, - "Binary length must be \(precision.rawValue), but binary length is \(binary.count)" - ) + let isSameLength = binary.count == precision.rawValue + if !isSameLength { + Task { + await runtimeWarning.log( + message: "Binary length must be %d, but binary length is %d", + args: precision.rawValue, binary.count + ) + } + } } self.binary = binary self.precision = precision @@ -41,19 +54,27 @@ public struct GeoHash: Sendable, Hashable { ) } - public init( + public init?( latitude: Double, longitude: Double, precision: GeoHashBitsPrecision = .mid ) { - precondition( - latitude >= -90 && latitude <= 90, - "Latitude must be between -90 and 90" - ) - precondition( - longitude >= -180 && longitude <= 180, - "Longitude must be between -180 and 180" - ) + if latitude >= 90 || latitude <= -90 { + Task { + await runtimeWarning.log( + message: "Latitude must be between -90 and 90" + ) + } + return nil + } + if longitude >= 180 || longitude <= -180 { + Task { + await runtimeWarning.log( + message: "Longitude must be between -180 and 180" + ) + } + return nil + } self.init( binary: Self.makeBinary( @@ -67,7 +88,7 @@ public struct GeoHash: Sendable, Hashable { ) } - public init(geoHash: String, precision: GeoHashBitsPrecision = .mid) { + public init?(geoHash: String, precision: GeoHashBitsPrecision = .mid) { self.init( binary: Self.makeBinary( from: geoHash, @@ -88,9 +109,13 @@ extension GeoHash { for char in geoHash { guard let index = Self.base32Chars.firstIndex(of: char) else { - preconditionFailure( - "Invalid geohash character \(char) in geoHash: \(geoHash)" - ) + Task { + await runtimeWarning.log( + message: "Invalid geohash character %s in geoHash: %s", + args: String(char), geoHash + ) + } + continue } let value = Self.base32Chars.distance( @@ -237,7 +262,7 @@ extension GeoHash { latitude: newLat, longitude: newLng, precision: precision - ) + )! } return [ @@ -363,7 +388,7 @@ extension GeoHash { let bound = GeoHash( binary: String(repeating: "0", count: precision.rawValue), precision: precision - ).getBound() + )!.getBound() let latitudeDelta = bound[0].latitude - bound[3].latitude let longitudeDelta = bound[1].longitude - bound[0].longitude diff --git a/Sources/GeoHashFramework/RuntimeWarning.swift b/Sources/GeoHashFramework/RuntimeWarning.swift new file mode 100644 index 0000000..fdc5784 --- /dev/null +++ b/Sources/GeoHashFramework/RuntimeWarning.swift @@ -0,0 +1,45 @@ +// +// RuntimeWarning.swift +// GeoHash +// +// Created by Fumiya Tanaka on 2024/12/17. +// + +import Foundation +import OSLog + +actor RuntimeWarning { + var info = Dl_info() + + init() { +#if DEBUG + dladdr( + dlsym( + dlopen( + nil, + RTLD_LAZY + ), + """ + $sxSg7SwiftUI8CommandsA2bCRzlAbCP4body4BodyQzvgTW + """ + ), + &info + ) + #endif + } + + func log(message: StaticString, args: any CVarArg...) { + #if DEBUG + os_log( + .fault, + dso: info.dli_fbase, + log: OSLog( + subsystem: "com.apple.runtime-issues", + category: "ReportRuntimeWarningSample" + ), + message, + args + ) + #endif + } +} \ No newline at end of file diff --git a/Tests/GeoHashFrameworkTests/GeoHashTests.swift b/Tests/GeoHashFrameworkTests/GeoHashTests.swift index 6c7a440..2d01308 100644 --- a/Tests/GeoHashFrameworkTests/GeoHashTests.swift +++ b/Tests/GeoHashFrameworkTests/GeoHashTests.swift @@ -18,7 +18,7 @@ struct GeoHashTests { let geoHash = GeoHash( binary: input, precision: .exact(digits: 20) - ) + )! #expect(geoHash.geoHash == "e9p6") #expect(geoHash.geoHash.count == input.count / 5) #expect(geoHash.latitudeBits == expectedLat) @@ -31,7 +31,7 @@ struct GeoHashTests { let lat = 35.681382 let lng = 139.766084 - let geoHash = GeoHash(latitude: lat, longitude: lng) + let geoHash = GeoHash(latitude: lat, longitude: lng)! #expect( geoHash.binary == "1110110100001110011011010101111110001101" ) @@ -48,7 +48,7 @@ struct GeoHashTests { precision: .exact( digits: geoHashString.count * 5 ) - ) + )! #expect(geoHash.geoHash == geoHashString) } @@ -64,7 +64,7 @@ struct GeoHashTests { latitude: coordinate.latitude, longitude: coordinate.longitude, precision: precision - ) + )! let actual = geoHash.getBound() let expected = [ GeoHashCoordinate2D( @@ -110,7 +110,7 @@ struct GeoHashTests { "xn76urwk", ] - let geoHash = GeoHash(latitude: lat, longitude: lng) + let geoHash = GeoHash(latitude: lat, longitude: lng)! let neighbors = geoHash.getNeighbors() #expect(neighbors.map(\.geoHash) == expected) }