Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ platform :ios, '15.1'
prepare_react_native_project!

$RNMapboxMapsImpl = 'mapbox'
# RNMBX_SPM=1 pod install: get MapboxMaps through Swift Package Manager instead of CocoaPods
$RNMapboxMapsSwiftPackageManager = true if ENV['RNMBX_SPM'] == '1'
if ENV['RNMBX10']
$RNMapboxMapsVersion = '10.19.0'
end
Expand Down
22 changes: 22 additions & 0 deletions ios/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,28 @@ $RNMapboxMapsVersion = '= 11.16.2'

If using expo managed workflow, set the "RNMapboxMapsVersion" variable. See the [expo guide](/plugin/install.md)

## Using Swift Package Manager (experimental)

By default the Mapbox SDK comes from CocoaPods. To get it through Swift Package Manager instead, set this at the top of your `ios/Podfile`:

```ruby
$RNMapboxMapsSwiftPackageManager = true
```

The package requirement is derived from `$RNMapboxMapsVersion` (or the default version): `~> 11.23.1` becomes "up to next minor" from `11.23.1`, `= 11.23.1` an exact version. Your Podfile must call `react_native_post_install`, which adds the package. To override parts of the setup, pass a hash instead:

```ruby
$RNMapboxMapsSwiftPackageManager = {
url: 'https://github.com/mapbox/mapbox-maps-ios.git',
requirement: { kind: 'exactVersion', version: '11.23.1' },
product_name: 'MapboxMaps',
}
```

The `rnmapbox-maps` pod links `MapboxMaps`, and `MapboxCommon`, `MapboxCoreMaps` and `Turf` are embedded by CocoaPods' `[CP] Embed Pods Frameworks` phase. Don't add the `mapbox-maps-ios` package to your app target as well, that links a second copy; `pod install` removes it if an earlier `@rnmapbox/maps` version added it.

With Expo, set `"RNMapboxMapsSwiftPackageManager": true` (or the hash as an object) in the plugin options. See the [expo guide](/plugin/install.md).

## Troubleshooting

### Pod install fails on upgrade of @rnmapbox/maps with `could not find compatible versions for pod "MapboxMaps"`
Expand Down
62 changes: 62 additions & 0 deletions ios/rnmapbox_spm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# CocoaPods helpers for pods that get Mapbox frameworks through Swift Package Manager.
module RNMapboxSPM
@frameworks_by_pod ||= {}
@embedded_by_react_native ||= []

# CocoaPods doesn't embed the dynamic frameworks of a pod's Swift packages, so the app would crash at launch
# with dyld "Library not loaded: @rpath/<name>.framework". Listing them among the frameworks CocoaPods embeds
# also makes it create the embed phase in apps without any dynamic pod.
def self.embed_frameworks(pod_name, framework_names)
@frameworks_by_pod[pod_name] = framework_names
end

# facebook/react-native#58781 makes `spm_dependency` embed package frameworks itself, but only into an existing
# embed script.
def self.spm_dependency_options(pod_name, framework_names)
return {} unless defined?(::SPMManager) &&
::SPMManager.instance_method(:dependency).parameters.include?([:key, :embed_frameworks])
@embedded_by_react_native << pod_name
{ embed_frameworks: framework_names }
end

def self.frameworks_to_embed(aggregate_target, embed_script_exists)
aggregate_target.pod_targets.flat_map do |pod|
next [] if embed_script_exists && @embedded_by_react_native.include?(pod.pod_name)
@frameworks_by_pod.fetch(pod.pod_name, [])
end.uniq
end

# Xcode 26 archives collect xcframework signatures into one folder, and the copies Xcode writes to both the
# shared products dir and the pod's own build dir fail the archive with "<name>.xcframework-ios.signature
# already exists". From react-native 0.87 a static pod with Swift packages builds into the shared dir, which
# then holds the only copy.
def self.signature_cleanup_script_phase(pod_name)
{
:name => "[#{pod_name}] Remove duplicate xcframework signatures",
:script => '[ "${CONFIGURATION_BUILD_DIR}" = "${PODS_CONFIGURATION_BUILD_DIR}" ] || ' \
'rm -f "${CONFIGURATION_BUILD_DIR}"/*.xcframework-*.signature',
:execution_position => :after_compile,
:always_out_of_date => '1'
}
end

