Skip to content

Commit 7bf6dd9

Browse files
mapbox-github-ci-writer-1[bot]evil159
authored andcommitted
[Backport release/v0.17] expose async method to set and update layer properties (#8170)
Backport 4526b93b9b0d42ecf003cfe209598f6413c0c3be from #8140. cc @mapbox/sdk-ci cc @mapbox/maps-ios --------- Co-authored-by: Roman Laitarenko <[email protected]> GitOrigin-RevId: aa17521fb2dad8c5b4ab28ca6aa1a55b6a7971ac
1 parent b5b7b3e commit 7bf6dd9

File tree

6 files changed

+148
-3
lines changed

6 files changed

+148
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Mapbox welcomes participation and contributions from everyone.
1313
* Add `RasterArraySource.volatile` experimental property.
1414
* Make `line-emissive-strength` property data-driven.
1515
* Add experimental `MapboxMap.setFeatureStateExpression()`, `removeFeatureStateExpression()`, and `resetFeatureStateExpressions()` APIs to efficiently update feature state for multiple features at once using expressions.
16+
* Add experimental async variants of `MapboxMap.setLayerProperty()`, `setLayerProperties` and `updateLayer()`.
1617

1718
## 11.17.0-beta.1 - 05 November, 2025
1819

Sources/MapboxMaps/Foundation/CoreAliases.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,4 @@ typealias CoreFeaturesetFeatureId = MapboxCoreMaps_Private.FeaturesetFeatureId
4646
typealias CoreFeaturesetQueryTarget = MapboxCoreMaps_Private.FeaturesetQueryTarget
4747
typealias CoreFeaturesetDescriptor = MapboxCoreMaps_Private.FeaturesetDescriptor
4848
typealias CoreColorTheme = MapboxCoreMaps_Private.ColorTheme
49+
typealias CoreAsyncOperationResultCallback = MapboxCoreMaps_Private.AsyncOperationResultCallback

Sources/MapboxMaps/Style/StyleManager.swift

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,34 @@ public class StyleManager {
193193
public func updateLayer<T>(withId id: String,
194194
type: T.Type,
195195
update: (inout T) throws -> Void) throws where T: Layer {
196+
let layerProperties = try resolveUpdatedLayerProperties(withId: id, update)
197+
// Apply the changes to the layer properties to the style
198+
try setLayerProperties(for: id, properties: layerProperties)
199+
}
200+
201+
/// Updates a `layer` that exists in the `style` already
202+
///
203+
/// - Parameters:
204+
/// - id: identifier of layer to update
205+
/// - type: Type of the layer
206+
/// - update: Closure that mutates a layer passed to it
207+
///
208+
/// - Throws: ``TypeConversionError`` if there is a problem getting a layer data.
209+
/// - Throws: ``StyleError`` if there is a problem updating the layer.
210+
/// - Throws: An error when executing `update` block.
211+
@_spi(Experimental)
212+
public func updateLayer<T>(withId id: String,
213+
type: T.Type,
214+
update: (inout T) throws -> Void) async throws where T: Layer {
215+
let layerProperties = try resolveUpdatedLayerProperties(withId: id, update)
216+
// Apply the changes to the layer properties to the style
217+
try await setLayerProperties(for: id, properties: layerProperties)
218+
}
219+
220+
private func resolveUpdatedLayerProperties<T: Layer>(
221+
withId id: String,
222+
_ update: (inout T) throws -> Void
223+
) throws -> [String: Any] {
196224
let oldLayerProperties = try layerProperties(for: id)
197225
var layer = try T(jsonObject: oldLayerProperties)
198226

@@ -222,9 +250,7 @@ public class StyleManager {
222250
reduceStrategy(&result, element)
223251
}
224252
})
225-
226-
// Apply the changes to the layer properties to the style
227-
try setLayerProperties(for: id, properties: layerProperties)
253+
return layerProperties
228254
}
229255

230256
// MARK: - Sources
@@ -822,6 +848,22 @@ public class StyleManager {
822848
}
823849
}
824850

851+
/// Sets a JSON value to a style layer property.
852+
///
853+
/// - Parameters:
854+
/// - layerId: Style layer identifier.
855+
/// - property: Style layer property name.
856+
/// - value: Style layer property value.
857+
///
858+
/// - Throws:
859+
/// An error describing why the operation was unsuccessful.
860+
@_spi(Experimental)
861+
public func setLayerProperty(for layerId: String, property: String, value: Any) async throws {
862+
try await handleExpected { callback in
863+
return styleManager.__setStyleLayerPropertyAsyncForLayerId(layerId, property: property, value: value, callback: callback)
864+
}
865+
}
866+
825867
/// Gets the default value of style layer property.
826868
///
827869
/// - Parameters:
@@ -871,6 +913,29 @@ public class StyleManager {
871913
}
872914
}
873915

916+
/// Sets style layer properties.
917+
///
918+
/// This method can be used to perform batch update for a style layer properties.
919+
/// The structure of a provided `properties` value must conform to the
920+
/// [format for a corresponding layer type](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/).
921+
///
922+
/// Modification of a [layer identifier](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#id)
923+
/// and/or [layer type](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#type)
924+
/// is not allowed.
925+
///
926+
/// - Parameters:
927+
/// - layerId: Style layer identifier.
928+
/// - properties: JSON dictionary representing the updated layer properties.
929+
///
930+
/// - Throws:
931+
/// An error describing why the operation was unsuccessful.
932+
@_spi(Experimental)
933+
public func setLayerProperties(for layerId: String, properties: [String: Any]) async throws {
934+
try await handleExpected { callback in
935+
return styleManager.__setStyleLayerPropertiesAsyncForLayerId(layerId, properties: properties, callback: callback)
936+
}
937+
}
938+
874939
// MARK: - Sources
875940

