Skip to content

fix(ios): resolve pending layer waiters - #4288

Open
LukasB-DEV wants to merge 1 commit into
rnmapbox:mainfrom
LukasB-DEV:codex/fix-ios-layer-waiters
Open

fix(ios): resolve pending layer waiters#4288
LukasB-DEV wants to merge 1 commit into
rnmapbox:mainfrom
LukasB-DEV:codex/fix-ios-layer-waiters

Conversation

@LukasB-DEV

Copy link
Copy Markdown

Description

On iOS, a layer mounted with belowLayerID before its anchor layer exists is queued by waitForLayerWithID. However, RNMBXMapView.layerAdded(_:) was left as a TODO, so the queued callbacks were never invoked after the anchor layer was added.

As a result, the dependent layer remained absent from the map.

This change removes and invokes the callbacks waiting for the newly added layer. It mirrors the existing Android implementation and also prevents fulfilled callbacks from remaining retained in layerWaiters.

This is a general iOS layer-ordering issue and is not specific to an application integration.

No linked issue.

Reproduction and verification

Tested against unmodified main

Environment:

  • @rnmapbox/maps 10.3.5
  • Mapbox Maps SDK v11.23.1
  • React Native New Architecture
  • iPhone 17 Pro Simulator
  • iOS 26.5
  • Xcode 26.6
  • Node.js 22.16.0

Before the fix:

  • FAIL: WAITING LAYER MISSING
  • The magenta anchor layer was visible.
  • The blue layer waiting for belowLayerID="anchor-layer" was absent.

After the fix:

  • PASS: WAITING LAYER RENDERED
  • Both the blue waiting layer and the magenta anchor layer were visible.

Checklist

  • I've read CONTRIBUTING.md
  • I updated the doc/other generated code with running yarn generate in the root folder
    • yarn generate completed successfully and produced no changes.
  • I have tested the fix on the /example app.
    • In V11 mode/ios
    • In New Architecture mode/ios
    • In V11 mode/android — not applicable to this iOS-only change
    • In New Architecture mode/android — not applicable to this iOS-only change
  • I added/updated a sample — not applicable; this is a bug fix and the temporary reproducer was intentionally kept out of the final commit.

Screenshot OR Video

Before — unfixed main:

image

After — commit 9af2571:

image

Tests

  • yarn generate — passed, generated tree remained unchanged
  • yarn test — passed, 22 test suites and 112 tests
  • ESLint — 0 errors; existing repository warnings only
  • yarn typecheck — passed
  • iOS Example Debug build — passed before and after the fix
  • Reproducer ESLint check — passed
  • git diff --check — passed

Component to reproduce the issue you're fixing

BugReportExample.js
import React from 'react';
import { Text, View } from 'react-native';
import {
  Camera,
  LineLayer,
  MapView,
  ShapeSource,
  StyleURL,
} from '@rnmapbox/maps';

const routes = {
  type: 'FeatureCollection',
  features: [
    {
      type: 'Feature',
      properties: { kind: 'waiting' },
      geometry: {
        type: 'LineString',
        coordinates: [
          [-74.012, 40.716],
          [-74.0, 40.716],
        ],
      },
    },
    {
      type: 'Feature',
      properties: { kind: 'anchor' },
      geometry: {
        type: 'LineString',
        coordinates: [
          [-74.012, 40.713],
          [-74.0, 40.713],
        ],
      },
    },
  ],
};

const styles = {
  map: { flex: 1 },
  status: {
    position: 'absolute',
    top: 60,
    left: 12,
    right: 12,
    padding: 12,
    backgroundColor: 'white',
  },
  statusText: { color: 'black', fontWeight: '700' },
};

class BugReportExample extends React.Component {
  state = { status: 'CHECKING' };

  checkWaitingLayer = async () => {
    try {
      const result = await this.map.queryRenderedFeaturesInRect([], null, [
        'waiting-layer',
      ]);

      this.setState({
        status: result?.features?.length
          ? 'PASS: WAITING LAYER RENDERED'
          : 'FAIL: WAITING LAYER MISSING',
      });
    } catch {
      this.setState({ status: 'FAIL: WAITING LAYER MISSING' });
    }
  };

  render() {
    return (
      <View style={styles.map}>
        <MapView
          ref={(map) => {
            this.map = map;
          }}
          style={styles.map}
          styleURL={StyleURL.Light}
          onMapIdle={this.checkWaitingLayer}
        >
          <Camera
            defaultSettings={{
              centerCoordinate: [-74.006, 40.7145],
              zoomLevel: 14,
            }}
          />

          <ShapeSource id="layer-waiter-reproducer" shape={routes}>
            <LineLayer
              id="waiting-layer"
              belowLayerID="anchor-layer"
              filter={['==', ['get', 'kind'], 'waiting']}
              style={{ lineColor: '#0066ff', lineWidth: 12 }}
            />

            <LineLayer
              id="anchor-layer"
              filter={['==', ['get', 'kind'], 'anchor']}
              style={{ lineColor: '#ff0066', lineWidth: 12 }}
            />
          </ShapeSource>
        </MapView>

        <View style={styles.status}>
          <Text testID="layer-waiter-status" style={styles.statusText}>
            {this.state.status}
          </Text>
        </View>
      </View>
    );
  }
}

export default BugReportExample;

@LukasB-DEV
LukasB-DEV requested a deployment to CI with Mapbox Tokens August 27, 2026 08:15 — with GitHub Actions Waiting
@LukasB-DEV
LukasB-DEV requested a deployment to CI with Mapbox Tokens August 27, 2026 08:15 — with GitHub Actions Waiting
@LukasB-DEV
LukasB-DEV requested a deployment to CI with Mapbox Tokens August 27, 2026 08:15 — with GitHub Actions Waiting
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant