maplibre / maplibre/maplibre-react-native

post_install adds MapLibre SPM dependency to every user_target, crashing unrelated App Extensions (e.g. WidgetKit)

Open Beginner friendly
#1,650 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
661
Forks
124
Avg merge
1d 2h
Merged PRs (30d)
30

Description

## Summary

`MapLibreReactNative.podspec`'s `$MLRN.post_install` loops `installer.aggregate_targets` and, for **every** `user_target` it finds, unconditionally adds the MapLibre Swift Package dependency — with no check for whether that target actually depends on `MapLibreReactNative`. In a project that has a second native target integrated with its own CocoaPods aggregate target in the same `Podfile` (a WidgetKit / Notification Service / any App Extension target that pods something unrelated to MapLibre), that target gets the MapLibre SPM product linked into it too. Since nothing embeds the actual `MapLibre.framework` into that extension's bundle, the extension crashes on launch:

```
Library not loaded: @rpath/MapLibre.framework/MapLibre
Reason: tried: ... (no such file), ...
```

For a WidgetKit extension specifically, this crash happens before the extension can report any widget kinds to `chronod`/WidgetKit, so the app's widgets never show up in the widget gallery at all — with no build error, since each target compiles independently and Xcode only fails at **runtime**, when the extension process tries to `dyld`-load a framework that was never embedded there.

## Where

https://github.com/maplibre/maplibre-react-native/blob/main/MapLibreReactNative.podspec

```ruby
def $MLRN.post_install(installer)
spm_spec = $MLRN_SPM_SPEC

project = installer.pods_project
mlrn_target = project.targets.find { |t| t.name == "MapLibreReactNative" }
self._add_spm_to_target(
project,
mlrn_target,
spm_spec[:url],
spm_spec[:requirement],
spm_spec[:product_name]
)

installer.aggregate_targets.group_by(&:user_project).each do |project, targets|
targets.each do |target|
target.user_targets.each do |user_target|
self._add_spm_to_target(
project,
user_target,
spm_spec[:url],
spm_spec[:requirement],
spm_spec[:product_name]
)

phase_name = "[MapLibre React Native] Remove MapLibre.xcframework-ios.signature"
unless user_target.shell_script_build_phases.any? { |p| p.name == phase_name }
phase = user_target.new_shell_script_build_phase(phase_name)
phase.shell_script = 'rm -rf "$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature"'
phase.always_out_of_date = "1"
end
end
end
end
end
```

The second loop (`installer.aggregate_targets.group_by(&:user_project)...`) walks **every** aggregate target in the whole `Pods` install — not just the one that actually pods `MapLibreReactNative` — and calls `_add_spm_to_target` + adds the signature-removal build phase unconditionally on every `user_target` it finds.

## Repro

1. A React Native / Expo app using `@maplibre/maplibre-react-native` in the main app target.
2. Add a second native target with its own CocoaPods integration in the same `Podfile`, e.g.:
```ruby
target 'MyApp' do
# ... maplibre-react-native autolinked here via use_native_modules!
end

target 'MyAppWidget' do
pod 'SomeUnrelatedPod'
end
```
(This is exactly what Expo's Voltra widget plugin (`@use-voltra/ios-client`) generates — a widget extension target with its own CocoaPods target that only pods `VoltraWidget`, nothing MapLibre-related.)
3. Run `pod install`.
4. Inspect `MyAppWidget`'s Xcode target: it now has a Swift Package dependency on `MapLibre` (from `maplibre-gl-native-distribution`) and a `[MapLibre React Native] Remove MapLibre.xcframework-ios.signature` build phase, despite never referencing MapLibre.
5. Build and run the extension (or check `codesign`/the built `.appex`'s Mach-O load commands): the extension binary references `@rpath/MapLibre.framework/MapLibre`, but the framework is never embedded in that target's bundle, so the extension crashes on launch with `Library not loaded: @rpath/MapLibre.framework/MapLibre`.

## Expected behavior

Only targets that actually depend on `MapLibreReactNative` (i.e., targets whose aggregate target includes the `MapLibreReactNative` pod) should get the SPM dependency and the signature-removal build phase. Unrelated targets (extensions, etc.) sharing the same `Podfile`/workspace should be left alone.

## Suggested fix

Skip `user_target`s whose aggregate target doesn't actually include `MapLibreReactNative`, e.g.:

```ruby
installer.aggregate_targets.group_by(&:user_project).each do |project, targets|
targets.each do |target|
next unless target.pod_targets.map(&:pod_name).include?("MapLibreReactNative")

target.user_targets.each do |user_target|
# ... existing logic unchanged
end
end
end
```

Happy to open a PR with this change (or an equivalent guard) if that's welcome.

## Environment

- `@maplibre/maplibre-react-native`: 11.3.10 (bug present here too — confirmed against the current `main` podspec logic, not just an older version)
- Expo SDK 57, React Native 0.86
- The second target in our case is a WidgetKit extension generated by Expo's Voltra plugin (`@use-voltra/ios-client`), but the bug isn't specific to Voltra — any second CocoaPods-integrated target in the same workspace is affected.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in MapLibreReactNative.podspec at `$MLRN.post_install`, especially the loop over `installer.aggregate_targets`. Run `pod install` with a MapLibre app target and an unrelated extension target, then verify that only aggregates containing `MapLibreReactNative` receive the SPM dependency and signature-removal phase; the extension should no longer reference or load MapLibre.framework.

Written by the indexing model from the issue text.

Assessment

Tech stack
ios, react-native, ruby
Domain
build-system, mobile
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.