876941
/// Adds a new style source.

Sources/MapboxMaps/Style/StyleManagerProtocol.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,23 @@ internal protocol StyleManagerProtocol {
9292
property: String,
9393
value: Any) -> Expected<NSNull, NSString>
9494

95+
func __setStyleLayerPropertyAsyncForLayerId(
96+
_ layerId: String,
97+
property: String,
98+
value: Any,
99+
callback: @escaping CoreAsyncOperationResultCallback) -> any Cancelable
100+
95101
func getStyleLayerProperties(forLayerId layerId: String) -> Expected<AnyObject, NSString>
96102

97103
func setStyleLayerPropertiesForLayerId(
98104
_ layerId: String,
99105
properties: Any) -> Expected<NSNull, NSString>
100106

107+
func __setStyleLayerPropertiesAsyncForLayerId(
108+
_ layerId: String,
109+
properties: Any,
110+
callback: @escaping CoreAsyncOperationResultCallback) -> any Cancelable
111+
101112
func addStyleSource(
102113
forSourceId sourceId: String,
103114
properties: Any) -> Expected<NSNull, NSString>

Tests/MapboxMapsTests/Foundation/Mocks/MockStyleManager.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,38 @@ class MockStyleManager: StyleManagerProtocol {
415415
)
416416
}
417417

418+
struct SetStyleLayerPropertyAsyncParameters {
419+
let layerId: String
420+
let property: String
421+
let value: Any
422+
let callback: CoreAsyncOperationResultCallback
423+
}
424+
let setStyleLayerPropertyAsyncStub = Stub<SetStyleLayerPropertyAsyncParameters, Cancelable>(
425+
defaultReturnValue: AnyCancelable { }
426+
)
427+
func __setStyleLayerPropertyAsyncForLayerId(_ layerId: String, property: String, value: Any, callback: @escaping CoreAsyncOperationResultCallback) -> Cancelable {
428+
let result = setStyleLayerPropertyAsyncStub.call(
429+
with: SetStyleLayerPropertyAsyncParameters(layerId: layerId, property: property, value: value, callback: callback)
430+
)
431+
callback(.init(value: NSNull()))
432+
return result
433+
}
434+
struct SetStyleLayerPropertiesAsyncParameters {
435+
let layerId: String
436+
let properties: Any
437+
let callback: CoreAsyncOperationResultCallback
438+
}
439+
let setStyleLayerPropertiesAsyncStub = Stub<SetStyleLayerPropertiesAsyncParameters, Cancelable>(
440+
defaultReturnValue: AnyCancelable { }
441+
)
442+
func __setStyleLayerPropertiesAsyncForLayerId(_ layerId: String, properties: Any, callback: @escaping CoreAsyncOperationResultCallback) -> Cancelable {
443+
let result = setStyleLayerPropertiesAsyncStub.call(
444+
with: SetStyleLayerPropertiesAsyncParameters(layerId: layerId, properties: properties, callback: callback)
445+
)
446+
callback(.init(value: NSNull()))
447+
return result
448+
}
449+
418450
struct SetStyleSourceParameters {
419451
let sourceId: String
420452
let properties: Any

Tests/MapboxMapsTests/Foundation/Style/StyleTests.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,41 @@ final class StyleManagerTests: XCTestCase {
541541
XCTAssertEqual(layoutProperties["visibility"] as? String, "visible", "visibility is not reset and should keep old value")
542542
}
543543

544+
func testStyleCanUpdateLayerAsync() async throws {
545+
styleManager.getStyleLayerPropertiesStub.defaultReturnValue = Expected(value: NSDictionary(dictionary: [
546+
"id": "dummy-layer-id",
547+
"type": "background",
548+
"minzoom": 1,
549+
"maxzoom": 10,
550+
"paint": [
551+
"background-opacity-transition": ["delay": 1, "duration": 100],
552+
],
553+
"layout": [
554+
"visibility": "visible",
555+
],
556+
]))
557+
try await style.updateLayer(
558+
withId: "dummy-layer-id",
559+
type: BackgroundLayer.self) { layer in
560+
layer.minZoom = nil
561+
layer.maxZoom = 12
562+
layer.backgroundOpacityTransition = nil
563+
layer.backgroundOpacity = nil
564+
}
565+
566+
let rootProperties = try XCTUnwrap(styleManager.setStyleLayerPropertiesAsyncStub.invocations.last!.parameters.properties as? [String: Any])
567+
XCTAssertEqual(rootProperties["id"] as? String, "dummy-layer-id", "id should always be presented")
568+
XCTAssertTrue(rootProperties.keys.contains("minzoom"), "minzoom is reset and should be presented")
569+
XCTAssertEqual(rootProperties["maxzoom"] as? Double, 12, "maxzoom is updated and should be presented")
570+
571+
let paintProperties = try XCTUnwrap(rootProperties["paint"] as? [String: Any])
572+
XCTAssertTrue(paintProperties.keys.contains("background-opacity-transition"), "background-opacity-transition is reset and should be presented")
573+
XCTAssertFalse(paintProperties.keys.contains("background-opacity"), "background-opacity is newly added and should be presented")
574+
575+
let layoutProperties = try XCTUnwrap(rootProperties["layout"] as? [String: Any])
576+
XCTAssertEqual(layoutProperties["visibility"] as? String, "visible", "visibility is not reset and should keep old value")
577+
}
578+
544579
func testAddImageWithStretches() throws {
545580
let image = UIImage.empty
546581
let id = UUID().uuidString

0 commit comments

Comments
 (0)