You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor(iOS): null safety for mapView addToMap, removeFromMap (#4079)
* refactor(ios): introduce RNMBXMapAndMapViewComponent protocol for type-safe mapView access
This commit addresses the issue raised in PR #3963 regarding the optional
mapView property access pattern that could lead to nil access crashes.
Key Changes:
- Created new RNMBXMapAndMapViewComponent protocol that guarantees non-nil
MapView parameter in addToMap and removeFromMap methods
- Kept existing RNMBXMapComponent protocol for components that don't require
direct MapView access
- Updated RNMBXMapView to handle both protocol types and validate mapView
presence before calling RNMBXMapAndMapViewComponent methods
- Created RNMBXMapAndMapViewComponentBase as a base class for components
requiring MapView
Components migrated to RNMBXMapAndMapViewComponent:
- RNMBXCustomLocationProvider: Needs mapView for location provider setup
- RNMBXCamera: Accesses mapView.viewport for status observers
- RNMBXViewport: Directly manages viewport state
- RNMBXNativeUserLocation: Configures location puck via mapView.location
- RNMBXInteractiveElement: Base class that accesses mapView.mapboxMap.style
- RNMBXSource: Manages sources via mapView.mapboxMap.style
- RNMBXPointAnnotation: Inherits from RNMBXInteractiveElement
Architecture Benefits:
- Type system enforces mapView availability at compile time
- Clear contract: components explicitly declare mapView requirement
- Eliminates defensive nil checks in component implementations
- Centralizes nil checking in RNMBXMapView with proper error logging
- Maintains invariant: if addedToMap is true, mapView must be non-nil
This approach is superior to PR #3963's defensive nil checks because:
1. It makes the requirement explicit via protocol
2. Failures are logged at the framework level, not silently ignored
3. Type safety prevents accidental nil access
4. Components can safely assume mapView is valid in lifecycle methods
Related: #3963
* refactor(ios): improve protocol hierarchy with inheritance and default implementations
Refinements to the RNMBXMapComponent protocol architecture:
1. Extracted common protocol requirement into base protocol:
- Created RNMBXMapComponentProtocol with waitForStyleLoad() method
- Both RNMBXMapComponent and RNMBXMapAndMapViewComponent inherit from it
- Eliminates duplication of waitForStyleLoad() in both protocols
2. Added default implementation via Swift protocol extension:
- RNMBXMapComponentProtocol extension provides default waitForStyleLoad() -> false
- Components that need style load can override, others use default
- Removed redundant implementations from:
* RNMBXMapComponentBase
* RNMBXMapAndMapViewComponentBase
* RNMBXCustomLocationProvider
* RNMBXImages
Benefits:
- DRY: Single definition of waitForStyleLoad() requirement
- Convenience: Default implementation reduces boilerplate
- Clarity: Explicit protocol inheritance shows relationship
- Type safety: Compiler enforces common interface
This follows Swift best practices for protocol-oriented programming.
* refactor(ios): make RNMBXMapAndMapViewComponent inherit from RNMBXMapComponent
Changed RNMBXMapAndMapViewComponent to inherit from RNMBXMapComponent instead
of RNMBXMapComponentProtocol, establishing a proper subtype relationship.
Key Changes:
1. Protocol Inheritance:
- RNMBXMapAndMapViewComponent now extends RNMBXMapComponent
- This makes semantic sense: "a component that needs MapView IS A map component"
- Ensures compatibility with existing code that checks for RNMBXMapComponent
2. Default Implementation Safety Guard:
- Added protocol extension with default implementations of base protocol methods
- These implementations log errors and trigger assertions if called
- Forces developers to use the mapView-aware versions
- Catches mistakes at runtime (or compile time in debug builds)
3. Updated RNMBXSource to Handle Both Protocols:
- Changed components array type to RNMBXMapComponentProtocol (base protocol)
- Check for more specific protocol first (RNMBXMapAndMapViewComponent)
- Then fallback to RNMBXMapComponent for components that don't need mapView
- Applied this pattern to insertReactSubviewInternal, removeReactSubviewInternal,
addToMap, and removeFromMap methods
Benefits:
- Type system correctly models the relationship (MapAndMapViewComponent IS A MapComponent)
- Existing code that checks "as? RNMBXMapComponent" still works (matches both types)
- Safety: Can't accidentally call wrong method signature due to default implementations
- Future-proof: New code can store both types in arrays typed as RNMBXMapComponent
RNMBXMapView already checks for the more specific protocol first, so no changes
needed there - the inheritance makes the code more correct without breaking anything.
* ios: build fix
* Update ios/RNMBX/RNMBXCamera.swift
* Update ios/RNMBX/RNMBXCamera.swift
* Update ios/RNMBX/RNMBXCamera.swift
---------
Co-authored-by: Claude <noreply@anthropic.com>
Logger.error("CRITICAL: addToMap(_:style:) called on RNMBXMapAndMapViewComponent. Use addToMap(_:mapView:style:) instead. Component: \(type(of:self))")
Logger.error("CRITICAL: removeFromMap(_:reason:) called on RNMBXMapAndMapViewComponent. Use removeFromMap(_:mapView:reason:) instead. Component: \(type(of:self))")
0 commit comments