-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 037c69b
Showing
11 changed files
with
409 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.DS_Store | ||
/.build | ||
/Packages | ||
xcuserdata/ | ||
DerivedData/ | ||
.swiftpm/configuration/registries.json | ||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata | ||
.netrc |
8 changes: 8 additions & 0 deletions
8
.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>IDEDidComputeMac32BitWarning</key> | ||
<true/> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
Copyright (c) 2023, Stadia Maps, Inc. | ||
|
||
All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without modification, | ||
are permitted provided that the following conditions are met: | ||
|
||
* Redistributions of source code must retain the above copyright notice, | ||
this list of conditions and the following disclaimer. | ||
* Redistributions in binary form must reproduce the above copyright notice, | ||
this list of conditions and the following disclaimer in the documentation | ||
and/or other materials provided with the distribution. | ||
* Neither the name of Stadia Maps nor the names of its contributors | ||
may be used to endorse or promote products derived from this software | ||
without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"originHash" : "7e0e0b4f9f6cf2f1eb0a555a1c8cbf395ed94d8d167b2cb16c044c92df7ced2d", | ||
"pins" : [ | ||
{ | ||
"identity" : "anycodable", | ||
"kind" : "remoteSourceControl", | ||
"location" : "https://github.com/Flight-School/AnyCodable", | ||
"state" : { | ||
"revision" : "862808b2070cd908cb04f9aafe7de83d35f81b05", | ||
"version" : "0.6.7" | ||
} | ||
}, | ||
{ | ||
"identity" : "stadiamaps-api-swift", | ||
"kind" : "remoteSourceControl", | ||
"location" : "https://github.com/stadiamaps/stadiamaps-api-swift", | ||
"state" : { | ||
"revision" : "87d74dca9e79390e36a5909ee6be75f7f9e9dfac", | ||
"version" : "4.0.0" | ||
} | ||
} | ||
], | ||
"version" : 3 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// swift-tools-version: 5.10 | ||
// The swift-tools-version declares the minimum version of Swift required to build this package. | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "StadiaMapsAutocompleteSearch", | ||
platforms: [ | ||
.iOS(.v15), | ||
.macOS(.v14), | ||
], | ||
products: [ | ||
// Products define the executables and libraries a package produces, making them visible to other packages. | ||
.library( | ||
name: "StadiaMapsAutocompleteSearch", | ||
targets: ["StadiaMapsAutocompleteSearch"] | ||
), | ||
], | ||
dependencies: [ | ||
.package(url: "https://github.com/stadiamaps/stadiamaps-api-swift", from: "4.0.0"), | ||
], | ||
targets: [ | ||
// Targets are the basic building blocks of a package, defining a module or a test suite. | ||
// Targets can depend on other targets in this package and products from dependencies. | ||
.target( | ||
name: "StadiaMapsAutocompleteSearch", | ||
dependencies: [ | ||
.product(name: "StadiaMaps", package: "stadiamaps-api-swift"), | ||
] | ||
), | ||
.testTarget( | ||
name: "StadiaMapsAutocompleteSearchTests", | ||
dependencies: ["StadiaMapsAutocompleteSearch"] | ||
), | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Stadia Maps Autocomplete Search for SwiftUI | ||
|
||
This package lets you add geographic autocomplete search to a SwiftUI app. | ||
|
||
* Displays a search box and list which you can embed in other views | ||
* Provides a callback handler with the result details when users tap a result | ||
* Can bias search results to be nearby a specific location | ||
* Automatically localizes place names based on the user's device settings (where available) | ||
|
||
![Screenshot](screenshot.png) | ||
|
||
## Installation | ||
|
||
The Xcode UI changes frequently, but you can usually add packages to your project using an option in the File menu. | ||
Then, you'll need to paste in the repository URL to search: https://github.com/stadiamaps/swiftui-autocomplete-search. | ||
See https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app for the latest detailed | ||
instructions from Apple. | ||
|
||
## Getting an API key | ||
|
||
You will need an API key to use this view. | ||
|
||
You can create an API key for free | ||
[here](https://client.stadiamaps.com/signup/?utm_source=github&utm_campaign=sdk_readme&utm_content=swiftui_autocomplete_readme) | ||
(no credit card required). | ||
|
||
## Using the SwiftUI view | ||
|
||
```swift | ||
import StadiaMapsAutocompleteSearch | ||
let stadiaMapsAPIKey = "YOUR-API-KEY" // Replace with your API key | ||
|
||
// Somewhere in your view body.... | ||
AutocompleteSearch(apiKey: stadiaMapsAPIKey, userLocation: userLocation.clLocation) { selection in | ||
// Do something with the selection. | ||
// For example, you might do something like this to start navigation in an app using Ferrostar. | ||
Task { | ||
do { | ||
routes = try await ferrostarCore.getRoutes(initialLocation: userLocation, waypoints: [Waypoint(coordinate: GeographicCoordinate(lat: selection.geometry.coordinates[1], lng: selection.geometry.coordinates[0]), kind: .break)]) | ||
|
||
try ferrostarCore.startNavigation(route: routes!.first!) | ||
errorMessage = nil | ||
} catch { | ||
errorMessage = "Error: \(error)" | ||
} | ||
} | ||
} | ||
``` |
100 changes: 100 additions & 0 deletions
100
Sources/StadiaMapsAutocompleteSearch/AutocompleteSearch.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import CoreLocation | ||
import StadiaMaps | ||
import SwiftUI | ||
|
||
/// An autocomplete search view that searches for geographic locations as you type. | ||
public struct AutocompleteSearch: View { | ||
@State private var searchText = "" | ||
@State private var searchResults: [PeliasGeoJSONFeature] = [] | ||
@State private var isLoading = false | ||
|
||
let userLocation: CLLocation? | ||
let onResultSelected: ((PeliasGeoJSONFeature) -> Void)? | ||
|
||
/// Creates an autocomplete geographic search view. | ||
/// - Parameters: | ||
/// - apiKey: Your Stadia Maps API key | ||
/// - useEUEndpoint: Send requests to servers located in the European Union (may significantly degrade performance outside Europe) | ||
/// - userLocation: If present, biases the search for results near a specific location and displays results with (straight-line) distances from this location | ||
/// - onResultSelected: A callback invoked when a result is tapped in the list | ||
public init(apiKey: String, useEUEndpoint: Bool = false, userLocation: CLLocation? = nil, onResultSelected: ((PeliasGeoJSONFeature) -> Void)? = nil) { | ||
StadiaMapsAPI.customHeaders = ["Authorization": "Stadia-Auth \(apiKey)"] | ||
if useEUEndpoint { | ||
StadiaMapsAPI.basePath = "https://api-eu.stadiamaps.com" | ||
} | ||
self.userLocation = userLocation | ||
self.onResultSelected = onResultSelected | ||
} | ||
|
||
public var body: some View { | ||
// TODO: Language override? | ||
// TODO: Min search length? | ||
TextField("Search", text: $searchText) | ||
.onChange(of: searchText) { query in | ||
Task { | ||
try await search(query: query, autocomplete: true) | ||
} | ||
} | ||
.onSubmit { | ||
Task { | ||
try await search(query: searchText, autocomplete: false) | ||
} | ||
} | ||
|
||
ZStack { | ||
List { | ||
ForEach(searchResults) { result in | ||
SearchResult(feature: result, relativeTo: userLocation) | ||
.contentShape(.rect) | ||
.onTapGesture { | ||
onResultSelected?(result) | ||
} | ||
} | ||
} | ||
|
||
if isLoading { | ||
ProgressView() | ||
} | ||
} | ||
} | ||
|
||
private func search(query: String, autocomplete: Bool) async throws { | ||
guard !query.isEmpty else { | ||
searchResults = [] | ||
return | ||
} | ||
|
||
isLoading = true | ||
|
||
defer { | ||
self.isLoading = false | ||
} | ||
|
||
let result: PeliasResponse | ||
|
||
if autocomplete { | ||
result = try await GeocodingAPI.autocomplete(text: query, focusPointLat: userLocation?.coordinate.latitude, focusPointLon: userLocation?.coordinate.longitude) | ||
} else { | ||
result = try await GeocodingAPI.search(text: query, focusPointLat: userLocation?.coordinate.latitude, focusPointLon: userLocation?.coordinate.longitude) | ||
} | ||
|
||
// Only replace results if the text matches the current input | ||
if query == searchText { | ||
searchResults = result.features | ||
} | ||
} | ||
} | ||
|
||
// Set this to your own Stadia Maps API key. | ||
// Get an free key at client.stadiamaps.com. | ||
private let previewApiKey = "YOUR-API-KEY" | ||
|
||
#Preview { | ||
if previewApiKey == "YOUR-API-KEY" { | ||
Text("You need an API key for this to be very useful. Get one at client.stadiamaps.com.") | ||
} else { | ||
AutocompleteSearch(apiKey: previewApiKey) { selection in | ||
print("Selected: \(selection)") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import Foundation | ||
import StadiaMaps | ||
import SwiftUI | ||
|
||
extension PeliasGeoJSONFeature: Identifiable { | ||
public var id: String? { | ||
properties?.gid | ||
} | ||
} | ||
|
||
public extension PeliasGeoJSONFeature { | ||
var subtitle: String? { | ||
if let layer = properties?.layer { | ||
switch layer { | ||
case .venue, .address, .street, .neighbourhood, .postalcode, .macrohood: | ||
return properties?.locality ?? properties?.region ?? properties?.country | ||
case .country, .dependency, .disputed, .continent: | ||
return properties?.continent | ||
case .macroregion, .region: | ||
return properties?.country | ||
case .locality, .localadmin, .borough, .macrocounty, .county: | ||
return properties?.region ?? properties?.country | ||
case .coarse, .marinearea, .empire, .ocean: | ||
return nil | ||
} | ||
} else { | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
extension PeliasLayer { | ||
var iconImage: Image { | ||
let imageName = switch self { | ||
case .venue: | ||
"building.2.crop.circle" | ||
case .address: | ||
"123.rectangle" | ||
case .street: | ||
"road.lanes" | ||
case .country: | ||
"globe.americas" | ||
case .macroregion: | ||
"globe.americas" | ||
case .region: | ||
"globe.americas" | ||
case .macrocounty: | ||
"mappin.and.ellipse" | ||
case .county: | ||
"mappin.and.ellipse" | ||
case .locality: | ||
"mappin.and.ellipse" | ||
case .localadmin: | ||
"mappin.and.ellipse" | ||
case .borough: | ||
"mappin.and.ellipse" | ||
case .neighbourhood: | ||
"mappin.and.ellipse" | ||
case .postalcode: | ||
"mappin.and.ellipse" | ||
case .coarse: | ||
"mappin.and.ellipse" | ||
case .dependency: | ||
"globe.americas" | ||
case .macrohood: | ||
"globe.americas" | ||
case .marinearea: | ||
"water.waves" | ||
case .disputed: | ||
"questionmark.circle" | ||
case .empire: | ||
"globe.americas" | ||
case .continent: | ||
"globe.americas" | ||
case .ocean: | ||
"water.waves" | ||
} | ||
|
||
return Image(systemName: imageName) | ||
} | ||
} |
Oops, something went wrong.