Skip to content

Commit ce4dcb7

Browse files
adrian-cojocarupre-commit-ci[bot]louwers
authored
Add iOS/macOS observer hooks (#3245)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bart Louwers <bart@emeel.net>
1 parent f5f6e7f commit ce4dcb7

20 files changed

+1016
-0
lines changed

platform/darwin/bazel/files.bzl

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ MLN_DARWIN_OBJC_HEADERS = [
9393
"src/MLNStyle.h",
9494
"src/MLNStyleLayer.h",
9595
"src/MLNStyleValue.h",
96+
"src/MLNTileOperation.h",
9697
"src/MLNTilePyramidOfflineRegion.h",
9798
"src/MLNTileServerOptions.h",
9899
"src/MLNTileSource.h",
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#import <Foundation/Foundation.h>
2+
3+
typedef NS_ENUM(NSInteger, MLNTileOperation) {
4+
MLNTileOperationRequestedFromCache, ///< A read request from the cache
5+
MLNTileOperationRequestedFromNetwork, ///< A read request from the online source
6+
MLNTileOperationLoadFromNetwork, ///< Tile data from the network has been retrieved
7+
MLNTileOperationLoadFromCache, ///< Tile data from the cache has been retrieved
8+
MLNTileOperationStartParse, ///< Background processing of tile data has been initiated
9+
MLNTileOperationEndParse, ///< Background processing of tile data has been completed
10+
MLNTileOperationError, ///< An error occurred while loading the tile
11+
MLNTileOperationCancelled, ///< Loading of a tile was cancelled
12+
MLNTileOperationNullOp, ///< No operation has taken place
13+
};

platform/ios/MapLibre.docc/MapLibre.md

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Powerful, free and open-source mapping toolkit with full control over data sourc
5151

5252
### Advanced
5353

54+
- <doc:ObserverExample>
5455
- <doc:CustomStyleLayerExample>
5556

5657
### Other Articles
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Observe Low-Level Events
2+
3+
Learn about the ``MLNMapViewDelegate`` methods for observing map events.
4+
5+
> Warning: These methods are not thread-safe.
6+
7+
You can observe certain low-level events as they happen. Use these methods to collect metrics or investigate issues during map rendering. This feature is intended primarily for power users. We are always interested in improving observability, so if you have a special use case, feel free to [open an issue or pull request](https://github.com/maplibre/maplibre-native) to extend the types of observability methods.
8+
9+
## Shader Events
10+
11+
Observe shader compilation with ``MLNMapViewDelegate/mapView:shaderWillCompile:backend:defines:`` and ``MLNMapViewDelegate/mapView:shaderDidCompile:backend:defines:``.
12+
13+
<!-- include-example(ObserverExampleShaders) -->
14+
15+
```swift
16+
func mapView(_: MLNMapView, shaderWillCompile id: Int, backend: Int, defines: String) {
17+
print("A new shader is being compiled - shaderID:\(id), backend type:\(backend), program configuration:\(defines)")
18+
}
19+
20+
func mapView(_: MLNMapView, shaderDidCompile id: Int, backend: Int, defines: String) {
21+
print("A shader has been compiled - shaderID:\(id), backend type:\(backend), program configuration:\(defines)")
22+
}
23+
```
24+
25+
See also: ``MLNMapViewDelegate/mapView:shaderDidFailCompile:backend:defines:``.
26+
27+
## Glyph Loading
28+
29+
Observe glyph loading events with ``MLNMapViewDelegate/mapView:glyphsWillLoad:range:`` and ``MLNMapViewDelegate/mapView:glyphsDidLoad:range:``.
30+
31+
<!-- include-example(ObserverExampleGlyphs) -->
32+
33+
```swift
34+
func mapView(_: MLNMapView, glyphsWillLoad fontStack: [String], range: NSRange) {
35+
print("Glyphs are being requested for the font stack \(fontStack), ranging from \(range.location) to \(range.location + range.length)")
36+
}
37+
38+
func mapView(_: MLNMapView, glyphsDidLoad fontStack: [String], range: NSRange) {
39+
print("Glyphs have been loaded for the font stack \(fontStack), ranging from \(range.location) to \(range.location + range.length)")
40+
}
41+
```
42+
43+
See also: ``MLNMapViewDelegate/mapView:glyphsDidError:range:``.
44+
45+
## Tile Events
46+
47+
Monitor tile-related actions using the delegate method ``MLNMapViewDelegate/mapView:tileDidTriggerAction:x:y:z:wrap:overscaledZ:sourceID:`` with the ``MLNTileOperation`` type.
48+
49+
<!-- include-example(ObserverExampleTiles) -->
50+
51+
```swift
52+
func mapView(_: MLNMapView, tileDidTriggerAction operation: MLNTileOperation,
53+
x: Int,
54+
y: Int,
55+
z: Int,
56+
wrap: Int,
57+
overscaledZ: Int,
58+
sourceID: String)
59+
{
60+
let tileStr = String(format: "(x: %ld, y: %ld, z: %ld, wrap: %ld, overscaledZ: %ld, sourceID: %@)",
61+
x, y, z, wrap, overscaledZ, sourceID)
62+
63+
switch operation {
64+
case MLNTileOperation.requestedFromCache:
65+
print("Requesting tile \(tileStr) from cache")
66+
67+
case MLNTileOperation.requestedFromNetwork:
68+
print("Requesting tile \(tileStr) from network")
69+
70+
case MLNTileOperation.loadFromCache:
71+
print("Loading tile \(tileStr), requested from the cache")
72+
73+
case MLNTileOperation.loadFromNetwork:
74+
print("Loading tile \(tileStr), requested from the network")
75+
76+
case MLNTileOperation.startParse:
77+
print("Parsing tile \(tileStr)")
78+
79+
case MLNTileOperation.endParse:
80+
print("Completed parsing tile \(tileStr)")
81+
82+
case MLNTileOperation.error:
83+
print("An error occured during proccessing for tile \(tileStr)")
84+
85+
case MLNTileOperation.cancelled:
86+
print("Pending work on tile \(tileStr)")
87+
88+
case MLNTileOperation.nullOp:
89+
print("An unknown tile operation was emitted for tile \(tileStr)")
90+
91+
@unknown default:
92+
assertionFailure()
93+
}
94+
}
95+
```
96+
97+
## Sprite Loading
98+
99+
Observe sprite loading events with ``MLNMapViewDelegate/mapView:spriteWillLoad:url:`` and ``MLNMapViewDelegate/mapView:spriteDidLoad:url:``.
100+
101+
<!-- include-example(ObserverExampleSprites) -->
102+
103+
```swift
104+
func mapView(_: MLNMapView, spriteWillLoad id: String, url: String) {
105+
print("The sprite \(id) has been requested from \(url)")
106+
}
107+
108+
func mapView(_: MLNMapView, spriteDidLoad id: String, url: String) {
109+
print("The sprite \(id) has been loaded from \(url)")
110+
}
111+
```
112+
113+
See also: ``MLNMapViewDelegate/mapView:spriteDidError:url:``.

platform/ios/app-swift/Sources/MapLibreNavigationView.swift

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ struct MapLibreNavigationView: View {
3434
NavigationLink("AddMarkerExample") {
3535
AddMarkerSymbolExampleUIViewControllerRepresentable()
3636
}
37+
NavigationLink("ObserverExample") {
38+
ObserverExampleViewExampleUIViewControllerRepresentable()
39+
}
3740
Group {
3841
NavigationLink("AnimatedLineExample") {
3942
AnimatedLineExampleUIViewControllerRepresentable()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import MapLibre
2+
import SwiftUI
3+
import UIKit
4+
5+
class ObserverExampleView: UIViewController, MLNMapViewDelegate {
6+
var mapView: MLNMapView!
7+
8+
override func viewDidLoad() {
9+
super.viewDidLoad()
10+
11+
mapView = MLNMapView(frame: view.bounds, styleURL: AMERICANA_STYLE)
12+
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
13+
14+
mapView.setCenter(
15+
CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736),
16+
zoomLevel: 11,
17+
animated: false
18+
)
19+
view.addSubview(mapView)
20+
21+
mapView.delegate = self
22+
}
23+
24+
// #-example-code(ObserverExampleShaders)
25+
func mapView(_: MLNMapView, shaderWillCompile id: Int, backend: Int, defines: String) {
26+
print("A new shader is being compiled - shaderID:\(id), backend type:\(backend), program configuration:\(defines)")
27+
}
28+
29+
func mapView(_: MLNMapView, shaderDidCompile id: Int, backend: Int, defines: String) {
30+
print("A shader has been compiled - shaderID:\(id), backend type:\(backend), program configuration:\(defines)")
31+
}
32+
33+
// #-end-example-code
34+
35+
// #-example-code(ObserverExampleGlyphs)
36+
func mapView(_: MLNMapView, glyphsWillLoad fontStack: [String], range: NSRange) {
37+
print("Glyphs are being requested for the font stack \(fontStack), ranging from \(range.location) to \(range.location + range.length)")
38+
}
39+
40+
func mapView(_: MLNMapView, glyphsDidLoad fontStack: [String], range: NSRange) {
41+
print("Glyphs have been loaded for the font stack \(fontStack), ranging from \(range.location) to \(range.location + range.length)")
42+
}
43+
44+
// #-end-example-code
45+
46+
// #-example-code(ObserverExampleTiles)
47+
func mapView(_: MLNMapView, tileDidTriggerAction operation: MLNTileOperation,
48+
x: Int,
49+
y: Int,
50+
z: Int,
51+
wrap: Int,
52+
overscaledZ: Int,
53+
sourceID: String)
54+
{
55+
let tileStr = String(format: "(x: %ld, y: %ld, z: %ld, wrap: %ld, overscaledZ: %ld, sourceID: %@)",
56+
x, y, z, wrap, overscaledZ, sourceID)
57+
58+
switch operation {
59+
case MLNTileOperation.requestedFromCache:
60+
print("Requesting tile \(tileStr) from cache")
61+
62+
case MLNTileOperation.requestedFromNetwork:
63+
print("Requesting tile \(tileStr) from network")
64+
65+
case MLNTileOperation.loadFromCache:
66+
print("Loading tile \(tileStr), requested from the cache")
67+
68+
case MLNTileOperation.loadFromNetwork:
69+
print("Loading tile \(tileStr), requested from the network")
70+
71+
case MLNTileOperation.startParse:
72+
print("Parsing tile \(tileStr)")
73+
74+
case MLNTileOperation.endParse:
75+
print("Completed parsing tile \(tileStr)")
76+
77+
case MLNTileOperation.error:
78+
print("An error occured during proccessing for tile \(tileStr)")
79+
80+
case MLNTileOperation.cancelled:
81+
print("Pending work on tile \(tileStr)")
82+
83+
case MLNTileOperation.nullOp:
84+
print("An unknown tile operation was emitted for tile \(tileStr)")
85+
86+
@unknown default:
87+
assertionFailure()
88+
}
89+
}
90+
91+
// #-end-example-code
92+
93+
// #-example-code(ObserverExampleSprites)
94+
func mapView(_: MLNMapView, spriteWillLoad id: String, url: String) {
95+
print("The sprite \(id) has been requested from \(url)")
96+
}
97+
98+
func mapView(_: MLNMapView, spriteDidLoad id: String, url: String) {
99+
print("The sprite \(id) has been loaded from \(url)")
100+
}
101+
// #-end-example-code
102+
}
103+
104+
struct ObserverExampleViewExampleUIViewControllerRepresentable: UIViewControllerRepresentable {
105+
typealias UIViewControllerType = ObserverExampleView
106+
107+
func makeUIViewController(context _: Context) -> ObserverExampleView {
108+
ObserverExampleView()
109+
}
110+
111+
func updateUIViewController(_: ObserverExampleView, context _: Context) {}
112+
}

platform/ios/app/MBXViewController.mm

+91
Original file line numberDiff line numberDiff line change
@@ -2274,6 +2274,97 @@ - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIVi
22742274

22752275
// MARK: - MLNMapViewDelegate
22762276

2277+
- (void)mapView:(MLNMapView *)mapView
2278+
shaderWillCompile:(NSInteger)id
2279+
backend:(NSInteger)backend
2280+
defines:(NSString *)defines
2281+
{
2282+
NSLog(@"A new shader is being compiled - shaderID:%ld, backend type:%ld, program configuration:%@", id, backend, defines);
2283+
}
2284+
2285+
- (void)mapView:(MLNMapView *)mapView
2286+
shaderDidCompile:(NSInteger)id
2287+
backend:(NSInteger)backend
2288+
defines:(NSString *)defines
2289+
{
2290+
NSLog(@"A shader has been compiled - shaderID:%ld, backend type:%ld, program configuration:%@", id, backend, defines);
2291+
}
2292+
2293+
- (void)mapView:(MLNMapView *)mapView
2294+
glyphsWillLoad:(NSArray<NSString *> *)fontStack
2295+
range:(NSRange)range
2296+
{
2297+
NSLog(@"Glyphs are being requested for the font stack %@, ranging from %ld to %ld", fontStack, range.location, range.location + range.length);
2298+
}
2299+
2300+
- (void)mapView:(MLNMapView *)mapView
2301+
glyphsDidLoad:(NSArray<NSString *> *)fontStack
2302+
range:(NSRange)range
2303+
{
2304+
NSLog(@"Glyphs have been loaded for the font stack %@, ranging from %ld to %ld", fontStack, range.location, range.location + range.length);
2305+
}
2306+
2307+
- (void)mapView:(MLNMapView *)mapView
2308+
tileDidTriggerAction:(MLNTileOperation)operation
2309+
x:(NSInteger)x
2310+
y:(NSInteger)y
2311+
z:(NSInteger)z
2312+
wrap:(NSInteger)wrap
2313+
overscaledZ:(NSInteger)overscaledZ
2314+
sourceID:(NSString *)sourceID
2315+
{
2316+
NSString* tileStr = [NSString stringWithFormat:@"(x: %ld, y: %ld, z: %ld, wrap: %ld, overscaledZ: %ld, sourceID: %@)",
2317+
x, y, z, wrap, overscaledZ, sourceID];
2318+
2319+
switch (operation) {
2320+
case MLNTileOperationRequestedFromCache:
2321+
NSLog(@"Requesting tile %@ from cache", tileStr);
2322+
break;
2323+
2324+
case MLNTileOperationRequestedFromNetwork:
2325+
NSLog(@"Requesting tile %@ from network", tileStr);
2326+
break;
2327+
2328+
case MLNTileOperationLoadFromCache:
2329+
NSLog(@"Loading tile %@, requested from the cache", tileStr);
2330+
break;
2331+
2332+
case MLNTileOperationLoadFromNetwork:
2333+
NSLog(@"Loading tile %@, requested from the network", tileStr);
2334+
break;
2335+
2336+
case MLNTileOperationStartParse:
2337+
NSLog(@"Parsing tile %@", tileStr);
2338+
break;
2339+
2340+
case MLNTileOperationEndParse:
2341+
NSLog(@"Completed parsing tile %@", tileStr);
2342+
break;
2343+
2344+
case MLNTileOperationError:
2345+
NSLog(@"An error occured during proccessing for tile %@", tileStr);
2346+
break;
2347+
2348+
case MLNTileOperationCancelled:
2349+
NSLog(@"Pending work on tile %@", tileStr);
2350+
break;
2351+
2352+
case MLNTileOperationNullOp:
2353+
NSLog(@"An unknown tile operation was emitted for tile %@", tileStr);
2354+
break;
2355+
}
2356+
}
2357+
2358+
- (void)mapView:(MLNMapView *)mapView spriteWillLoad:(NSString *)id url:(NSString *)url
2359+
{
2360+
NSLog(@"The sprite %@ has been requested from %@", id, url);
2361+
}
2362+
2363+
- (void)mapView:(MLNMapView *)mapView spriteDidLoad:(NSString *)id url:(NSString *)url
2364+
{
2365+
NSLog(@"The sprite %@ has been loaded from %@", id, url);
2366+
}
2367+
22772368
- (MLNAnnotationView *)mapView:(MLNMapView *)mapView viewForAnnotation:(id<MLNAnnotation>)annotation
22782369
{
22792370
if (annotation == mapView.userLocation)

platform/ios/sdk-files.json

+1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@
183183
"MLNMapCamera.h": "platform/darwin/src/MLNMapCamera.h",
184184
"MLNForegroundStyleLayer.h": "platform/darwin/src/MLNForegroundStyleLayer.h",
185185
"MLNOfflineRegion.h": "platform/darwin/src/MLNOfflineRegion.h",
186+
"MLNTileOperation.h": "platform/darwin/src/MLNTileOperation.h",
186187
"MLNMapViewDelegate.h": "platform/ios/src/MLNMapViewDelegate.h",
187188
"MLNDistanceFormatter.h": "platform/darwin/src/MLNDistanceFormatter.h",
188189
"MLNCustomStyleLayer.h": "platform/darwin/src/MLNCustomStyleLayer.h",

platform/ios/src/MLNMapView+Impl.h

+15
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ class MLNMapViewImpl : public mbgl::MapObserver {
7272
void onDidBecomeIdle() override;
7373
void onStyleImageMissing(const std::string& imageIdentifier) override;
7474
bool onCanRemoveUnusedStyleImage(const std::string& imageIdentifier) override;
75+
void onRegisterShaders(mbgl::gfx::ShaderRegistry&) override;
76+
void onPreCompileShader(mbgl::shaders::BuiltIn, mbgl::gfx::Backend::Type,
77+
const std::string&) override;
78+
void onPostCompileShader(mbgl::shaders::BuiltIn, mbgl::gfx::Backend::Type,
79+
const std::string&) override;
80+
void onShaderCompileFailed(mbgl::shaders::BuiltIn, mbgl::gfx::Backend::Type,
81+
const std::string&) override;
82+
void onGlyphsLoaded(const mbgl::FontStack&, const mbgl::GlyphRange&) override;
83+
void onGlyphsError(const mbgl::FontStack&, const mbgl::GlyphRange&, std::exception_ptr) override;
84+
void onGlyphsRequested(const mbgl::FontStack&, const mbgl::GlyphRange&) override;
85+
void onTileAction(mbgl::TileOperation, const mbgl::OverscaledTileID&,
86+
const std::string&) override;
87+
void onSpriteLoaded(const std::optional<mbgl::style::Sprite>&) override;
88+
void onSpriteError(const std::optional<mbgl::style::Sprite>&, std::exception_ptr) override;
89+
void onSpriteRequested(const std::optional<mbgl::style::Sprite>&) override;
7590

7691
protected:
7792
/// Cocoa map view that this adapter bridges to.

0 commit comments

Comments
 (0)