module AggregateTarget
def framework_paths_by_config
@rnmapbox_spm_framework_paths_by_config ||= begin
paths_by_config = super
embed_script_exists = paths_by_config.values.any?(&:any?) ||
xcframeworks_by_config.values.flatten.any? { |xcframework| xcframework.build_type.dynamic_framework? }
frameworks = RNMapboxSPM.frameworks_to_embed(self, embed_script_exists)
paths_by_config.transform_values do |paths|
missing = frameworks.reject do |name|
paths.any? { |path| path.source_path.end_with?("/#{name}.framework") }
end
# Swift package binary targets are copied to the shared products dir whatever the pod's build dir is.
paths + missing.map { |name| Pod::Xcode::FrameworkPaths.new("${PODS_CONFIGURATION_BUILD_DIR}/#{name}.framework") }
end
end
end
end
end

Pod::AggregateTarget.prepend(RNMapboxSPM::AggregateTarget) unless Pod::AggregateTarget.ancestors.include?(RNMapboxSPM::AggregateTarget)
18 changes: 16 additions & 2 deletions plugin/build/withMapbox.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,24 @@ export type MapboxPlugProps = {
*/
RNMapboxMapsDownloadToken?: string;
RNMapboxMapsUseV11?: boolean;
/**
* Get the Mapbox Maps SDK through Swift Package Manager instead of CocoaPods on iOS.
* `true`, or the options of `$RNMapboxMapsSwiftPackageManager`, see ios/install.md.
*/
RNMapboxMapsSwiftPackageManager?: boolean | SwiftPackageManagerOptions;
};
export type SwiftPackageManagerOptions = {
url?: string;
requirement?: {
kind: string;
version?: string;
minimumVersion?: string;
};
product_name?: string;
};
export declare const addInstallerBlock: (src: string, blockName: InstallerBlockName) => string;
export declare const addConstantBlock: (src: string, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, }: MapboxPlugProps) => string;
export declare const applyCocoaPodsModifications: (contents: string, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, }: MapboxPlugProps) => string;
export declare const addConstantBlock: (src: string, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, RNMapboxMapsSwiftPackageManager, }: MapboxPlugProps) => string;
export declare const applyCocoaPodsModifications: (contents: string, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, RNMapboxMapsSwiftPackageManager, }: MapboxPlugProps) => string;
export declare const addMapboxInstallerBlock: (src: string, blockName: InstallerBlockName) => string;
export declare const addMapboxMavenRepo: (src: string) => string;
declare const _default: ConfigPlugin<MapboxPlugProps>;
Expand Down
26 changes: 19 additions & 7 deletions plugin/build/withMapbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ try {
catch {
// empty catch block
}
const toRubyLiteral = (value) => {
if (value !== null && typeof value === 'object') {
const entries = Object.entries(value).map(([key, item]) => `${key}: ${toRubyLiteral(item)}`);
return `{ ${entries.join(', ')} }`;
}
return JSON.stringify(value);
};
const addInstallerBlock = (src, blockName) => {
const matchBlock = new RegExp(`${blockName}_install do \\|installer\\|`);
const tag = `${blockName}_installer`;
Expand Down Expand Up @@ -46,11 +53,12 @@ const addInstallerBlock = (src, blockName) => {
}).contents;
};
exports.addInstallerBlock = addInstallerBlock;
const addConstantBlock = (src, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, }) => {
const addConstantBlock = (src, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, RNMapboxMapsSwiftPackageManager, }) => {
const tag = `@rnmapbox/maps-rnmapboxmapsimpl`;
if (RNMapboxMapsVersion == null &&
RNMapboxMapsDownloadToken == null &&
RNMapboxMapsUseV11 == null) {
RNMapboxMapsUseV11 == null &&
!RNMapboxMapsSwiftPackageManager) {
const modified = (0, generateCode_1.removeGeneratedContents)(src, tag);
if (!modified) {
return src;
Expand All @@ -74,6 +82,9 @@ const addConstantBlock = (src, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapbox
if (RNMapboxMapsUseV11) {
newSrc.push(`$RNMapboxMapsUseV11 = true`);
}
if (RNMapboxMapsSwiftPackageManager) {
newSrc.push(`$RNMapboxMapsSwiftPackageManager = ${toRubyLiteral(RNMapboxMapsSwiftPackageManager)}`);
}
return (0, generateCode_1.mergeContents)({
tag,
src,
Expand All @@ -85,15 +96,14 @@ const addConstantBlock = (src, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapbox
}).contents;
};
exports.addConstantBlock = addConstantBlock;
// Only the preinstaller block is required, the post installer block is
// used for spm (swift package manager) which Expo doesn't currently support.
const applyCocoaPodsModifications = (contents, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, }) => {
const applyCocoaPodsModifications = (contents, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, RNMapboxMapsSwiftPackageManager, }) => {
// Ensure installer blocks exist
let src = (0, exports.addConstantBlock)(contents, {
RNMapboxMapsImpl,
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
});
src = (0, exports.addInstallerBlock)(src, 'pre');
src = (0, exports.addInstallerBlock)(src, 'post');
Expand All @@ -117,7 +127,7 @@ exports.addMapboxInstallerBlock = addMapboxInstallerBlock;
*
* https://github.com/rnmapbox/maps/blob/main/ios/install.md#react-native--0600
*/
const withCocoaPodsInstallerBlocks = (config, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, } = {}) => (0, config_plugins_1.withDangerousMod)(config, [
const withCocoaPodsInstallerBlocks = (config, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, RNMapboxMapsSwiftPackageManager, } = {}) => (0, config_plugins_1.withDangerousMod)(config, [
'ios',
async (exportedConfig) => {
const file = path_1.default.join(exportedConfig.modRequest.platformProjectRoot, 'Podfile');
Expand All @@ -127,6 +137,7 @@ const withCocoaPodsInstallerBlocks = (config, { RNMapboxMapsImpl, RNMapboxMapsVe
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
}), 'utf-8');
return exportedConfig;
},
Expand Down Expand Up @@ -283,7 +294,7 @@ const withMapboxAndroid = (config, { RNMapboxMapsImpl, RNMapboxMapsDownloadToken
config = withAndroidAppGradle(config, { RNMapboxMapsImpl });
return config;
};
const withMapbox = (config, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, } = {}) => {
const withMapbox = (config, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMapsDownloadToken, RNMapboxMapsUseV11, RNMapboxMapsSwiftPackageManager, } = {}) => {
config = withMapboxAndroid(config, {
RNMapboxMapsImpl,
RNMapboxMapsVersion,
Expand All @@ -295,6 +306,7 @@ const withMapbox = (config, { RNMapboxMapsImpl, RNMapboxMapsVersion, RNMapboxMap
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
});
return config;
};
Expand Down
17 changes: 17 additions & 0 deletions plugin/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@ It's possible to overwrite the native SDK version with `RNMapboxMapsVersion`:
}
```

To get the Mapbox SDK through Swift Package Manager on iOS, set `RNMapboxMapsSwiftPackageManager` to `true`, or to the options described in the [iOS guide](/ios/install.md#using-swift-package-manager-experimental):

```json
{
"expo": {
"plugins": [
[
"@rnmapbox/maps",
{
"RNMapboxMapsSwiftPackageManager": true
}
]
]
}
}
```

## Manual Setup

For bare workflow projects, you can follow the manual setup guides:
Expand Down
17 changes: 17 additions & 0 deletions plugin/src/__tests__/withMapbox-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,23 @@ describe('applyCocoaPodsModifications', () => {
}),
).toMatchSnapshot();
});
it('sets $RNMapboxMapsSwiftPackageManager', () => {
expect(
applyCocoaPodsModifications(iosFixtures.expoTemplatePodfile, {
RNMapboxMapsSwiftPackageManager: true,
}),
).toContain('$RNMapboxMapsSwiftPackageManager = true\n');
expect(
applyCocoaPodsModifications(iosFixtures.expoTemplatePodfile, {
RNMapboxMapsSwiftPackageManager: {
requirement: { kind: 'exactVersion', version: '11.23.1' },
product_name: 'MapboxMaps',
},
}),
).toContain(
`$RNMapboxMapsSwiftPackageManager = { requirement: { kind: "exactVersion", version: "11.23.1" }, product_name: "MapboxMaps" }\n`,
);
});
it('adds blocks to a expo prebuild template podfile', () => {
expect(
applyCocoaPodsModifications(iosFixtures.expoTemplatePodfile, {}),
Expand Down
40 changes: 37 additions & 3 deletions plugin/src/withMapbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ export type MapboxPlugProps = {
RNMapboxMapsDownloadToken?: string;

RNMapboxMapsUseV11?: boolean;

/**
* Get the Mapbox Maps SDK through Swift Package Manager instead of CocoaPods on iOS.
* `true`, or the options of `$RNMapboxMapsSwiftPackageManager`, see ios/install.md.
*/
RNMapboxMapsSwiftPackageManager?: boolean | SwiftPackageManagerOptions;
};

export type SwiftPackageManagerOptions = {
url?: string;
requirement?: { kind: string; version?: string; minimumVersion?: string };
product_name?: string;
};

const toRubyLiteral = (value: unknown): string => {
if (value !== null && typeof value === 'object') {
const entries = Object.entries(value).map(
([key, item]) => `${key}: ${toRubyLiteral(item)}`,
);
return `{ ${entries.join(', ')} }`;
}
return JSON.stringify(value);
};

export const addInstallerBlock = (
Expand Down Expand Up @@ -86,14 +108,16 @@ export const addConstantBlock = (
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
}: MapboxPlugProps,
): string => {
const tag = `@rnmapbox/maps-rnmapboxmapsimpl`;

if (
RNMapboxMapsVersion == null &&
RNMapboxMapsDownloadToken == null &&
RNMapboxMapsUseV11 == null
RNMapboxMapsUseV11 == null &&
!RNMapboxMapsSwiftPackageManager
) {
const modified = removeGeneratedContents(src, tag);
if (!modified) {
Expand Down Expand Up @@ -128,6 +152,12 @@ export const addConstantBlock = (
newSrc.push(`$RNMapboxMapsUseV11 = true`);
}

if (RNMapboxMapsSwiftPackageManager) {
newSrc.push(
`$RNMapboxMapsSwiftPackageManager = ${toRubyLiteral(RNMapboxMapsSwiftPackageManager)}`,
);
}

return mergeContents({
tag,
src,
Expand All @@ -139,15 +169,14 @@ export const addConstantBlock = (
}).contents;
};

// Only the preinstaller block is required, the post installer block is
// used for spm (swift package manager) which Expo doesn't currently support.
export const applyCocoaPodsModifications = (
contents: string,
{
RNMapboxMapsImpl,
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
}: MapboxPlugProps,
): string => {
// Ensure installer blocks exist
Expand All @@ -156,6 +185,7 @@ export const applyCocoaPodsModifications = (
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
});
src = addInstallerBlock(src, 'pre');
src = addInstallerBlock(src, 'post');
Expand Down Expand Up @@ -191,6 +221,7 @@ const withCocoaPodsInstallerBlocks: ConfigPlugin<MapboxPlugProps> = (
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
}: MapboxPlugProps = {},
) =>
withDangerousMod(config, [
Expand All @@ -209,6 +240,7 @@ const withCocoaPodsInstallerBlocks: ConfigPlugin<MapboxPlugProps> = (
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
}),
'utf-8',
);
Expand Down Expand Up @@ -464,6 +496,7 @@ const withMapbox: ConfigPlugin<MapboxPlugProps> = (
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
}: MapboxPlugProps = {},
) => {
config = withMapboxAndroid(config, {
Expand All @@ -477,6 +510,7 @@ const withMapbox: ConfigPlugin<MapboxPlugProps> = (
RNMapboxMapsVersion,
RNMapboxMapsDownloadToken,
RNMapboxMapsUseV11,
RNMapboxMapsSwiftPackageManager,
});

return config;
Expand Down
Loading
Loading