Skip to content

Commit 2b856f5

Browse files
authored
Merge pull request maplibre#50 from stadiamaps/maplibre-onboarding
Maplibre onboarding
2 parents a789bbe + 458daff commit 2b856f5

File tree

9 files changed

+182
-88
lines changed

9 files changed

+182
-88
lines changed

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github: [maplibre]
2+
open_collective: maplibre

.github/pull_request_template.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Issue/Motivation
2+
3+
What issue is this PR targeting?
4+
5+
## Tasklist
6+
7+
- [ ] Include tests (if applicable) and examples (new or edits)
8+
- [ ] If there are any visual changes as a result, include before/after screenshots and/or videos
9+
- [ ] Add #fixes with the issue number that this PR addresses
10+
- [ ] Update any documentation for affected APIs

CODE_OF_CONDUCT.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Contributor Covenant
2+
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/maplibre/maplibre/blob/main/CODE_OF_CONDUCT.md)

CONTRIBUTING.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Contributor Guide
2+
3+
This project is a standard Swift package.
4+
5+
## Environment setup
6+
7+
The only special thing you might need besides Xcode is [`swiftformat`](https://github.com/nicklockwood/SwiftFormat).
8+
We use it to automatically handle basic formatting and to linting
9+
so the code has a standard style.
10+
11+
Check out the swiftformat [Install Guide](https://github.com/nicklockwood/SwiftFormat?tab=readme-ov-file#how-do-i-install-it)
12+
to add swiftformat to your machine.
13+
Once installed, you can autoformat code using the command:
14+
15+
```sh
16+
swiftformat .
17+
```
18+
19+
Swiftformat can occasionally poorly resolve a formatting issue (e.g. when you've already line-broken a large comment).
20+
Issues like this are typically easy to manually correct.
21+
22+
## Structure
23+
24+
This package is structured into a few targets. `InternalUtils` is pretty much what it says. `MapLibreSwiftDSL` and
25+
`MapLibreSwiftUI` are published products, and make up the bulk of the project. Finally, `Examples` is a collection of
26+
SwiftUI previews.
27+
28+
The DSL provides a more Swift-y layer on top of the lower level MapLibre APIs, and features a number of
29+
result builders which enable more modern expressive APIs.
30+
31+
The SwiftUI layer publishes a SwiftUI view with the end goal of being a universal view that can be adapted to a wide
32+
variety of use cases, much like MapKit's SwiftUI views.
33+
34+
## Testing
35+
36+
We do not currently have full UI tests.
37+
These are a bit tricky due to the async nature of MapLibre and integrating this into an Xcode UI test is challenging.
38+
If you have any suggestions, we welcome them!
39+
40+
## PRs
41+
42+
If you're using this project and want to improve it, send us a PR!
43+
We have a checklist in our PR template to help reviews go smoothly.
44+
45+
NOTE: If possible, enable maintainer edits when you open PRs.
46+
If your PR is good and just needs a few minor edits to get merged,
47+
we can often put those finishing touches on for you.
48+
In order to allow maintainer edits,
49+
you need to send PRs from a personal fork (org-owned ones have funky auth).

LICENSE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2023, Stadia Maps, Inc.
1+
Copyright (c) 2024, MapLibre contributors.
22

33
All rights reserved.
44

@@ -10,7 +10,7 @@ are permitted provided that the following conditions are met:
1010
* Redistributions in binary form must reproduce the above copyright notice,
1111
this list of conditions and the following disclaimer in the documentation
1212
and/or other materials provided with the distribution.
13-
* Neither the name of MapLibre GL JS nor the names of its contributors
13+
* Neither the name of MapLibre nor the names of its contributors
1414
may be used to endorse or promote products derived from this software
1515
without specific prior written permission.
1616

README.md

Lines changed: 101 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
1+
<p align="center">
2+
<img src="https://maplibre.org/img/maplibre-logo-big.svg" alt="MapLibre Logo">
3+
</p>
4+
15
# MapLibreSwiftUI
26

37
Swift DSLs for [MapLibre Native](https://github.com/maplibre/maplibre-native), a free open-source renderer
48
for interactive vector maps, to enable better integration with SwiftUI and generally enable easier use of MapLibre.
59

610
![A screen recording demonstrating the declarative SwiftUI DSL reacting to changes live](demo.gif)
711

8-
This package is a playground (read: not yet stable) to experiment with Swift DSLs and SwiftUI without constraints.
9-
As more people try this out with their own use cases, the community should eventuallly stabilize on a
10-
reasonably optimal DSL for working with MapLibre in Swift. The package name is currently MapLibreSwiftUI, but it's
11-
possible that the SwiftUI and DSL layers are separable.
12-
13-
If successful, some version this project should eventually either get merged into MapLibre Native or at the least
14-
merged into the MapLibre organization more formally. It is being developed as a separate package initially so we
15-
can move fast without breaking anything important.
16-
17-
**NOTE: This currently only works on iOS, as that's the only architecture currently published in the dynamic framework.**
12+
This package is a reimagining of the MapLibre API with a modern DSLs for SwiftUI.
13+
The pre-1.0 status means only that we are not yet committed to API stability yet,
14+
since we care deeply about finding the best way to express things for SwfitUI users.
15+
The package is robust for the subset of the MapLibre iOS API that it supports.
16+
Any breaking API changes will be reflected in release notes.
1817

1918
## Goals
2019

21-
1. Primary: Make common use cases easy and make complicated ones possible
20+
1. Primary: Make common use cases easy and [make complicated ones possible](Sources/MapLibreSwiftUI/Examples/Other.swift)
2221
* Easy integration of MapLibre into a modern SwiftUI app
23-
* Add markers and similar annotations
22+
* Add [markers](Sources/MapLibreSwiftUI/Examples/Gestures.swift), [polylines](Sources/MapLibreSwiftUI/Examples/Polyline.swift) and similar annotations
23+
* Interaction with features through [gestures](Sources/MapLibreSwiftUI/Examples/Gestures.swift)
2424
* Clustering (common use case that's rather difficult for first timers)
25-
* Overlays
25+
* [Overlays]](Sources/MapLibreSwiftUI/Examples/)
2626
* Dynamic styling
27-
* Camera control / animation??
28-
* Navigation
27+
* [Camera control](Sources/MapLibreSwiftUI/Examples/Camera.swift)
28+
* Turn-by-turn Navigation (see the showcase integrations below)
29+
* Animation
2930
2. Prevent most common classes of mistakes that users make with the lower level APIs (ex: adding the same source twice)
3031
3. Deeper SwiftUI integration (ex: SwiftUI callout views)
3132

@@ -44,28 +45,104 @@ Then, for each target add either the DSL (for just the DSL) or both (for the Swi
4445
.product(name: "MapLibreSwiftUI", package: "maplibre-swiftui-dsl-playground"),
4546
```
4647

47-
Check out the (super basic) [previews at the bottom of MapView.swift](Sources/MapLibreSwiftUI/MapView.swift)
48-
or more detailed [Examples](Sources/MapLibreSwiftUI/Examples) to see how it works in practice.
48+
Then, you can use it in a SwiftUI view body like this:
49+
50+
```swift
51+
import MapLibre
52+
import MapLibreSwiftDSL
53+
import SwiftUI
54+
import CoreLocation
55+
56+
struct PolylineMapView: View {
57+
// You'll need a MapLibre Style for this to work.
58+
// You can use https://demotiles.maplibre.org/style.json for basic testing.
59+
// For a list of commercially supported tile providers, check out https://wiki.openstreetmap.org/wiki/Vector_tiles#Providers.
60+
// These providers all have their own "house styles" as well as custom styling.
61+
// You can create your own style or modify others (subject to license restrictions) using https://maplibre.org/maputnik/.
62+
let styleURL: URL
63+
64+
// Just a list of waypoints (ex: a route to follow)
65+
let waypoints: [CLLocationCoordinate2D]
66+
67+
var body: some View {
68+
MapView(styleURL: styleURL,
69+
camera: .constant(.center(waypoints.first!, zoom: 14)))
70+
{
71+
// Define a data source.
72+
// It will be automatically if a layer references it.
73+
let polylineSource = ShapeSource(identifier: "polyline") {
74+
MLNPolylineFeature(coordinates: waypoints)
75+
}
76+
77+
// Add a polyline casing for a stroke effect
78+
LineStyleLayer(identifier: "polyline-casing", source: polylineSource)
79+
.lineCap(.round)
80+
.lineJoin(.round)
81+
.lineColor(.white)
82+
.lineWidth(interpolatedBy: .zoomLevel,
83+
curveType: .exponential,
84+
parameters: NSExpression(forConstantValue: 1.5),
85+
stops: NSExpression(forConstantValue: [14: 6, 18: 24]))
86+
87+
// Add an inner (blue) polyline
88+
LineStyleLayer(identifier: "polyline-inner", source: polylineSource)
89+
.lineCap(.round)
90+
.lineJoin(.round)
91+
.lineColor(.systemBlue)
92+
.lineWidth(interpolatedBy: .zoomLevel,
93+
curveType: .exponential,
94+
parameters: NSExpression(forConstantValue: 1.5),
95+
stops: NSExpression(forConstantValue: [14: 3, 18: 16]))
96+
}
97+
}
98+
}
99+
```
100+
101+
Check out more [Examples](Sources/MapLibreSwiftUI/Examples) to go deeper.
49102

50-
## Common integrations
103+
**NOTE: This currently only works on iOS, as the dynamic framework doesn't yet include macOS.**
104+
105+
## How can you help?
106+
107+
The first thing you can do is try it out!
108+
Check out the [Examples](Sources/MapLibreSwiftUI/Examples) for inspiration,
109+
swap it into your own SwiftUI app, or check out some showcase integrations for inspiration.
110+
Putting it "through the paces" is the best way for us to converge on the "right" APIs as a community.
111+
Your use case probably isn't supported today, in which case you can either open an issue or contribute a PR.
112+
113+
The code has a number of TODOs, most of which can be tackled by any intermediate Swift programmer.
114+
The important issues should all be tracked in GitHub.
115+
We also have a `#maplibre-swiftui-compose-playground` channel in the
116+
[OpenStreetMap US Slack](https://slack.openstreetmap.us/).
117+
118+
The skeleton is already in place for several of the core concepts, including style layers and sources, but
119+
these are incomplete. You can help by opening a PR that fills these in.
120+
For example, if you wanted to fill out the API for the line style layer,
121+
head over to [the docs](https://maplibre.org/maplibre-native/ios/api/Classes/MGLLineStyleLayer.html)
122+
and just start filling out the remaining properties and modifiers.
123+
124+
## Showcase integrations
51125

52126
### Ferrostar
53127

54-
[Ferrostar](https://github.com/stadiamaps/ferrostar) has a MapLibre UI module as part of its Swift Package
55-
which is already built on this!
128+
[Ferrostar](https://github.com/stadiamaps/ferrostar) has a MapLibre UI module as part of its Swift Package.
129+
That was actually the impetus for building this package,
130+
and the core devs are eating their own dogfood.
56131
See the [SwiftUI customization](https://stadiamaps.github.io/ferrostar/swiftui-customization.html)
57132
part of the Ferrostar user guide for details on how to customize the map.
58133

59134
### MapLibre Navigation iOS
60135

61-
This package can help bridge the gap between MapLibre Navigation iOS and SwiftUI!
136+
This package also helps to bridge the gap between MapLibre Navigation iOS and SwiftUI!
137+
Thanks to developers from [HudHud](https://hudhud.sa/en) for their contributions which made this possible!
62138

63-
Add the Swift Package froh Hudhud (https://github.com/HudHud-Maps/maplibre-navigation-ios.git) to your Package.swift
64-
and add code like this:
139+
Add the [Swift Package](https://github.com/maplibre/maplibre-navigation-ios) to your project.
140+
Then add some code like this:
65141

66142
```swift
67143
import MapboxCoreNavigation
68144
import MapboxNavigation
145+
import MapLibreSwiftUI
69146

70147
extension NavigationViewController: MapViewHostViewController {
71148
public typealias MapType = NavigationMapView
@@ -78,7 +155,7 @@ extension NavigationViewController: MapViewHostViewController {
78155
@ViewBuilder
79156
var mapView: some View {
80157
MapView<NavigationViewController>(makeViewController: NavigationViewController(dayStyleURL: self.styleURL), styleURL: self.styleURL, camera: self.$mapStore.camera) {
81-
158+
// TODO: Your customizations here; add more layers or whatever you like!
82159
}
83160
.unsafeMapViewControllerModifier { navigationViewController in
84161
navigationViewController.delegate = self.mapStore
@@ -96,52 +173,3 @@ var mapView: some View {
96173
.cameraModifierDisabled(self.route != nil)
97174
}
98175
```
99-
100-
## Developer Quick Start
101-
102-
This project is a standard Swift package.
103-
The only special thing you might need besides Xcode is [`swiftformat`](https://github.com/nicklockwood/SwiftFormat).
104-
We use it to automatically handle basic formatting and to linting
105-
so the code has a standard style.
106-
107-
Check out the swiftformat [Install Guide](https://github.com/nicklockwood/SwiftFormat?tab=readme-ov-file#how-do-i-install-it)
108-
to add swiftformat to your machine.
109-
Once installed, you can autoformat code using the command:
110-
111-
```sh
112-
swiftformat .
113-
```
114-
115-
Swiftformat can occasionally poorly resolve a formatting issue (e.g. when you've already line-broken a large comment).
116-
Issues like this are typically easy to manually correct.
117-
118-
## Structure
119-
120-
This package is structured into a few targets. `InternalUtils` is pretty much what it says. `MapLibreSwiftDSL` and
121-
`MapLibreSwiftUI` are published products, and make up the bulk of the project. Finally, `Examples` is a collection of
122-
SwiftUI previews.
123-
124-
The DSL provides a more Swift-y layer on top of the lower level MapLibre APIs, and features a number of
125-
result builders which enable more modern expressive APIs.
126-
127-
The SwiftUI layer publishes a SwiftUI view with the end goal of being a universal view that can be adapted to a wide
128-
variety of use cases, much like MapKit's SwiftUI views.
129-
130-
The `Examples` should serve as a rough indication of development progress. While much of the code in the UI layer
131-
isn't formally tested, this acts as our test, and for all intents and purposes, we're following a practical variant of
132-
TDD as we build out functionality.
133-
134-
## How can you help?
135-
136-
The first thing you can do is try out the library! Check out the detailed [Examples](Sources/MapLibreSwiftUI/Examples)
137-
for inspiration. Putting it "through the paces" is the best way for us to converge on the "right" APIs as a community.
138-
Your use case probably isn't supported today, in which case you can either open an issue or contribute a PR.
139-
140-
The code has a number of TODOs, most of which can be tackled by any intermediate Swift programmer. The important
141-
issues should all be tracked in GitHub. DISCUSS comments (should migrate to GitHub issues/discussions) are deeper
142-
questions. Input welcome.
143-
144-
The skeleton is already in place for several of the core concepts, including style layers and sources, but
145-
these are incomplete. You can help by opening a PR that fills these in. For example, if you wanted to fill out the
146-
API for the line style layer, head over to [the docs](https://maplibre.org/maplibre-native/ios/api/Classes/MGLLineStyleLayer.html)
147-
and get to work filling out the remaining properties and modifiers.

SECURITY_POLICY.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
For an up-to-date policy refer to
2+
https://github.com/maplibre/maplibre/blob/main/SECURITY_POLICY.txt

Sources/MapLibreSwiftUI/Examples/Other.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import SwiftUI
2929
}
3030
.unsafeMapViewControllerModifier { viewController in
3131
// Not all properties have modifiers yet. Until they do, you can use this 'escape hatch' to the underlying
32-
// MLNMapView. Be careful: if you modify properties that the DSL controls already, they may be overridden. This
33-
// modifier is a "hack", not a final function.
32+
// MLNMapView.
33+
// Be careful: if you modify properties that the DSL controls already, they may be overridden!
34+
// This modifier is a temporary solution; let us know your use case(s)
35+
// so we can build safe support into the DSL.
3436
viewController.mapView.logoView.isHidden = false
3537
viewController.mapView.compassViewPosition = .topLeft
3638
}

Sources/MapLibreSwiftUI/Examples/Polyline.swift

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@ import MapLibre
33
import MapLibreSwiftDSL
44
import SwiftUI
55

6-
struct PolylinePreview: View {
6+
struct PolylineMapView: View {
77
let styleURL: URL
8+
let waypoints: [CLLocationCoordinate2D]
89

910
var body: some View {
1011
MapView(styleURL: styleURL,
11-
camera: .constant(.center(samplePedestrianWaypoints.first!, zoom: 14)))
12+
camera: .constant(.center(waypoints.first!, zoom: 14)))
1213
{
13-
// Note: This line does not add the source to the style as if it
14-
// were a statement in an imperative programming language.
15-
// The source is added automatically if a layer references it.
16-
let polylineSource = ShapeSource(identifier: "pedestrian-polyline") {
17-
MLNPolylineFeature(coordinates: samplePedestrianWaypoints)
14+
// Define a data source.
15+
// It will be automatically if a layer references it.
16+
let polylineSource = ShapeSource(identifier: "polyline") {
17+
MLNPolylineFeature(coordinates: waypoints)
1818
}
1919

2020
// Add a polyline casing for a stroke effect
21-
LineStyleLayer(identifier: "route-line-casing", source: polylineSource)
21+
LineStyleLayer(identifier: "polyline-casing", source: polylineSource)
2222
.lineCap(.round)
2323
.lineJoin(.round)
2424
.lineColor(.white)
@@ -28,7 +28,7 @@ struct PolylinePreview: View {
2828
stops: NSExpression(forConstantValue: [14: 6, 18: 24]))
2929

3030
// Add an inner (blue) polyline
31-
LineStyleLayer(identifier: "route-line-inner", source: polylineSource)
31+
LineStyleLayer(identifier: "polyline-inner", source: polylineSource)
3232
.lineCap(.round)
3333
.lineJoin(.round)
3434
.lineColor(.systemBlue)
@@ -37,13 +37,12 @@ struct PolylinePreview: View {
3737
parameters: NSExpression(forConstantValue: 1.5),
3838
stops: NSExpression(forConstantValue: [14: 3, 18: 16]))
3939
}
40-
.previewDisplayName("Polyline")
4140
}
4241
}
4342

4443
struct Polyline_Previews: PreviewProvider {
4544
static var previews: some View {
46-
PolylinePreview(styleURL: demoTilesURL)
45+
PolylineMapView(styleURL: demoTilesURL, waypoints: samplePedestrianWaypoints)
4746
.ignoresSafeArea(.all)
4847
}
4948
}

0 commit comments

Comments
 (0)