From 1e468a6e8df4e814c780f290a7a9cc00a93e65a7 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Wed, 27 Nov 2024 17:38:05 +0100 Subject: [PATCH 01/36] feat: add GoogleLocationEngineImpl --- android/build.gradle | 1 + .../rctmln/location/LocationManager.java | 14 +- .../engine/GoogleLocationEngineImpl.java | 151 ++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 android/src/main/java/com/maplibre/rctmln/location/engine/GoogleLocationEngineImpl.java diff --git a/android/build.gradle b/android/build.gradle index 0b4e8cef4..d27cf944e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -56,6 +56,7 @@ dependencies { implementation "org.maplibre.gl:android-sdk-turf:6.0.1" // Dependencies + implementation "com.google.android.gms:play-services-location:21.3.0" implementation "androidx.vectordrawable:vectordrawable:1.1.0" implementation "androidx.annotation:annotation:1.7.0" implementation "androidx.appcompat:appcompat:1.6.1" diff --git a/android/src/main/java/com/maplibre/rctmln/location/LocationManager.java b/android/src/main/java/com/maplibre/rctmln/location/LocationManager.java index bc5c44420..b9723d4a6 100644 --- a/android/src/main/java/com/maplibre/rctmln/location/LocationManager.java +++ b/android/src/main/java/com/maplibre/rctmln/location/LocationManager.java @@ -12,6 +12,11 @@ import com.mapbox.android.core.location.LocationEngineListener; import com.mapbox.android.core.location.LocationEnginePriority; */ +import org.maplibre.android.location.engine.LocationEngineProxy; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; +import com.maplibre.rctmln.location.engine.GoogleLocationEngineImpl; import org.maplibre.android.location.engine.LocationEngineDefault; import org.maplibre.android.location.engine.LocationEngineRequest; @@ -63,7 +68,14 @@ private LocationManager(Context context) { } private void buildEngineRequest() { - locationEngine = LocationEngineDefault.INSTANCE.getDefaultLocationEngine(this.context.getApplicationContext()); + if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS) { + locationEngine = new LocationEngineProxy<>(new GoogleLocationEngineImpl(context.getApplicationContext())); + Log.d(LOG_TAG, "Google Location Engine created successfully."); + } else { + locationEngine = LocationEngineDefault.INSTANCE.getDefaultLocationEngine(this.context.getApplicationContext()); + Log.d(LOG_TAG, "Default Location Engine created successfully."); + } + locationEngineRequest = new LocationEngineRequest.Builder(DEFAULT_INTERVAL_MILLIS) .setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS) .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) diff --git a/android/src/main/java/com/maplibre/rctmln/location/engine/GoogleLocationEngineImpl.java b/android/src/main/java/com/maplibre/rctmln/location/engine/GoogleLocationEngineImpl.java new file mode 100644 index 000000000..55ba2445b --- /dev/null +++ b/android/src/main/java/com/maplibre/rctmln/location/engine/GoogleLocationEngineImpl.java @@ -0,0 +1,151 @@ +package com.maplibre.rctmln.location.engine; + +import android.annotation.SuppressLint; +import android.app.PendingIntent; +import android.content.Context; +import android.location.Location; +import android.os.Looper; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationCallback; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationResult; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.location.Priority; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; + +import org.maplibre.android.location.engine.LocationEngineCallback; +import org.maplibre.android.location.engine.LocationEngineImpl; +import org.maplibre.android.location.engine.LocationEngineRequest; +import org.maplibre.android.location.engine.LocationEngineResult; + +import java.util.Collections; +import java.util.List; + +/** + * Wraps implementation of Fused Location Provider + */ +public class GoogleLocationEngineImpl implements LocationEngineImpl { + private final FusedLocationProviderClient fusedLocationProviderClient; + + @VisibleForTesting + GoogleLocationEngineImpl(FusedLocationProviderClient fusedLocationProviderClient) { + this.fusedLocationProviderClient = fusedLocationProviderClient; + } + + public GoogleLocationEngineImpl(@NonNull Context context) { + this.fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context); + } + + @NonNull + @Override + public LocationCallback createListener(LocationEngineCallback callback) { + return new GoogleLocationEngineCallbackTransport(callback); + } + + @SuppressLint("MissingPermission") + @Override + public void getLastLocation(@NonNull LocationEngineCallback callback) + throws SecurityException { + GoogleLastLocationEngineCallbackTransport transport = + new GoogleLastLocationEngineCallbackTransport(callback); + fusedLocationProviderClient.getLastLocation().addOnSuccessListener(transport).addOnFailureListener(transport); + } + + @SuppressLint("MissingPermission") + @Override + public void requestLocationUpdates(@NonNull LocationEngineRequest request, + @NonNull LocationCallback listener, + @Nullable Looper looper) throws SecurityException { + fusedLocationProviderClient.requestLocationUpdates(toGMSLocationRequest(request), listener, looper); + } + + @SuppressLint("MissingPermission") + @Override + public void requestLocationUpdates(@NonNull LocationEngineRequest request, + @NonNull PendingIntent pendingIntent) throws SecurityException { + fusedLocationProviderClient.requestLocationUpdates(toGMSLocationRequest(request), pendingIntent); + } + + @Override + public void removeLocationUpdates(@NonNull LocationCallback listener) { + if (listener != null) { + fusedLocationProviderClient.removeLocationUpdates(listener); + } + } + + @Override + public void removeLocationUpdates(PendingIntent pendingIntent) { + if (pendingIntent != null) { + fusedLocationProviderClient.removeLocationUpdates(pendingIntent); + } + } + + private static LocationRequest toGMSLocationRequest(LocationEngineRequest request) { + LocationRequest.Builder builder = new LocationRequest.Builder(request.getInterval()); + builder.setMinUpdateIntervalMillis(request.getFastestInterval()); + builder.setMinUpdateDistanceMeters(request.getDisplacement()); + builder.setMaxUpdateDelayMillis(request.getMaxWaitTime()); + builder.setPriority(toGMSLocationPriority(request.getPriority())); + return builder.build(); + } + + private static int toGMSLocationPriority(int enginePriority) { + switch (enginePriority) { + case LocationEngineRequest.PRIORITY_HIGH_ACCURACY: + return Priority.PRIORITY_HIGH_ACCURACY; + case LocationEngineRequest.PRIORITY_BALANCED_POWER_ACCURACY: + return Priority.PRIORITY_BALANCED_POWER_ACCURACY; + case LocationEngineRequest.PRIORITY_LOW_POWER: + return Priority.PRIORITY_LOW_POWER; + case LocationEngineRequest.PRIORITY_NO_POWER: + default: + return Priority.PRIORITY_PASSIVE; + } + } + + private static final class GoogleLocationEngineCallbackTransport extends LocationCallback { + private final LocationEngineCallback callback; + + GoogleLocationEngineCallbackTransport(LocationEngineCallback callback) { + this.callback = callback; + } + + @Override + public void onLocationResult(LocationResult locationResult) { + super.onLocationResult(locationResult); + List locations = locationResult.getLocations(); + if (!locations.isEmpty()) { + callback.onSuccess(LocationEngineResult.create(locations)); + } else { + callback.onFailure(new Exception("Unavailable location")); + } + } + } + + @VisibleForTesting + static final class GoogleLastLocationEngineCallbackTransport + implements OnSuccessListener, OnFailureListener { + private final LocationEngineCallback callback; + + GoogleLastLocationEngineCallbackTransport(LocationEngineCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(Location location) { + callback.onSuccess(location != null ? LocationEngineResult.create(location) : + LocationEngineResult.create(Collections.emptyList())); + } + + @Override + public void onFailure(@NonNull Exception e) { + callback.onFailure(e); + } + } +} \ No newline at end of file From 9132058317e8393bd71e9b5c0b03416c62c00ad6 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Mon, 2 Dec 2024 13:09:32 +0100 Subject: [PATCH 02/36] fix: locatin package names --- .../java/org/maplibre/reactnative/location/LocationManager.java | 2 +- .../reactnative/location/engine/GoogleLocationEngineImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/org/maplibre/reactnative/location/LocationManager.java b/android/src/main/java/org/maplibre/reactnative/location/LocationManager.java index fcac03f6b..291ff2c3f 100644 --- a/android/src/main/java/org/maplibre/reactnative/location/LocationManager.java +++ b/android/src/main/java/org/maplibre/reactnative/location/LocationManager.java @@ -16,7 +16,7 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; -import com.maplibre.rctmln.location.engine.GoogleLocationEngineImpl; +import org.maplibre.reactnative.location.engine.GoogleLocationEngineImpl; import org.maplibre.android.location.engine.LocationEngineDefault; import org.maplibre.android.location.engine.LocationEngineRequest; diff --git a/android/src/main/java/org/maplibre/reactnative/location/engine/GoogleLocationEngineImpl.java b/android/src/main/java/org/maplibre/reactnative/location/engine/GoogleLocationEngineImpl.java index 8742d3a4a..fe446bba2 100644 --- a/android/src/main/java/org/maplibre/reactnative/location/engine/GoogleLocationEngineImpl.java +++ b/android/src/main/java/org/maplibre/reactnative/location/engine/GoogleLocationEngineImpl.java @@ -1,4 +1,4 @@ -package com.maplibre.reactnative.location.engine; +package org.maplibre.reactnative.location.engine; import android.annotation.SuppressLint; import android.app.PendingIntent; From 94adc62bae719e37a29c53308f8d071778ae3e1b Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Sat, 28 Dec 2024 10:31:46 +0100 Subject: [PATCH 03/36] chore: remove debug --- .../src/examples/UserLocation/UserLocationForNavigation.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/examples/src/examples/UserLocation/UserLocationForNavigation.tsx b/packages/examples/src/examples/UserLocation/UserLocationForNavigation.tsx index 54b95de07..f5882b16e 100755 --- a/packages/examples/src/examples/UserLocation/UserLocationForNavigation.tsx +++ b/packages/examples/src/examples/UserLocation/UserLocationForNavigation.tsx @@ -60,10 +60,6 @@ export function UserLocationForNavigation() { followPitch={60} pitch={0} onUserTrackingModeChange={(event) => { - console.log("js userTrackingModeChange"); - console.log("js", event.type); - console.log("js", JSON.stringify(event.nativeEvent)); - if ( navigationActive && !event.nativeEvent.payload.followUserLocation From f6dd68ed7d9a3dc7a7396e97ec9a8b396025ec74 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Mon, 30 Dec 2024 12:30:50 +0100 Subject: [PATCH 04/36] feat: use namespace prefix for gradle properties --- android/build.gradle | 6 +++--- android/gradle.properties | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 99991b60c..555fe89ea 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,6 +1,6 @@ buildscript { // Buildscript is evaluated before everything else so we can't use getExtOrDefault - def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["MapLibreReactNative_kotlinVersion"] + def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["org.maplibre.reactnative.kotlinVersion"] repositories { google() @@ -32,11 +32,11 @@ apply plugin: "kotlin-android" // } def getExtOrDefault(name) { - return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["MapLibreReactNative_" + name] + return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["org.maplibre.reactnative." + name] } def getExtOrIntegerDefault(name) { - return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["MapLibreReactNative_" + name]).toInteger() + return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["org.maplibre.reactnative." + name]).toInteger() } def supportsNamespace() { diff --git a/android/gradle.properties b/android/gradle.properties index 7989274b6..d0eddc380 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,7 +1,9 @@ -MapLibreReactNative_kotlinVersion=1.7.0 -MapLibreReactNative_minSdkVersion=21 -MapLibreReactNative_targetSdkVersion=31 -MapLibreReactNative_compileSdkVersion=31 -MapLibreReactNative_ndkversion=21.4.7075529 +org.maplibre.reactnative.kotlinVersion=1.7.0 +org.maplibre.reactnative.minSdkVersion=21 +org.maplibre.reactnative.targetSdkVersion=31 +org.maplibre.reactnative.compileSdkVersion=31 +org.maplibre.reactnative.ndkVersion=21.4.7075529 -MapLibreReactNative_okhttpVersion=4.9.0 +# MapLibre React Native Customizations + +org.maplibre.reactnative.okhttpVersion=4.9.0 From 6a26674dc32fbbdbbfcfc6399fdbf2aecfd475ae Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Mon, 30 Dec 2024 12:45:21 +0100 Subject: [PATCH 05/36] feat: configure locationEngine via gradle.properties and sourceSets --- android/build.gradle | 23 +++++++++++++-- android/gradle.properties | 3 ++ .../reactnative/location/LocationManager.java | 28 ++++--------------- .../engine/DefaultLocationEngineProvider.java | 18 ++++++++++++ .../engine/LocationEngineProvidable.java | 9 ++++++ .../engine/LocationEngineProvider.java | 12 ++++++++ .../engine/GoogleLocationEngineImpl.java | 0 .../engine/GoogleLocationEngineProvider.java | 24 ++++++++++++++++ .../engine/LocationEngineProvider.java | 12 ++++++++ 9 files changed, 104 insertions(+), 25 deletions(-) create mode 100644 android/src/main/java/org/maplibre/reactnative/location/engine/DefaultLocationEngineProvider.java create mode 100644 android/src/main/java/org/maplibre/reactnative/location/engine/LocationEngineProvidable.java create mode 100644 android/src/main/location-engine-default/org/maplibre/reactnative/location/engine/LocationEngineProvider.java rename android/src/main/{java => location-engine-google}/org/maplibre/reactnative/location/engine/GoogleLocationEngineImpl.java (100%) create mode 100644 android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/GoogleLocationEngineProvider.java create mode 100644 android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/LocationEngineProvider.java diff --git a/android/build.gradle b/android/build.gradle index 555fe89ea..688c18851 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -68,6 +68,20 @@ android { buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() } + sourceSets { + main { + java.srcDirs = ['src/main/java'] + + if (getExtOrDefault("locationEngine") == "default") { + java.srcDirs += 'src/main/location-engine-default' + } else if (getExtOrDefault("locationEngine") == "google") { + java.srcDirs += 'src/main/location-engine-google' + } else { + throw new GradleException("org.maplibre.reactnative.locationEngine.locationEngine should be one of [\"default\", \"google\"]`. \"${getExtOrDefault("locationEngine")}\" was provided.") + } + } + } + buildTypes { release { minifyEnabled false @@ -109,10 +123,13 @@ dependencies { implementation "com.squareup.okhttp3:okhttp:${getExtOrDefault('okhttpVersion')}" implementation "com.squareup.okhttp3:okhttp-urlconnection:${getExtOrDefault('okhttpVersion')}" - implementation "com.google.android.gms:play-services-location:21.3.0" - - // MapLibre plugins + // MapLibre Plugins implementation ("org.maplibre.gl:android-plugin-localization-v9:3.0.1") implementation ("org.maplibre.gl:android-plugin-annotation-v9:3.0.1") implementation ("org.maplibre.gl:android-plugin-markerview-v9:3.0.1") + + // Dependencies for Google Location Engine + if (getExtOrDefault("locationEngine") == "google") { + implementation "com.google.android.gms:play-services-location:21.3.0" + } } diff --git a/android/gradle.properties b/android/gradle.properties index d0eddc380..e172a7db6 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -7,3 +7,6 @@ org.maplibre.reactnative.ndkVersion=21.4.7075529 # MapLibre React Native Customizations org.maplibre.reactnative.okhttpVersion=4.9.0 + +# Available values: default, google +org.maplibre.reactnative.locationEngine=default diff --git a/android/src/main/java/org/maplibre/reactnative/location/LocationManager.java b/android/src/main/java/org/maplibre/reactnative/location/LocationManager.java index 291ff2c3f..79150a2d3 100644 --- a/android/src/main/java/org/maplibre/reactnative/location/LocationManager.java +++ b/android/src/main/java/org/maplibre/reactnative/location/LocationManager.java @@ -7,26 +7,14 @@ import org.maplibre.android.location.engine.LocationEngine; import org.maplibre.android.location.engine.LocationEngineCallback; - -/* -import com.mapbox.android.core.location.LocationEngineListener; -import com.mapbox.android.core.location.LocationEnginePriority; -*/ -import org.maplibre.android.location.engine.LocationEngineProxy; - -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; -import org.maplibre.reactnative.location.engine.GoogleLocationEngineImpl; - -import org.maplibre.android.location.engine.LocationEngineDefault; import org.maplibre.android.location.engine.LocationEngineRequest; import org.maplibre.android.location.engine.LocationEngineResult; import org.maplibre.android.location.permissions.PermissionsManager; +import org.maplibre.reactnative.location.engine.LocationEngineProvider; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; -import java.util.Locale; @SuppressWarnings({"MissingPermission"}) public class LocationManager implements LocationEngineCallback { @@ -63,14 +51,9 @@ private LocationManager(Context context) { this.buildEngineRequest(); } + private void buildEngineRequest() { - if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS) { - locationEngine = new LocationEngineProxy<>(new GoogleLocationEngineImpl(context.getApplicationContext())); - Log.d(LOG_TAG, "Google Location Engine created successfully."); - } else { - locationEngine = LocationEngineDefault.INSTANCE.getDefaultLocationEngine(this.context.getApplicationContext()); - Log.d(LOG_TAG, "Default Location Engine created successfully."); - } + locationEngine = new LocationEngineProvider().getLocationEngine(context); locationEngineRequest = new LocationEngineRequest.Builder(DEFAULT_INTERVAL_MILLIS) .setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS) @@ -90,9 +73,11 @@ public void removeLocationListener(OnUserLocationChange listener) { listeners.remove(listener); } } + public void setMinDisplacement(float minDisplacement) { mMinDisplacement = minDisplacement; } + public void enable() { if (!PermissionsManager.areLocationPermissionsGranted(context)) { return; @@ -146,8 +131,7 @@ public void getLastKnownLocation(LocationEngineCallback ca try { locationEngine.getLastLocation(callback); - } - catch(Exception exception) { + } catch (Exception exception) { Log.w(LOG_TAG, exception); callback.onFailure(exception); } diff --git a/android/src/main/java/org/maplibre/reactnative/location/engine/DefaultLocationEngineProvider.java b/android/src/main/java/org/maplibre/reactnative/location/engine/DefaultLocationEngineProvider.java new file mode 100644 index 000000000..95e1e38e0 --- /dev/null +++ b/android/src/main/java/org/maplibre/reactnative/location/engine/DefaultLocationEngineProvider.java @@ -0,0 +1,18 @@ +package org.maplibre.reactnative.location.engine; + +import android.content.Context; +import android.util.Log; + +import org.maplibre.android.location.engine.LocationEngine; +import org.maplibre.android.location.engine.LocationEngineDefault; + +public class DefaultLocationEngineProvider implements LocationEngineProvidable { + private static final String LOG_TAG = "DefaultLocationEngineProvider"; + + @Override + public LocationEngine getLocationEngine(Context context) { + LocationEngine locationEngine = LocationEngineDefault.INSTANCE.getDefaultLocationEngine(context.getApplicationContext()); + Log.d(LOG_TAG, "DefaultLocationEngine will be used."); + return locationEngine; + } +} \ No newline at end of file diff --git a/android/src/main/java/org/maplibre/reactnative/location/engine/LocationEngineProvidable.java b/android/src/main/java/org/maplibre/reactnative/location/engine/LocationEngineProvidable.java new file mode 100644 index 000000000..a18e04286 --- /dev/null +++ b/android/src/main/java/org/maplibre/reactnative/location/engine/LocationEngineProvidable.java @@ -0,0 +1,9 @@ +package org.maplibre.reactnative.location.engine; + +import android.content.Context; + +import org.maplibre.android.location.engine.LocationEngine; + +public interface LocationEngineProvidable { + LocationEngine getLocationEngine(Context context); +} \ No newline at end of file diff --git a/android/src/main/location-engine-default/org/maplibre/reactnative/location/engine/LocationEngineProvider.java b/android/src/main/location-engine-default/org/maplibre/reactnative/location/engine/LocationEngineProvider.java new file mode 100644 index 000000000..f2ee021a3 --- /dev/null +++ b/android/src/main/location-engine-default/org/maplibre/reactnative/location/engine/LocationEngineProvider.java @@ -0,0 +1,12 @@ +package org.maplibre.reactnative.location.engine; + +import android.content.Context; + +import org.maplibre.android.location.engine.LocationEngine; + +public class LocationEngineProvider implements LocationEngineProvidable { + @Override + public LocationEngine getLocationEngine(Context context) { + return new DefaultLocationEngineProvider().getLocationEngine(context); + } +} diff --git a/android/src/main/java/org/maplibre/reactnative/location/engine/GoogleLocationEngineImpl.java b/android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/GoogleLocationEngineImpl.java similarity index 100% rename from android/src/main/java/org/maplibre/reactnative/location/engine/GoogleLocationEngineImpl.java rename to android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/GoogleLocationEngineImpl.java diff --git a/android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/GoogleLocationEngineProvider.java b/android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/GoogleLocationEngineProvider.java new file mode 100644 index 000000000..778bee51e --- /dev/null +++ b/android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/GoogleLocationEngineProvider.java @@ -0,0 +1,24 @@ +package org.maplibre.reactnative.location.engine; + +import android.content.Context; +import android.util.Log; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; + +import org.maplibre.android.location.engine.LocationEngine; +import org.maplibre.android.location.engine.LocationEngineProxy; + +public class GoogleLocationEngineProvider implements LocationEngineProvidable { + private static final String LOG_TAG = "GoogleLocationEngineProvider"; + + public LocationEngine getLocationEngine(Context context) { + if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS) { + LocationEngine locationEngine = new LocationEngineProxy<>(new GoogleLocationEngineImpl(context.getApplicationContext())); + Log.d(LOG_TAG, "GoogleLocationEngine will be used."); + return locationEngine; + } else { + return new DefaultLocationEngineProvider().getLocationEngine(context); + } + } +} \ No newline at end of file diff --git a/android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/LocationEngineProvider.java b/android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/LocationEngineProvider.java new file mode 100644 index 000000000..1475fca05 --- /dev/null +++ b/android/src/main/location-engine-google/org/maplibre/reactnative/location/engine/LocationEngineProvider.java @@ -0,0 +1,12 @@ +package org.maplibre.reactnative.location.engine; + +import android.content.Context; + +import org.maplibre.android.location.engine.LocationEngine; + +public class LocationEngineProvider implements LocationEngineProvidable { + @Override + public LocationEngine getLocationEngine(Context context) { + return new GoogleLocationEngineProvider().getLocationEngine(context); + } +} From 50f0fcb9024a055c5d274cfbc3bbfa2b73541161 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Mon, 30 Dec 2024 13:28:40 +0100 Subject: [PATCH 06/36] refactor: split out ios plugin code --- src/__tests__/plugin/withMapLibre.test.ts | 8 +- src/plugin/ios.ts | 168 ++++++++++++++++++++ src/plugin/withMapLibre.ts | 177 ++-------------------- 3 files changed, 185 insertions(+), 168 deletions(-) create mode 100644 src/plugin/ios.ts diff --git a/src/__tests__/plugin/withMapLibre.test.ts b/src/__tests__/plugin/withMapLibre.test.ts index a145bed7f..65f4cde63 100644 --- a/src/__tests__/plugin/withMapLibre.test.ts +++ b/src/__tests__/plugin/withMapLibre.test.ts @@ -1,5 +1,5 @@ import * as fixtures from "./__fixtures__/cocoapodFiles"; -import { applyCocoaPodsModifications } from "../../plugin/withMapLibre"; +import { applyCocoaPodsModifications } from "../../plugin/ios"; describe("applyCocoaPodsModifications", () => { it("adds blocks to a react native template podfile", () => { @@ -7,22 +7,26 @@ describe("applyCocoaPodsModifications", () => { applyCocoaPodsModifications(fixtures.reactNativeTemplatePodfile), ).toMatchSnapshot(); }); + it("adds blocks to a expo prebuild template podfile", () => { expect( applyCocoaPodsModifications(fixtures.expoTemplatePodfile), ).toMatchSnapshot(); }); + it("adds blocks to a expo prebuild template podfile with custom modifications", () => { expect( applyCocoaPodsModifications(fixtures.customExpoTemplatePodfile), ).toMatchSnapshot(); }); + it("fails to add blocks to a bare podfile", () => { expect(() => applyCocoaPodsModifications(fixtures.blankTemplatePodfile), ).toThrow("Failed to match"); expect(() => applyCocoaPodsModifications("")).toThrow("Failed to match"); }); + it("does not re add blocks to an applied template podfile", () => { const runOnce = applyCocoaPodsModifications( fixtures.reactNativeTemplatePodfile, @@ -30,6 +34,7 @@ describe("applyCocoaPodsModifications", () => { expect(applyCocoaPodsModifications(runOnce)).toMatch(runOnce); }); + it("works after revisions to blocks", () => { const runOnce = applyCocoaPodsModifications( fixtures.expoTemplateWithRevisions, @@ -37,6 +42,7 @@ describe("applyCocoaPodsModifications", () => { expect(runOnce).toMatchSnapshot(); }); + // A known issue is that the regex won't work if the template // has a pre_install/post_install block commented out, before the `use_react_native` function. it("does not work with revisions to blocks after comments", () => { diff --git a/src/plugin/ios.ts b/src/plugin/ios.ts new file mode 100644 index 000000000..5d6b9c091 --- /dev/null +++ b/src/plugin/ios.ts @@ -0,0 +1,168 @@ +import { + type ConfigPlugin, + withDangerousMod, + withXcodeProject, + type XcodeProject, +} from "@expo/config-plugins"; +import { + mergeContents, + removeGeneratedContents, +} from "@expo/config-plugins/build/utils/generateCode"; +import { promises } from "node:fs"; +import path from "node:path"; + +type InstallerBlockName = "pre" | "post"; + +/** + * Dangerously adds the custom installer hooks to the Podfile. + * TODO: In the future this should be removed in favor of some custom hooks provided by Expo autolinking. + */ +const withCocoaPodsInstallerBlocks: ConfigPlugin = (c) => { + return withDangerousMod(c, [ + "ios", + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + async (config) => { + const file = path.join(config.modRequest.platformProjectRoot, "Podfile"); + + const contents = await promises.readFile(file, "utf8"); + + await promises.writeFile( + file, + applyCocoaPodsModifications(contents), + "utf-8", + ); + + return config; + }, + ]); +}; + +// Only the post-install block is required, the post installer block is +// used for spm (swift package manager) which Expo doesn't currently support. +export function applyCocoaPodsModifications(contents: string): string { + // Ensure installer blocks exist + let src = addInstallerBlock(contents, "post"); + src = addMapLibreInstallerBlock(src, "post"); + + return src; +} + +export function addInstallerBlock( + src: string, + blockName: InstallerBlockName, +): string { + const matchBlock = new RegExp(`${blockName}_install do \\|installer\\|`); + const tag = `${blockName}_installer`; + for (const line of src.split("\n")) { + const contents = line.trim(); + // Ignore comments + if (!contents.startsWith("#")) { + // Prevent adding the block if it exists outside of comments. + if (contents.match(matchBlock)) { + // This helps to still allow revisions, since we enabled the block previously. + // Only continue if the generated block exists... + const modified = removeGeneratedContents(src, tag); + if (!modified) { + return src; + } + } + } + } + + return mergeContents({ + tag, + src, + newSrc: [` ${blockName}_install do |installer|`, " end"].join("\n"), + anchor: /use_react_native/, + // We can't go after the use_react_native block because it might have parameters, causing it to be multi-line (see react-native template). + offset: 0, + comment: "#", + }).contents; +} + +export function addMapLibreInstallerBlock( + src: string, + blockName: InstallerBlockName, +): string { + return mergeContents({ + tag: `@maplibre/maplibre-react-native-${blockName}_installer`, + src, + newSrc: ` $MLRN.${blockName}_install(installer)`, + anchor: new RegExp(`${blockName}_install do \\|installer\\|`), + offset: 1, + comment: "#", + }).contents; +} + +/** + * Exclude building for arm64 on simulator devices in the pbxproj project. + * Without this, production builds targeting simulators will fail. + */ +export function setExcludedArchitectures(project: XcodeProject): XcodeProject { + const configurations = project.pbxXCBuildConfigurationSection(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + for (const { name, buildSettings } of Object.values(configurations || {})) { + // Guessing that this is the best way to emulate Xcode. + // Using `project.addToBuildSettings` modifies too many targets. + if ( + name === "Release" && + typeof buildSettings?.PRODUCT_NAME !== "undefined" + ) { + buildSettings['"EXCLUDED_ARCHS[sdk=iphonesimulator*]"'] = '"arm64"'; + } + } + + return project; +} + +const withoutSignatures: ConfigPlugin = (config) => { + return withXcodeProject(config, async (config) => { + config.modResults.addBuildPhase( + [], + "PBXShellScriptBuildPhase", + "Remove signature files (Xcode workaround)", + null, + { + shellPath: "/bin/sh", + shellScript: ` + echo "Remove signature files (Xcode workaround)"; + rm -rf "$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature"; + `, + }, + ); + + return config; + }); +}; + +/** + * Set the Debug Information Format to DWARF with dSYM File during EAS Build for Managed App + * https://github.com/expo/eas-cli/issues/968 + * // Set artifactPath in eas.json + * "ios": { + * "artifactPath": "ios/build/*" + * } + */ +const withDwarfDsym: ConfigPlugin = (config) => { + return withXcodeProject(config, async (config) => { + config.modResults.debugInformationFormat = "dwarf-with-dsym"; + + return config; + }); +}; + +const withExcludedSimulatorArchitectures: ConfigPlugin = (c) => { + return withXcodeProject(c, (config) => { + config.modResults = setExcludedArchitectures(config.modResults); + + return config; + }); +}; + +export const ios = { + withCocoaPodsInstallerBlocks, + withoutSignatures, + withDwarfDsym, + withExcludedSimulatorArchitectures, +}; diff --git a/src/plugin/withMapLibre.ts b/src/plugin/withMapLibre.ts index 4682ce095..1cd9a1b38 100644 --- a/src/plugin/withMapLibre.ts +++ b/src/plugin/withMapLibre.ts @@ -1,16 +1,6 @@ -import { - type ConfigPlugin, - createRunOncePlugin, - withDangerousMod, - withXcodeProject, - type XcodeProject, -} from "@expo/config-plugins"; -import { - mergeContents, - removeGeneratedContents, -} from "@expo/config-plugins/build/utils/generateCode"; -import { promises } from "node:fs"; -import path from "node:path"; +import { type ConfigPlugin, createRunOncePlugin } from "@expo/config-plugins"; + +import { ios } from "./ios"; let pkg: { name: string; version?: string } = { name: "@maplibre/maplibre-react-native", @@ -21,161 +11,14 @@ try { // empty catch block } -type InstallerBlockName = "pre" | "post"; - -/** - * Dangerously adds the custom installer hooks to the Podfile. - * In the future this should be removed in favor of some custom hooks provided by Expo autolinking. - * - * https://github.com/maplibre/maplibre-react-native/blob/main/docs/guides/setup/iOS.md - */ -const withCocoaPodsInstallerBlocks: ConfigPlugin = (c) => { - return withDangerousMod(c, [ - "ios", - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - async (config) => { - const file = path.join(config.modRequest.platformProjectRoot, "Podfile"); - - const contents = await promises.readFile(file, "utf8"); - - await promises.writeFile( - file, - applyCocoaPodsModifications(contents), - "utf-8", - ); - - return config; - }, - ]); -}; - -// Only the post-install block is required, the post installer block is -// used for spm (swift package manager) which Expo doesn't currently support. -export function applyCocoaPodsModifications(contents: string): string { - // Ensure installer blocks exist - let src = addInstallerBlock(contents, "post"); - src = addMapLibreInstallerBlock(src, "post"); - - return src; -} - -export function addInstallerBlock( - src: string, - blockName: InstallerBlockName, -): string { - const matchBlock = new RegExp(`${blockName}_install do \\|installer\\|`); - const tag = `${blockName}_installer`; - for (const line of src.split("\n")) { - const contents = line.trim(); - // Ignore comments - if (!contents.startsWith("#")) { - // Prevent adding the block if it exists outside of comments. - if (contents.match(matchBlock)) { - // This helps to still allow revisions, since we enabled the block previously. - // Only continue if the generated block exists... - const modified = removeGeneratedContents(src, tag); - if (!modified) { - return src; - } - } - } - } - - return mergeContents({ - tag, - src, - newSrc: [` ${blockName}_install do |installer|`, " end"].join("\n"), - anchor: /use_react_native/, - // We can't go after the use_react_native block because it might have parameters, causing it to be multi-line (see react-native template). - offset: 0, - comment: "#", - }).contents; -} - -export function addMapLibreInstallerBlock( - src: string, - blockName: InstallerBlockName, -): string { - return mergeContents({ - tag: `@maplibre/maplibre-react-native-${blockName}_installer`, - src, - newSrc: ` $MLRN.${blockName}_install(installer)`, - anchor: new RegExp(`${blockName}_install do \\|installer\\|`), - offset: 1, - comment: "#", - }).contents; -} - -/** - * Exclude building for arm64 on simulator devices in the pbxproj project. - * Without this, production builds targeting simulators will fail. - */ -export function setExcludedArchitectures(project: XcodeProject): XcodeProject { - const configurations = project.pbxXCBuildConfigurationSection(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - for (const { name, buildSettings } of Object.values(configurations || {})) { - // Guessing that this is the best way to emulate Xcode. - // Using `project.addToBuildSettings` modifies too many targets. - if ( - name === "Release" && - typeof buildSettings?.PRODUCT_NAME !== "undefined" - ) { - buildSettings['"EXCLUDED_ARCHS[sdk=iphonesimulator*]"'] = '"arm64"'; - } - } - return project; -} - -const withoutSignatures: ConfigPlugin = (config) => { - const shellScript = ` - echo "Remove signature files (Xcode workaround)"; - rm -rf "$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature"; - `; - return withXcodeProject(config, async (config) => { - const xcodeProject = config.modResults; - xcodeProject.addBuildPhase( - [], - "PBXShellScriptBuildPhase", - "Remove signature files (Xcode workaround)", - null, - { - shellPath: "/bin/sh", - shellScript, - }, - ); - return config; - }); -}; - -/** - * Set the Debug Information Format to DWARF with dSYM File during EAS Build for Managed App - * https://github.com/expo/eas-cli/issues/968 - * // Set artifactPath in eas.json - * "ios": { - * "artifactPath": "ios/build/*" - * } - */ -const withDwarfDsym: ConfigPlugin = (config) => { - return withXcodeProject(config, async (config) => { - const xcodeProject = config.modResults; - xcodeProject.debugInformationFormat = "dwarf-with-dsym"; - return config; - }); -}; - -const withExcludedSimulatorArchitectures: ConfigPlugin = (c) => { - return withXcodeProject(c, (config) => { - config.modResults = setExcludedArchitectures(config.modResults); - return config; - }); -}; - const withMapLibre: ConfigPlugin = (config) => { - config = withoutSignatures( - withDwarfDsym(withExcludedSimulatorArchitectures(config)), - ); - return withCocoaPodsInstallerBlocks(config); + // iOS + config = ios.withExcludedSimulatorArchitectures(config); + config = ios.withDwarfDsym(config); + config = ios.withoutSignatures(config); + config = ios.withCocoaPodsInstallerBlocks(config); + + return config; }; export default createRunOncePlugin(withMapLibre, pkg.name, pkg.version); From 44078214afe8dd63252ba73bbac7ed1fbf50b3f9 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Mon, 30 Dec 2024 13:42:29 +0100 Subject: [PATCH 07/36] test: update Podfile fixtures --- src/__tests__/plugin/__fixtures__/Podfile.ts | 263 ++++++++++++++ .../plugin/__fixtures__/cocoapodFiles.ts | 173 ---------- .../__snapshots__/withMapLibre.test.ts.snap | 323 +++++++++++------- src/__tests__/plugin/withMapLibre.test.ts | 24 +- 4 files changed, 467 insertions(+), 316 deletions(-) create mode 100644 src/__tests__/plugin/__fixtures__/Podfile.ts delete mode 100644 src/__tests__/plugin/__fixtures__/cocoapodFiles.ts diff --git a/src/__tests__/plugin/__fixtures__/Podfile.ts b/src/__tests__/plugin/__fixtures__/Podfile.ts new file mode 100644 index 000000000..4404ec3d6 --- /dev/null +++ b/src/__tests__/plugin/__fixtures__/Podfile.ts @@ -0,0 +1,263 @@ +export const reactNativeTemplatePodfile = ` +# Resolve react_native_pods.rb with node to allow for hoisting +require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + +platform :ios, min_ios_version_supported +prepare_react_native_project! + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end + +target 'HelloWorld' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + target 'HelloWorldTests' do + inherit! :complete + # Pods for testing + end + + post_install do |installer| + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) + end +end +`; + +export const expoTemplatePodfile = ` +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! + +target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) + + post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + end +end +`; + +export const customExpoTemplatePodfile = ` +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! + +target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) + + # pre_install do |installer| + # end + + post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + end +end +`; + +// This tests that if an invalid revision is pushed, the plugin can correct it based on the ID. +export const expoTemplateWithRevisions = ` +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! + +target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) + + post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-001 + INVALID_$MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + end +end +`; + +export const blankTemplatePodfile = ` +platform :ios, '12.0' + +target 'HelloWorld' do +end +`; diff --git a/src/__tests__/plugin/__fixtures__/cocoapodFiles.ts b/src/__tests__/plugin/__fixtures__/cocoapodFiles.ts deleted file mode 100644 index d8a31b95b..000000000 --- a/src/__tests__/plugin/__fixtures__/cocoapodFiles.ts +++ /dev/null @@ -1,173 +0,0 @@ -export const reactNativeTemplatePodfile = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - config = use_native_modules! - - use_react_native!( - :path => config[:reactNativePath], - # to enable hermes on iOS, change \`false\` to \`true\` and then install pods - :hermes_enabled => false - ) - - target 'HelloWorldTests' do - inherit! :complete - # Pods for testing - end - - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable the next line. - use_flipper!() - - post_install do |installer| - react_native_post_install(installer) - end -end -`; - -export const expoTemplatePodfile = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - - use_react_native!(:path => config["reactNativePath"]) - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end -end -`; - -export const customExpoTemplatePodfile = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - - use_react_native!(:path => config["reactNativePath"]) - - # pre_install do |installer| - # end - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end -end -`; - -// This tests that if an invalid revision is pushed, the plugin can correct it based on the ID. -export const expoTemplateWithRevisions = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - -# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id -INVALID_pre_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 - INVALID_$MLRN.pre_install(installer) -# @generated end @react-native-mapbox-gl/maps-pre_installer -end -# @generated end pre_installer -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 -INVALID_post_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 - INVALID_$MLRN.post_install(installer) -# @generated end @react-native-mapbox-gl/maps-post_installer -end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - # pre_install do |installer| - # end - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end -end -`; - -export const expoTemplateWithRevisionsAfterComments = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - # pre_install do |installer| - # end - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end - -# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id -INVALID_pre_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 - INVALID_$MLRN.pre_install(installer) -# @generated end @react-native-mapbox-gl/maps-pre_installer -end -# @generated end pre_installer -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 -INVALID_post_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 - INVALID_$MLRN.post_install(installer) -# @generated end @react-native-mapbox-gl/maps-post_installer -end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - -end -`; - -export const blankTemplatePodfile = ` -platform :ios, '12.0' - -target 'HelloWorld' do -end -`; diff --git a/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap b/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap index 84caa9b32..c9721eee8 100644 --- a/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap +++ b/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap @@ -2,87 +2,174 @@ exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile 1`] = ` " -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") -platform :ios, '12.0' +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! target 'HelloWorld' do - use_unimodules! - config = use_native_modules! + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end end " `; exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` " -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} -platform :ios, '12.0' +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! target 'HelloWorld' do - use_unimodules! - config = use_native_modules! + use_expo_modules! -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) # pre_install do |installer| # end - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end + post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + end end " `; exports[`applyCocoaPodsModifications adds blocks to a react native template podfile 1`] = ` " -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' +# Resolve react_native_pods.rb with node to allow for hoisting +require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + +platform :ios, min_ios_version_supported +prepare_react_native_project! -platform :ios, '12.0' +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end target 'HelloWorld' do config = use_native_modules! use_react_native!( :path => config[:reactNativePath], - # to enable hermes on iOS, change \`false\` to \`true\` and then install pods - :hermes_enabled => false + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." ) target 'HelloWorldTests' do @@ -90,105 +177,89 @@ target 'HelloWorld' do # Pods for testing end - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable the next line. - use_flipper!() - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - react_native_post_install(installer) + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) end end " `; -exports[`applyCocoaPodsModifications does not work with revisions to blocks after comments 1`] = ` +exports[`applyCocoaPodsModifications works after revisions to blocks 1`] = ` " -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") -platform :ios, '12.0' +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - # pre_install do |installer| - # end +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - # flipper_post_install(installer) - # end - # end +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false -# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id -INVALID_pre_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 - INVALID_$MLRN.pre_install(installer) -# @generated end @react-native-mapbox-gl/maps-pre_installer -end -# @generated end pre_installer -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 - post_install do |installer| - end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) +prepare_react_native_project! +target 'HelloWorld' do + use_expo_modules! -end -" -`; + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end -exports[`applyCocoaPodsModifications works after revisions to blocks 1`] = ` -" -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + config = use_native_modules!(config_command) -platform :ios, '12.0' + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) -# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id -INVALID_pre_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 - INVALID_$MLRN.pre_install(installer) -# @generated end @react-native-mapbox-gl/maps-pre_installer -end -# @generated end pre_installer -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-001 + INVALID_$MLRN.post_install(installer) # @generated end @maplibre/maplibre-react-native-post_installer - end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - # pre_install do |installer| - # end + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + end end " `; diff --git a/src/__tests__/plugin/withMapLibre.test.ts b/src/__tests__/plugin/withMapLibre.test.ts index 65f4cde63..d3381061e 100644 --- a/src/__tests__/plugin/withMapLibre.test.ts +++ b/src/__tests__/plugin/withMapLibre.test.ts @@ -1,35 +1,35 @@ -import * as fixtures from "./__fixtures__/cocoapodFiles"; +import * as podfileFixtures from "./__fixtures__/Podfile"; import { applyCocoaPodsModifications } from "../../plugin/ios"; describe("applyCocoaPodsModifications", () => { it("adds blocks to a react native template podfile", () => { expect( - applyCocoaPodsModifications(fixtures.reactNativeTemplatePodfile), + applyCocoaPodsModifications(podfileFixtures.reactNativeTemplatePodfile), ).toMatchSnapshot(); }); it("adds blocks to a expo prebuild template podfile", () => { expect( - applyCocoaPodsModifications(fixtures.expoTemplatePodfile), + applyCocoaPodsModifications(podfileFixtures.expoTemplatePodfile), ).toMatchSnapshot(); }); it("adds blocks to a expo prebuild template podfile with custom modifications", () => { expect( - applyCocoaPodsModifications(fixtures.customExpoTemplatePodfile), + applyCocoaPodsModifications(podfileFixtures.customExpoTemplatePodfile), ).toMatchSnapshot(); }); it("fails to add blocks to a bare podfile", () => { expect(() => - applyCocoaPodsModifications(fixtures.blankTemplatePodfile), + applyCocoaPodsModifications(podfileFixtures.blankTemplatePodfile), ).toThrow("Failed to match"); expect(() => applyCocoaPodsModifications("")).toThrow("Failed to match"); }); it("does not re add blocks to an applied template podfile", () => { const runOnce = applyCocoaPodsModifications( - fixtures.reactNativeTemplatePodfile, + podfileFixtures.reactNativeTemplatePodfile, ); expect(applyCocoaPodsModifications(runOnce)).toMatch(runOnce); @@ -37,17 +37,7 @@ describe("applyCocoaPodsModifications", () => { it("works after revisions to blocks", () => { const runOnce = applyCocoaPodsModifications( - fixtures.expoTemplateWithRevisions, - ); - - expect(runOnce).toMatchSnapshot(); - }); - - // A known issue is that the regex won't work if the template - // has a pre_install/post_install block commented out, before the `use_react_native` function. - it("does not work with revisions to blocks after comments", () => { - const runOnce = applyCocoaPodsModifications( - fixtures.expoTemplateWithRevisionsAfterComments, + podfileFixtures.expoTemplateWithRevisions, ); expect(runOnce).toMatchSnapshot(); From ea53e6d8f2d39284e10d42e32eeeb580b4fbd393 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Mon, 30 Dec 2024 14:20:05 +0100 Subject: [PATCH 08/36] test: apply mods to snapshots --- .../plugin/__snapshots__/withMapLibre.test.ts.snap | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap b/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap index c9721eee8..5041ea01b 100644 --- a/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap +++ b/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap @@ -49,6 +49,9 @@ target 'HelloWorld' do ) post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer react_native_post_install( installer, config[:reactNativePath], @@ -123,6 +126,9 @@ target 'HelloWorld' do # end post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer react_native_post_install( installer, config[:reactNativePath], @@ -178,6 +184,9 @@ target 'HelloWorld' do end post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, @@ -239,8 +248,8 @@ target 'HelloWorld' do ) post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-001 - INVALID_$MLRN.post_install(installer) +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) # @generated end @maplibre/maplibre-react-native-post_installer react_native_post_install( installer, From ec8e3044a5621e6be1d34979bacf4eb3902d8edc Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 08:04:58 +0100 Subject: [PATCH 09/36] feat: add android expo plugin for location engine --- packages/expo-app/app.config.ts | 13 +++- .../android/getGradleProperties.test.ts | 41 +++++++++++ .../android/mergeGradleProperties.test.ts | 37 ++++++++++ .../plugin/{ => ios}/__fixtures__/Podfile.ts | 0 .../applyCocoaPodsModifications.test.ts.snap} | 8 +- .../applyCocoaPodsModifications.test.ts} | 4 +- src/plugin/MapLibrePluginProps.ts | 7 ++ src/plugin/android.ts | 73 +++++++++++++++++++ src/plugin/withMapLibre.ts | 7 +- 9 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 src/__tests__/plugin/android/getGradleProperties.test.ts create mode 100644 src/__tests__/plugin/android/mergeGradleProperties.test.ts rename src/__tests__/plugin/{ => ios}/__fixtures__/Podfile.ts (100%) rename src/__tests__/plugin/{__snapshots__/withMapLibre.test.ts.snap => ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap} (95%) rename src/__tests__/plugin/{withMapLibre.test.ts => ios/applyCocoaPodsModifications.test.ts} (90%) create mode 100644 src/plugin/MapLibrePluginProps.ts create mode 100644 src/plugin/android.ts diff --git a/packages/expo-app/app.config.ts b/packages/expo-app/app.config.ts index 79f5b60aa..3d64c349b 100644 --- a/packages/expo-app/app.config.ts +++ b/packages/expo-app/app.config.ts @@ -1,6 +1,8 @@ import "ts-node/register"; import { type ExpoConfig, type ConfigContext } from "expo/config"; +import { MapLibrePluginProps } from "../../src/plugin/MapLibrePluginProps"; + export default ({ config }: ConfigContext): ExpoConfig => ({ ...config, name: "Expo App", @@ -33,5 +35,14 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ backgroundColor: "#285daa", translucent: false, }, - plugins: ["../../src/plugin/withMapLibre.ts"], + plugins: [ + [ + "../../src/plugin/withMapLibre.ts", + { + android: { + locationEngine: "default", + }, + } as MapLibrePluginProps, + ], + ], }); diff --git a/src/__tests__/plugin/android/getGradleProperties.test.ts b/src/__tests__/plugin/android/getGradleProperties.test.ts new file mode 100644 index 000000000..8bb6ec020 --- /dev/null +++ b/src/__tests__/plugin/android/getGradleProperties.test.ts @@ -0,0 +1,41 @@ +import { getGradleProperties } from "../../../plugin/android"; + +describe("Expo Plugin Android – getGradleProperties", () => { + it("removes empty property keys", () => { + const result = getGradleProperties({ + android: { + // @ts-expect-error + "": "default", + }, + }); + + expect(result).toEqual([]); + }); + + it("removes empty property values", () => { + const result = getGradleProperties({ + android: { + // @ts-expect-error + locationEngine: "", + }, + }); + + expect(result).toEqual([]); + }); + + it("adds valid properties", () => { + const result = getGradleProperties({ + android: { + locationEngine: "google", + }, + }); + + expect(result).toEqual([ + { + type: "property", + key: "org.maplibre.reactnative.locationEngine", + value: "google", + }, + ]); + }); +}); diff --git a/src/__tests__/plugin/android/mergeGradleProperties.test.ts b/src/__tests__/plugin/android/mergeGradleProperties.test.ts new file mode 100644 index 000000000..b81cbd0eb --- /dev/null +++ b/src/__tests__/plugin/android/mergeGradleProperties.test.ts @@ -0,0 +1,37 @@ +import { mergeGradleProperties } from "../../../plugin/android"; + +const PROPERTY = { + type: "property", + key: "exampleProperty", + value: "value", +} as const; + +const NEW_PROPERTY = { + type: "property", + key: "newProperty", + value: "new", +} as const; + +describe("Expo Plugin Android – mergeGradleProperties", () => { + it("replaces duplicate property", () => { + expect( + mergeGradleProperties( + [ + PROPERTY, + { + ...NEW_PROPERTY, + value: "old", + }, + ], + [NEW_PROPERTY], + ), + ).toEqual([PROPERTY, NEW_PROPERTY]); + }); + + it("adds new property", () => { + expect(mergeGradleProperties([PROPERTY], [NEW_PROPERTY])).toEqual([ + PROPERTY, + NEW_PROPERTY, + ]); + }); +}); diff --git a/src/__tests__/plugin/__fixtures__/Podfile.ts b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts similarity index 100% rename from src/__tests__/plugin/__fixtures__/Podfile.ts rename to src/__tests__/plugin/ios/__fixtures__/Podfile.ts diff --git a/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap similarity index 95% rename from src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap rename to src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap index 5041ea01b..2e060a06f 100644 --- a/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap +++ b/src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile 1`] = ` +exports[`Expo Plugin iOS – applyCocoaPodsModifications adds blocks to a expo prebuild template podfile 1`] = ` " require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") @@ -74,7 +74,7 @@ end " `; -exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` +exports[`Expo Plugin iOS – applyCocoaPodsModifications adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` " require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") @@ -151,7 +151,7 @@ end " `; -exports[`applyCocoaPodsModifications adds blocks to a react native template podfile 1`] = ` +exports[`Expo Plugin iOS – applyCocoaPodsModifications adds blocks to a react native template podfile 1`] = ` " # Resolve react_native_pods.rb with node to allow for hoisting require Pod::Executable.execute_command('node', ['-p', @@ -199,7 +199,7 @@ end " `; -exports[`applyCocoaPodsModifications works after revisions to blocks 1`] = ` +exports[`Expo Plugin iOS – applyCocoaPodsModifications works after revisions to blocks 1`] = ` " require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") diff --git a/src/__tests__/plugin/withMapLibre.test.ts b/src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts similarity index 90% rename from src/__tests__/plugin/withMapLibre.test.ts rename to src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts index d3381061e..807ff7104 100644 --- a/src/__tests__/plugin/withMapLibre.test.ts +++ b/src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts @@ -1,7 +1,7 @@ import * as podfileFixtures from "./__fixtures__/Podfile"; -import { applyCocoaPodsModifications } from "../../plugin/ios"; +import { applyCocoaPodsModifications } from "../../../plugin/ios"; -describe("applyCocoaPodsModifications", () => { +describe("Expo Plugin iOS – applyCocoaPodsModifications", () => { it("adds blocks to a react native template podfile", () => { expect( applyCocoaPodsModifications(podfileFixtures.reactNativeTemplatePodfile), diff --git a/src/plugin/MapLibrePluginProps.ts b/src/plugin/MapLibrePluginProps.ts new file mode 100644 index 000000000..1d644a635 --- /dev/null +++ b/src/plugin/MapLibrePluginProps.ts @@ -0,0 +1,7 @@ +export type MapLibrePluginProps = + | { + android?: { + locationEngine?: "default" | "google"; + }; + } + | undefined; diff --git a/src/plugin/android.ts b/src/plugin/android.ts new file mode 100644 index 000000000..cec8896f1 --- /dev/null +++ b/src/plugin/android.ts @@ -0,0 +1,73 @@ +import { + type ConfigPlugin, + withGradleProperties as withGradlePropertiesExpo, +} from "@expo/config-plugins"; + +import type { MapLibrePluginProps } from "./MapLibrePluginProps"; + +type PropertyItem = { + type: "property"; + key: string; + value: string; +}; + +type PropertiesItem = + | { + type: "comment"; + value: string; + } + | { + type: "empty"; + } + | PropertyItem; + +export const getGradleProperties = ( + props: MapLibrePluginProps, +): PropertyItem[] => { + return Object.entries(props?.android || {}).reduce( + (properties, [key, value]) => { + if (key && value) { + properties.push({ + type: "property", + key: `org.maplibre.reactnative.${key}`, + value: value.toString(), + }); + } + + return properties; + }, + [] as PropertyItem[], + ); +}; + +export const mergeGradleProperties = ( + oldProperties: PropertiesItem[], + newProperties: PropertyItem[], +): PropertiesItem[] => { + const newPropertiesKeys = newProperties.map(({ key }) => key); + const merged = oldProperties.filter( + (item) => + !(item.type === "property" && newPropertiesKeys.includes(item.key)), + ); + + merged.push(...newProperties); + + return merged; +}; + +export const withGradleProperties: ConfigPlugin = ( + config, + props, +) => { + const gradleProperties = getGradleProperties(props); + + return withGradlePropertiesExpo(config, (c) => { + c.modResults = mergeGradleProperties(c.modResults, gradleProperties); + + return c; + }); +}; + +export const android = { + withGradleProperties, +}; diff --git a/src/plugin/withMapLibre.ts b/src/plugin/withMapLibre.ts index 1cd9a1b38..4d32308cb 100644 --- a/src/plugin/withMapLibre.ts +++ b/src/plugin/withMapLibre.ts @@ -1,5 +1,7 @@ import { type ConfigPlugin, createRunOncePlugin } from "@expo/config-plugins"; +import type { MapLibrePluginProps } from "./MapLibrePluginProps"; +import { android } from "./android"; import { ios } from "./ios"; let pkg: { name: string; version?: string } = { @@ -11,7 +13,10 @@ try { // empty catch block } -const withMapLibre: ConfigPlugin = (config) => { +const withMapLibre: ConfigPlugin = (config, props) => { + // Android + config = android.withGradleProperties(config, props); + // iOS config = ios.withExcludedSimulatorArchitectures(config); config = ios.withDwarfDsym(config); From 10527478fb0e5e69f969ae1f3d8122d7230979ad Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 10:34:10 +0100 Subject: [PATCH 10/36] fix: use PropertiesItem from @expo/config-plugins --- src/plugin/android.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/plugin/android.ts b/src/plugin/android.ts index cec8896f1..5e858b911 100644 --- a/src/plugin/android.ts +++ b/src/plugin/android.ts @@ -2,6 +2,7 @@ import { type ConfigPlugin, withGradleProperties as withGradlePropertiesExpo, } from "@expo/config-plugins"; +import type { PropertiesItem } from "@expo/config-plugins/build/android/Properties"; import type { MapLibrePluginProps } from "./MapLibrePluginProps"; @@ -11,16 +12,6 @@ type PropertyItem = { value: string; }; -type PropertiesItem = - | { - type: "comment"; - value: string; - } - | { - type: "empty"; - } - | PropertyItem; - export const getGradleProperties = ( props: MapLibrePluginProps, ): PropertyItem[] => { From 2890d72a82d0961f98fc76ea07b8d90687c233f4 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 11:12:10 +0100 Subject: [PATCH 11/36] style: fix type import --- packages/expo-app/app.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo-app/app.config.ts b/packages/expo-app/app.config.ts index 3d64c349b..47c9429a0 100644 --- a/packages/expo-app/app.config.ts +++ b/packages/expo-app/app.config.ts @@ -1,7 +1,7 @@ import "ts-node/register"; import { type ExpoConfig, type ConfigContext } from "expo/config"; -import { MapLibrePluginProps } from "../../src/plugin/MapLibrePluginProps"; +import { type MapLibrePluginProps } from "../../src/plugin/MapLibrePluginProps"; export default ({ config }: ConfigContext): ExpoConfig => ({ ...config, From bd6b68a46bc20f69fa85e2842c0c1f58584690fd Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 11:11:34 +0100 Subject: [PATCH 12/36] test: improve ios fixtures # Conflicts: # src/__tests__/plugin/ios/__fixtures__/Podfile.ts # src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts --- .../plugin/ios/__fixtures__/Podfile.ts | 154 ++---------------- .../applyCocoaPodsModifications.test.ts.snap | 4 +- .../ios/applyCocoaPodsModifications.test.ts | 4 +- 3 files changed, 16 insertions(+), 146 deletions(-) diff --git a/src/__tests__/plugin/ios/__fixtures__/Podfile.ts b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts index 4404ec3d6..e60b1bc55 100644 --- a/src/__tests__/plugin/ios/__fixtures__/Podfile.ts +++ b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts @@ -110,150 +110,20 @@ target 'HelloWorld' do end `; -export const customExpoTemplatePodfile = ` -require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") -require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} - -ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' -install! 'cocoapods', - :deterministic_uuids => false - -prepare_react_native_project! - -target 'HelloWorld' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - # pre_install do |installer| - # end - - post_install do |installer| - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', - ) - - # This is necessary for Xcode 14, because it signs resource bundles by default - # when building for devices. - installer.target_installation_results.pod_target_installation_results - .each do |pod_name, target_installation_result| - target_installation_result.resource_bundle_targets.each do |resource_bundle_target| - resource_bundle_target.build_configurations.each do |config| - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end - end -end -`; - -// This tests that if an invalid revision is pushed, the plugin can correct it based on the ID. -export const expoTemplateWithRevisions = ` -require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") -require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} - -ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' -install! 'cocoapods', - :deterministic_uuids => false - -prepare_react_native_project! - -target 'HelloWorld' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - post_install do |installer| +export const expoTemplatePodfileCustomized = expoTemplatePodfile.replace( + "post_install do |installer|", + `post_install do |installer| + # Some possible customization`, +); + +// This tests that if an invalid revision is pushed, the plugin can correct it based on the ID +export const expoTemplateWithRevisions = expoTemplatePodfile.replace( + "post_install do |installer|", + `post_install do |installer| # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-001 INVALID_$MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', - ) - - # This is necessary for Xcode 14, because it signs resource bundles by default - # when building for devices. - installer.target_installation_results.pod_target_installation_results - .each do |pod_name, target_installation_result| - target_installation_result.resource_bundle_targets.each do |resource_bundle_target| - resource_bundle_target.build_configurations.each do |config| - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end - end -end -`; +# @generated end @maplibre/maplibre-react-native-post_installer`, +); export const blankTemplatePodfile = ` platform :ios, '12.0' diff --git a/src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap index 2e060a06f..5fb534b94 100644 --- a/src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap +++ b/src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap @@ -122,13 +122,11 @@ target 'HelloWorld' do :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', ) - # pre_install do |installer| - # end - post_install do |installer| # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 $MLRN.post_install(installer) # @generated end @maplibre/maplibre-react-native-post_installer + # Some possible customization react_native_post_install( installer, config[:reactNativePath], diff --git a/src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts b/src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts index 807ff7104..22b784b73 100644 --- a/src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts +++ b/src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts @@ -16,7 +16,9 @@ describe("Expo Plugin iOS – applyCocoaPodsModifications", () => { it("adds blocks to a expo prebuild template podfile with custom modifications", () => { expect( - applyCocoaPodsModifications(podfileFixtures.customExpoTemplatePodfile), + applyCocoaPodsModifications( + podfileFixtures.expoTemplatePodfileCustomized, + ), ).toMatchSnapshot(); }); From 92ac8447736e7dd1991464fe6afd5866964c145e Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 11:31:49 +0100 Subject: [PATCH 13/36] feat: revert plugin --- .../plugin/__fixtures__/cocoapodFiles.ts | 173 +++++++++++ .../__snapshots__/withMapLibre.test.ts.snap | 194 +++++++++++++ .../android/getGradleProperties.test.ts | 41 --- .../android/mergeGradleProperties.test.ts | 37 --- .../plugin/ios/__fixtures__/Podfile.ts | 133 --------- .../applyCocoaPodsModifications.test.ts.snap | 272 ------------------ .../ios/applyCocoaPodsModifications.test.ts | 47 --- src/__tests__/plugin/withMapLibre.test.ts | 49 ++++ src/plugin/withMapLibre.ts | 180 +++++++++++- 9 files changed, 582 insertions(+), 544 deletions(-) create mode 100644 src/__tests__/plugin/__fixtures__/cocoapodFiles.ts create mode 100644 src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap delete mode 100644 src/__tests__/plugin/android/getGradleProperties.test.ts delete mode 100644 src/__tests__/plugin/android/mergeGradleProperties.test.ts delete mode 100644 src/__tests__/plugin/ios/__fixtures__/Podfile.ts delete mode 100644 src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap delete mode 100644 src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts create mode 100644 src/__tests__/plugin/withMapLibre.test.ts diff --git a/src/__tests__/plugin/__fixtures__/cocoapodFiles.ts b/src/__tests__/plugin/__fixtures__/cocoapodFiles.ts new file mode 100644 index 000000000..d8a31b95b --- /dev/null +++ b/src/__tests__/plugin/__fixtures__/cocoapodFiles.ts @@ -0,0 +1,173 @@ +export const reactNativeTemplatePodfile = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + # to enable hermes on iOS, change \`false\` to \`true\` and then install pods + :hermes_enabled => false + ) + + target 'HelloWorldTests' do + inherit! :complete + # Pods for testing + end + + # Enables Flipper. + # + # Note that if you have use_frameworks! enabled, Flipper will not work and + # you should disable the next line. + use_flipper!() + + post_install do |installer| + react_native_post_install(installer) + end +end +`; + +export const expoTemplatePodfile = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + + use_react_native!(:path => config["reactNativePath"]) + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +`; + +export const customExpoTemplatePodfile = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + + use_react_native!(:path => config["reactNativePath"]) + + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +`; + +// This tests that if an invalid revision is pushed, the plugin can correct it based on the ID. +export const expoTemplateWithRevisions = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id +INVALID_pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 + INVALID_$MLRN.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer +end +# @generated end pre_installer +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 +INVALID_post_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 + INVALID_$MLRN.post_install(installer) +# @generated end @react-native-mapbox-gl/maps-post_installer +end +# @generated end post_installer + use_react_native!(:path => config["reactNativePath"]) + + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +`; + +export const expoTemplateWithRevisionsAfterComments = ` +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end + +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id +INVALID_pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 + INVALID_$MLRN.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer +end +# @generated end pre_installer +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 +INVALID_post_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 + INVALID_$MLRN.post_install(installer) +# @generated end @react-native-mapbox-gl/maps-post_installer +end +# @generated end post_installer + use_react_native!(:path => config["reactNativePath"]) + + +end +`; + +export const blankTemplatePodfile = ` +platform :ios, '12.0' + +target 'HelloWorld' do +end +`; diff --git a/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap b/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap new file mode 100644 index 000000000..84caa9b32 --- /dev/null +++ b/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap @@ -0,0 +1,194 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 + post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + end +# @generated end post_installer + use_react_native!(:path => config["reactNativePath"]) + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +" +`; + +exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 + post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + end +# @generated end post_installer + use_react_native!(:path => config["reactNativePath"]) + + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +" +`; + +exports[`applyCocoaPodsModifications adds blocks to a react native template podfile 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + # to enable hermes on iOS, change \`false\` to \`true\` and then install pods + :hermes_enabled => false + ) + + target 'HelloWorldTests' do + inherit! :complete + # Pods for testing + end + + # Enables Flipper. + # + # Note that if you have use_frameworks! enabled, Flipper will not work and + # you should disable the next line. + use_flipper!() + + post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + react_native_post_install(installer) + end +end +" +`; + +exports[`applyCocoaPodsModifications does not work with revisions to blocks after comments 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + # flipper_post_install(installer) + # end + # end + +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id +INVALID_pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 + INVALID_$MLRN.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer +end +# @generated end pre_installer +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 + post_install do |installer| + end +# @generated end post_installer + use_react_native!(:path => config["reactNativePath"]) + + +end +" +`; + +exports[`applyCocoaPodsModifications works after revisions to blocks 1`] = ` +" +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/react-native-unimodules/cocoapods.rb' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '12.0' + +target 'HelloWorld' do + use_unimodules! + config = use_native_modules! + +# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id +INVALID_pre_install do |installer| +# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 + INVALID_$MLRN.pre_install(installer) +# @generated end @react-native-mapbox-gl/maps-pre_installer +end +# @generated end pre_installer +# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 + post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + end +# @generated end post_installer + use_react_native!(:path => config["reactNativePath"]) + + # pre_install do |installer| + # end + + # Uncomment to opt-in to using Flipper + # + # if !ENV['CI'] + # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') + # post_install do |installer| + # flipper_post_install(installer) + # end + # end +end +" +`; diff --git a/src/__tests__/plugin/android/getGradleProperties.test.ts b/src/__tests__/plugin/android/getGradleProperties.test.ts deleted file mode 100644 index 8bb6ec020..000000000 --- a/src/__tests__/plugin/android/getGradleProperties.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { getGradleProperties } from "../../../plugin/android"; - -describe("Expo Plugin Android – getGradleProperties", () => { - it("removes empty property keys", () => { - const result = getGradleProperties({ - android: { - // @ts-expect-error - "": "default", - }, - }); - - expect(result).toEqual([]); - }); - - it("removes empty property values", () => { - const result = getGradleProperties({ - android: { - // @ts-expect-error - locationEngine: "", - }, - }); - - expect(result).toEqual([]); - }); - - it("adds valid properties", () => { - const result = getGradleProperties({ - android: { - locationEngine: "google", - }, - }); - - expect(result).toEqual([ - { - type: "property", - key: "org.maplibre.reactnative.locationEngine", - value: "google", - }, - ]); - }); -}); diff --git a/src/__tests__/plugin/android/mergeGradleProperties.test.ts b/src/__tests__/plugin/android/mergeGradleProperties.test.ts deleted file mode 100644 index b81cbd0eb..000000000 --- a/src/__tests__/plugin/android/mergeGradleProperties.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { mergeGradleProperties } from "../../../plugin/android"; - -const PROPERTY = { - type: "property", - key: "exampleProperty", - value: "value", -} as const; - -const NEW_PROPERTY = { - type: "property", - key: "newProperty", - value: "new", -} as const; - -describe("Expo Plugin Android – mergeGradleProperties", () => { - it("replaces duplicate property", () => { - expect( - mergeGradleProperties( - [ - PROPERTY, - { - ...NEW_PROPERTY, - value: "old", - }, - ], - [NEW_PROPERTY], - ), - ).toEqual([PROPERTY, NEW_PROPERTY]); - }); - - it("adds new property", () => { - expect(mergeGradleProperties([PROPERTY], [NEW_PROPERTY])).toEqual([ - PROPERTY, - NEW_PROPERTY, - ]); - }); -}); diff --git a/src/__tests__/plugin/ios/__fixtures__/Podfile.ts b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts deleted file mode 100644 index e60b1bc55..000000000 --- a/src/__tests__/plugin/ios/__fixtures__/Podfile.ts +++ /dev/null @@ -1,133 +0,0 @@ -export const reactNativeTemplatePodfile = ` -# Resolve react_native_pods.rb with node to allow for hoisting -require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip - -platform :ios, min_ios_version_supported -prepare_react_native_project! - -linkage = ENV['USE_FRAMEWORKS'] -if linkage != nil - Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green - use_frameworks! :linkage => linkage.to_sym -end - -target 'HelloWorld' do - config = use_native_modules! - - use_react_native!( - :path => config[:reactNativePath], - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - - target 'HelloWorldTests' do - inherit! :complete - # Pods for testing - end - - post_install do |installer| - # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - # :ccache_enabled => true - ) - end -end -`; - -export const expoTemplatePodfile = ` -require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") -require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} - -ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' -install! 'cocoapods', - :deterministic_uuids => false - -prepare_react_native_project! - -target 'HelloWorld' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - post_install do |installer| - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', - ) - - # This is necessary for Xcode 14, because it signs resource bundles by default - # when building for devices. - installer.target_installation_results.pod_target_installation_results - .each do |pod_name, target_installation_result| - target_installation_result.resource_bundle_targets.each do |resource_bundle_target| - resource_bundle_target.build_configurations.each do |config| - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end - end -end -`; - -export const expoTemplatePodfileCustomized = expoTemplatePodfile.replace( - "post_install do |installer|", - `post_install do |installer| - # Some possible customization`, -); - -// This tests that if an invalid revision is pushed, the plugin can correct it based on the ID -export const expoTemplateWithRevisions = expoTemplatePodfile.replace( - "post_install do |installer|", - `post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-001 - INVALID_$MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer`, -); - -export const blankTemplatePodfile = ` -platform :ios, '12.0' - -target 'HelloWorld' do -end -`; diff --git a/src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap deleted file mode 100644 index 5fb534b94..000000000 --- a/src/__tests__/plugin/ios/__snapshots__/applyCocoaPodsModifications.test.ts.snap +++ /dev/null @@ -1,272 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Expo Plugin iOS – applyCocoaPodsModifications adds blocks to a expo prebuild template podfile 1`] = ` -" -require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") -require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} - -ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' -install! 'cocoapods', - :deterministic_uuids => false - -prepare_react_native_project! - -target 'HelloWorld' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', - ) - - # This is necessary for Xcode 14, because it signs resource bundles by default - # when building for devices. - installer.target_installation_results.pod_target_installation_results - .each do |pod_name, target_installation_result| - target_installation_result.resource_bundle_targets.each do |resource_bundle_target| - resource_bundle_target.build_configurations.each do |config| - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end - end -end -" -`; - -exports[`Expo Plugin iOS – applyCocoaPodsModifications adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` -" -require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") -require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} - -ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' -install! 'cocoapods', - :deterministic_uuids => false - -prepare_react_native_project! - -target 'HelloWorld' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - # Some possible customization - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', - ) - - # This is necessary for Xcode 14, because it signs resource bundles by default - # when building for devices. - installer.target_installation_results.pod_target_installation_results - .each do |pod_name, target_installation_result| - target_installation_result.resource_bundle_targets.each do |resource_bundle_target| - resource_bundle_target.build_configurations.each do |config| - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end - end -end -" -`; - -exports[`Expo Plugin iOS – applyCocoaPodsModifications adds blocks to a react native template podfile 1`] = ` -" -# Resolve react_native_pods.rb with node to allow for hoisting -require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip - -platform :ios, min_ios_version_supported -prepare_react_native_project! - -linkage = ENV['USE_FRAMEWORKS'] -if linkage != nil - Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green - use_frameworks! :linkage => linkage.to_sym -end - -target 'HelloWorld' do - config = use_native_modules! - - use_react_native!( - :path => config[:reactNativePath], - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - - target 'HelloWorldTests' do - inherit! :complete - # Pods for testing - end - - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - # :ccache_enabled => true - ) - end -end -" -`; - -exports[`Expo Plugin iOS – applyCocoaPodsModifications works after revisions to blocks 1`] = ` -" -require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") -require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} - -ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' -install! 'cocoapods', - :deterministic_uuids => false - -prepare_react_native_project! - -target 'HelloWorld' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', - ) - - # This is necessary for Xcode 14, because it signs resource bundles by default - # when building for devices. - installer.target_installation_results.pod_target_installation_results - .each do |pod_name, target_installation_result| - target_installation_result.resource_bundle_targets.each do |resource_bundle_target| - resource_bundle_target.build_configurations.each do |config| - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end - end -end -" -`; diff --git a/src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts b/src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts deleted file mode 100644 index 22b784b73..000000000 --- a/src/__tests__/plugin/ios/applyCocoaPodsModifications.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as podfileFixtures from "./__fixtures__/Podfile"; -import { applyCocoaPodsModifications } from "../../../plugin/ios"; - -describe("Expo Plugin iOS – applyCocoaPodsModifications", () => { - it("adds blocks to a react native template podfile", () => { - expect( - applyCocoaPodsModifications(podfileFixtures.reactNativeTemplatePodfile), - ).toMatchSnapshot(); - }); - - it("adds blocks to a expo prebuild template podfile", () => { - expect( - applyCocoaPodsModifications(podfileFixtures.expoTemplatePodfile), - ).toMatchSnapshot(); - }); - - it("adds blocks to a expo prebuild template podfile with custom modifications", () => { - expect( - applyCocoaPodsModifications( - podfileFixtures.expoTemplatePodfileCustomized, - ), - ).toMatchSnapshot(); - }); - - it("fails to add blocks to a bare podfile", () => { - expect(() => - applyCocoaPodsModifications(podfileFixtures.blankTemplatePodfile), - ).toThrow("Failed to match"); - expect(() => applyCocoaPodsModifications("")).toThrow("Failed to match"); - }); - - it("does not re add blocks to an applied template podfile", () => { - const runOnce = applyCocoaPodsModifications( - podfileFixtures.reactNativeTemplatePodfile, - ); - - expect(applyCocoaPodsModifications(runOnce)).toMatch(runOnce); - }); - - it("works after revisions to blocks", () => { - const runOnce = applyCocoaPodsModifications( - podfileFixtures.expoTemplateWithRevisions, - ); - - expect(runOnce).toMatchSnapshot(); - }); -}); diff --git a/src/__tests__/plugin/withMapLibre.test.ts b/src/__tests__/plugin/withMapLibre.test.ts new file mode 100644 index 000000000..a145bed7f --- /dev/null +++ b/src/__tests__/plugin/withMapLibre.test.ts @@ -0,0 +1,49 @@ +import * as fixtures from "./__fixtures__/cocoapodFiles"; +import { applyCocoaPodsModifications } from "../../plugin/withMapLibre"; + +describe("applyCocoaPodsModifications", () => { + it("adds blocks to a react native template podfile", () => { + expect( + applyCocoaPodsModifications(fixtures.reactNativeTemplatePodfile), + ).toMatchSnapshot(); + }); + it("adds blocks to a expo prebuild template podfile", () => { + expect( + applyCocoaPodsModifications(fixtures.expoTemplatePodfile), + ).toMatchSnapshot(); + }); + it("adds blocks to a expo prebuild template podfile with custom modifications", () => { + expect( + applyCocoaPodsModifications(fixtures.customExpoTemplatePodfile), + ).toMatchSnapshot(); + }); + it("fails to add blocks to a bare podfile", () => { + expect(() => + applyCocoaPodsModifications(fixtures.blankTemplatePodfile), + ).toThrow("Failed to match"); + expect(() => applyCocoaPodsModifications("")).toThrow("Failed to match"); + }); + it("does not re add blocks to an applied template podfile", () => { + const runOnce = applyCocoaPodsModifications( + fixtures.reactNativeTemplatePodfile, + ); + + expect(applyCocoaPodsModifications(runOnce)).toMatch(runOnce); + }); + it("works after revisions to blocks", () => { + const runOnce = applyCocoaPodsModifications( + fixtures.expoTemplateWithRevisions, + ); + + expect(runOnce).toMatchSnapshot(); + }); + // A known issue is that the regex won't work if the template + // has a pre_install/post_install block commented out, before the `use_react_native` function. + it("does not work with revisions to blocks after comments", () => { + const runOnce = applyCocoaPodsModifications( + fixtures.expoTemplateWithRevisionsAfterComments, + ); + + expect(runOnce).toMatchSnapshot(); + }); +}); diff --git a/src/plugin/withMapLibre.ts b/src/plugin/withMapLibre.ts index 4d32308cb..4682ce095 100644 --- a/src/plugin/withMapLibre.ts +++ b/src/plugin/withMapLibre.ts @@ -1,8 +1,16 @@ -import { type ConfigPlugin, createRunOncePlugin } from "@expo/config-plugins"; - -import type { MapLibrePluginProps } from "./MapLibrePluginProps"; -import { android } from "./android"; -import { ios } from "./ios"; +import { + type ConfigPlugin, + createRunOncePlugin, + withDangerousMod, + withXcodeProject, + type XcodeProject, +} from "@expo/config-plugins"; +import { + mergeContents, + removeGeneratedContents, +} from "@expo/config-plugins/build/utils/generateCode"; +import { promises } from "node:fs"; +import path from "node:path"; let pkg: { name: string; version?: string } = { name: "@maplibre/maplibre-react-native", @@ -13,17 +21,161 @@ try { // empty catch block } -const withMapLibre: ConfigPlugin = (config, props) => { - // Android - config = android.withGradleProperties(config, props); +type InstallerBlockName = "pre" | "post"; + +/** + * Dangerously adds the custom installer hooks to the Podfile. + * In the future this should be removed in favor of some custom hooks provided by Expo autolinking. + * + * https://github.com/maplibre/maplibre-react-native/blob/main/docs/guides/setup/iOS.md + */ +const withCocoaPodsInstallerBlocks: ConfigPlugin = (c) => { + return withDangerousMod(c, [ + "ios", + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + async (config) => { + const file = path.join(config.modRequest.platformProjectRoot, "Podfile"); + + const contents = await promises.readFile(file, "utf8"); + + await promises.writeFile( + file, + applyCocoaPodsModifications(contents), + "utf-8", + ); + + return config; + }, + ]); +}; - // iOS - config = ios.withExcludedSimulatorArchitectures(config); - config = ios.withDwarfDsym(config); - config = ios.withoutSignatures(config); - config = ios.withCocoaPodsInstallerBlocks(config); +// Only the post-install block is required, the post installer block is +// used for spm (swift package manager) which Expo doesn't currently support. +export function applyCocoaPodsModifications(contents: string): string { + // Ensure installer blocks exist + let src = addInstallerBlock(contents, "post"); + src = addMapLibreInstallerBlock(src, "post"); + + return src; +} + +export function addInstallerBlock( + src: string, + blockName: InstallerBlockName, +): string { + const matchBlock = new RegExp(`${blockName}_install do \\|installer\\|`); + const tag = `${blockName}_installer`; + for (const line of src.split("\n")) { + const contents = line.trim(); + // Ignore comments + if (!contents.startsWith("#")) { + // Prevent adding the block if it exists outside of comments. + if (contents.match(matchBlock)) { + // This helps to still allow revisions, since we enabled the block previously. + // Only continue if the generated block exists... + const modified = removeGeneratedContents(src, tag); + if (!modified) { + return src; + } + } + } + } + + return mergeContents({ + tag, + src, + newSrc: [` ${blockName}_install do |installer|`, " end"].join("\n"), + anchor: /use_react_native/, + // We can't go after the use_react_native block because it might have parameters, causing it to be multi-line (see react-native template). + offset: 0, + comment: "#", + }).contents; +} + +export function addMapLibreInstallerBlock( + src: string, + blockName: InstallerBlockName, +): string { + return mergeContents({ + tag: `@maplibre/maplibre-react-native-${blockName}_installer`, + src, + newSrc: ` $MLRN.${blockName}_install(installer)`, + anchor: new RegExp(`${blockName}_install do \\|installer\\|`), + offset: 1, + comment: "#", + }).contents; +} + +/** + * Exclude building for arm64 on simulator devices in the pbxproj project. + * Without this, production builds targeting simulators will fail. + */ +export function setExcludedArchitectures(project: XcodeProject): XcodeProject { + const configurations = project.pbxXCBuildConfigurationSection(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + for (const { name, buildSettings } of Object.values(configurations || {})) { + // Guessing that this is the best way to emulate Xcode. + // Using `project.addToBuildSettings` modifies too many targets. + if ( + name === "Release" && + typeof buildSettings?.PRODUCT_NAME !== "undefined" + ) { + buildSettings['"EXCLUDED_ARCHS[sdk=iphonesimulator*]"'] = '"arm64"'; + } + } + return project; +} + +const withoutSignatures: ConfigPlugin = (config) => { + const shellScript = ` + echo "Remove signature files (Xcode workaround)"; + rm -rf "$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature"; + `; + return withXcodeProject(config, async (config) => { + const xcodeProject = config.modResults; + xcodeProject.addBuildPhase( + [], + "PBXShellScriptBuildPhase", + "Remove signature files (Xcode workaround)", + null, + { + shellPath: "/bin/sh", + shellScript, + }, + ); + return config; + }); +}; + +/** + * Set the Debug Information Format to DWARF with dSYM File during EAS Build for Managed App + * https://github.com/expo/eas-cli/issues/968 + * // Set artifactPath in eas.json + * "ios": { + * "artifactPath": "ios/build/*" + * } + */ +const withDwarfDsym: ConfigPlugin = (config) => { + return withXcodeProject(config, async (config) => { + const xcodeProject = config.modResults; + xcodeProject.debugInformationFormat = "dwarf-with-dsym"; + return config; + }); +}; + +const withExcludedSimulatorArchitectures: ConfigPlugin = (c) => { + return withXcodeProject(c, (config) => { + config.modResults = setExcludedArchitectures(config.modResults); + return config; + }); +}; - return config; +const withMapLibre: ConfigPlugin = (config) => { + config = withoutSignatures( + withDwarfDsym(withExcludedSimulatorArchitectures(config)), + ); + return withCocoaPodsInstallerBlocks(config); }; export default createRunOncePlugin(withMapLibre, pkg.name, pkg.version); From cd79948e8c1dee6ac523d1b28c8af3422feed5a9 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 11:34:10 +0100 Subject: [PATCH 14/36] feat: remove all new plugin files --- packages/expo-app/app.config.ts | 11 +- src/plugin/MapLibrePluginProps.ts | 7 -- src/plugin/android.ts | 64 ------------ src/plugin/ios.ts | 168 ------------------------------ 4 files changed, 1 insertion(+), 249 deletions(-) delete mode 100644 src/plugin/MapLibrePluginProps.ts delete mode 100644 src/plugin/android.ts delete mode 100644 src/plugin/ios.ts diff --git a/packages/expo-app/app.config.ts b/packages/expo-app/app.config.ts index 47c9429a0..3cabe21bc 100644 --- a/packages/expo-app/app.config.ts +++ b/packages/expo-app/app.config.ts @@ -35,14 +35,5 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ backgroundColor: "#285daa", translucent: false, }, - plugins: [ - [ - "../../src/plugin/withMapLibre.ts", - { - android: { - locationEngine: "default", - }, - } as MapLibrePluginProps, - ], - ], + plugins: ["../../src/plugin/withMapLibre.ts"], }); diff --git a/src/plugin/MapLibrePluginProps.ts b/src/plugin/MapLibrePluginProps.ts deleted file mode 100644 index 1d644a635..000000000 --- a/src/plugin/MapLibrePluginProps.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type MapLibrePluginProps = - | { - android?: { - locationEngine?: "default" | "google"; - }; - } - | undefined; diff --git a/src/plugin/android.ts b/src/plugin/android.ts deleted file mode 100644 index 5e858b911..000000000 --- a/src/plugin/android.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { - type ConfigPlugin, - withGradleProperties as withGradlePropertiesExpo, -} from "@expo/config-plugins"; -import type { PropertiesItem } from "@expo/config-plugins/build/android/Properties"; - -import type { MapLibrePluginProps } from "./MapLibrePluginProps"; - -type PropertyItem = { - type: "property"; - key: string; - value: string; -}; - -export const getGradleProperties = ( - props: MapLibrePluginProps, -): PropertyItem[] => { - return Object.entries(props?.android || {}).reduce( - (properties, [key, value]) => { - if (key && value) { - properties.push({ - type: "property", - key: `org.maplibre.reactnative.${key}`, - value: value.toString(), - }); - } - - return properties; - }, - [] as PropertyItem[], - ); -}; - -export const mergeGradleProperties = ( - oldProperties: PropertiesItem[], - newProperties: PropertyItem[], -): PropertiesItem[] => { - const newPropertiesKeys = newProperties.map(({ key }) => key); - const merged = oldProperties.filter( - (item) => - !(item.type === "property" && newPropertiesKeys.includes(item.key)), - ); - - merged.push(...newProperties); - - return merged; -}; - -export const withGradleProperties: ConfigPlugin = ( - config, - props, -) => { - const gradleProperties = getGradleProperties(props); - - return withGradlePropertiesExpo(config, (c) => { - c.modResults = mergeGradleProperties(c.modResults, gradleProperties); - - return c; - }); -}; - -export const android = { - withGradleProperties, -}; diff --git a/src/plugin/ios.ts b/src/plugin/ios.ts deleted file mode 100644 index 5d6b9c091..000000000 --- a/src/plugin/ios.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { - type ConfigPlugin, - withDangerousMod, - withXcodeProject, - type XcodeProject, -} from "@expo/config-plugins"; -import { - mergeContents, - removeGeneratedContents, -} from "@expo/config-plugins/build/utils/generateCode"; -import { promises } from "node:fs"; -import path from "node:path"; - -type InstallerBlockName = "pre" | "post"; - -/** - * Dangerously adds the custom installer hooks to the Podfile. - * TODO: In the future this should be removed in favor of some custom hooks provided by Expo autolinking. - */ -const withCocoaPodsInstallerBlocks: ConfigPlugin = (c) => { - return withDangerousMod(c, [ - "ios", - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - async (config) => { - const file = path.join(config.modRequest.platformProjectRoot, "Podfile"); - - const contents = await promises.readFile(file, "utf8"); - - await promises.writeFile( - file, - applyCocoaPodsModifications(contents), - "utf-8", - ); - - return config; - }, - ]); -}; - -// Only the post-install block is required, the post installer block is -// used for spm (swift package manager) which Expo doesn't currently support. -export function applyCocoaPodsModifications(contents: string): string { - // Ensure installer blocks exist - let src = addInstallerBlock(contents, "post"); - src = addMapLibreInstallerBlock(src, "post"); - - return src; -} - -export function addInstallerBlock( - src: string, - blockName: InstallerBlockName, -): string { - const matchBlock = new RegExp(`${blockName}_install do \\|installer\\|`); - const tag = `${blockName}_installer`; - for (const line of src.split("\n")) { - const contents = line.trim(); - // Ignore comments - if (!contents.startsWith("#")) { - // Prevent adding the block if it exists outside of comments. - if (contents.match(matchBlock)) { - // This helps to still allow revisions, since we enabled the block previously. - // Only continue if the generated block exists... - const modified = removeGeneratedContents(src, tag); - if (!modified) { - return src; - } - } - } - } - - return mergeContents({ - tag, - src, - newSrc: [` ${blockName}_install do |installer|`, " end"].join("\n"), - anchor: /use_react_native/, - // We can't go after the use_react_native block because it might have parameters, causing it to be multi-line (see react-native template). - offset: 0, - comment: "#", - }).contents; -} - -export function addMapLibreInstallerBlock( - src: string, - blockName: InstallerBlockName, -): string { - return mergeContents({ - tag: `@maplibre/maplibre-react-native-${blockName}_installer`, - src, - newSrc: ` $MLRN.${blockName}_install(installer)`, - anchor: new RegExp(`${blockName}_install do \\|installer\\|`), - offset: 1, - comment: "#", - }).contents; -} - -/** - * Exclude building for arm64 on simulator devices in the pbxproj project. - * Without this, production builds targeting simulators will fail. - */ -export function setExcludedArchitectures(project: XcodeProject): XcodeProject { - const configurations = project.pbxXCBuildConfigurationSection(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - for (const { name, buildSettings } of Object.values(configurations || {})) { - // Guessing that this is the best way to emulate Xcode. - // Using `project.addToBuildSettings` modifies too many targets. - if ( - name === "Release" && - typeof buildSettings?.PRODUCT_NAME !== "undefined" - ) { - buildSettings['"EXCLUDED_ARCHS[sdk=iphonesimulator*]"'] = '"arm64"'; - } - } - - return project; -} - -const withoutSignatures: ConfigPlugin = (config) => { - return withXcodeProject(config, async (config) => { - config.modResults.addBuildPhase( - [], - "PBXShellScriptBuildPhase", - "Remove signature files (Xcode workaround)", - null, - { - shellPath: "/bin/sh", - shellScript: ` - echo "Remove signature files (Xcode workaround)"; - rm -rf "$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature"; - `, - }, - ); - - return config; - }); -}; - -/** - * Set the Debug Information Format to DWARF with dSYM File during EAS Build for Managed App - * https://github.com/expo/eas-cli/issues/968 - * // Set artifactPath in eas.json - * "ios": { - * "artifactPath": "ios/build/*" - * } - */ -const withDwarfDsym: ConfigPlugin = (config) => { - return withXcodeProject(config, async (config) => { - config.modResults.debugInformationFormat = "dwarf-with-dsym"; - - return config; - }); -}; - -const withExcludedSimulatorArchitectures: ConfigPlugin = (c) => { - return withXcodeProject(c, (config) => { - config.modResults = setExcludedArchitectures(config.modResults); - - return config; - }); -}; - -export const ios = { - withCocoaPodsInstallerBlocks, - withoutSignatures, - withDwarfDsym, - withExcludedSimulatorArchitectures, -}; From 40d0dcbddfcabbffd5b51cff4a76cdeafa38e6dc Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 11:34:35 +0100 Subject: [PATCH 15/36] style: remove unused import --- packages/expo-app/app.config.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/expo-app/app.config.ts b/packages/expo-app/app.config.ts index 3cabe21bc..79f5b60aa 100644 --- a/packages/expo-app/app.config.ts +++ b/packages/expo-app/app.config.ts @@ -1,8 +1,6 @@ import "ts-node/register"; import { type ExpoConfig, type ConfigContext } from "expo/config"; -import { type MapLibrePluginProps } from "../../src/plugin/MapLibrePluginProps"; - export default ({ config }: ConfigContext): ExpoConfig => ({ ...config, name: "Expo App", From b6a2e8c35cf5e7ea42120f7418fc3ff185a03dbb Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 12:24:23 +0100 Subject: [PATCH 16/36] feat: use customizable properties with prefix --- android/build.gradle | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 688c18851..621af27b1 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -35,6 +35,10 @@ def getExtOrDefault(name) { return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["org.maplibre.reactnative." + name] } +def getConfigurableExtOrDefault(name) { + return rootProject.ext.has("org.maplibre.reactnative." + name) ? rootProject.ext.get("org.maplibre.reactnative." + name) : project.properties["org.maplibre.reactnative." + name] +} + def getExtOrIntegerDefault(name) { return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["org.maplibre.reactnative." + name]).toInteger() } @@ -72,12 +76,12 @@ android { main { java.srcDirs = ['src/main/java'] - if (getExtOrDefault("locationEngine") == "default") { + if (getConfigurableExtOrDefault("locationEngine") == "default") { java.srcDirs += 'src/main/location-engine-default' - } else if (getExtOrDefault("locationEngine") == "google") { + } else if (getConfigurableExtOrDefault("locationEngine") == "google") { java.srcDirs += 'src/main/location-engine-google' } else { - throw new GradleException("org.maplibre.reactnative.locationEngine.locationEngine should be one of [\"default\", \"google\"]`. \"${getExtOrDefault("locationEngine")}\" was provided.") + throw new GradleException("org.maplibre.reactnative.locationEngine.locationEngine should be one of [\"default\", \"google\"]`. \"${getConfigurableExtOrDefault("locationEngine")}\" was provided.") } } } @@ -120,8 +124,8 @@ dependencies { implementation "androidx.vectordrawable:vectordrawable:1.1.0" implementation "androidx.annotation:annotation:1.7.0" implementation "androidx.appcompat:appcompat:1.6.1" - implementation "com.squareup.okhttp3:okhttp:${getExtOrDefault('okhttpVersion')}" - implementation "com.squareup.okhttp3:okhttp-urlconnection:${getExtOrDefault('okhttpVersion')}" + implementation "com.squareup.okhttp3:okhttp:${getConfigurableExtOrDefault('okhttpVersion')}" + implementation "com.squareup.okhttp3:okhttp-urlconnection:${getConfigurableExtOrDefault('okhttpVersion')}" // MapLibre Plugins implementation ("org.maplibre.gl:android-plugin-localization-v9:3.0.1") @@ -129,7 +133,7 @@ dependencies { implementation ("org.maplibre.gl:android-plugin-markerview-v9:3.0.1") // Dependencies for Google Location Engine - if (getExtOrDefault("locationEngine") == "google") { + if (getConfigurableExtOrDefault("locationEngine") == "google") { implementation "com.google.android.gms:play-services-location:21.3.0" } } From 1d239d2f0d6e4a1ef7f907a501bd6dcf995ada0e Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 17:41:44 +0100 Subject: [PATCH 17/36] feat: use withPodfile instead of withDangerousMod --- .../plugin/__fixtures__/cocoapodFiles.ts | 173 ----------- .../__snapshots__/withMapLibre.test.ts.snap | 194 ------------- .../plugin/ios/__fixtures__/Podfile.ts | 133 +++++++++ .../applyPodfilePostInstall.test.ts.snap | 272 ++++++++++++++++++ .../ios/applyPodfilePostInstall.test.ts | 45 +++ src/__tests__/plugin/withMapLibre.test.ts | 49 ---- src/plugin/ios.ts | 112 ++++++++ src/plugin/withMapLibre.ts | 177 +----------- 8 files changed, 572 insertions(+), 583 deletions(-) delete mode 100644 src/__tests__/plugin/__fixtures__/cocoapodFiles.ts delete mode 100644 src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap create mode 100644 src/__tests__/plugin/ios/__fixtures__/Podfile.ts create mode 100644 src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap create mode 100644 src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts delete mode 100644 src/__tests__/plugin/withMapLibre.test.ts create mode 100644 src/plugin/ios.ts diff --git a/src/__tests__/plugin/__fixtures__/cocoapodFiles.ts b/src/__tests__/plugin/__fixtures__/cocoapodFiles.ts deleted file mode 100644 index d8a31b95b..000000000 --- a/src/__tests__/plugin/__fixtures__/cocoapodFiles.ts +++ /dev/null @@ -1,173 +0,0 @@ -export const reactNativeTemplatePodfile = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - config = use_native_modules! - - use_react_native!( - :path => config[:reactNativePath], - # to enable hermes on iOS, change \`false\` to \`true\` and then install pods - :hermes_enabled => false - ) - - target 'HelloWorldTests' do - inherit! :complete - # Pods for testing - end - - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable the next line. - use_flipper!() - - post_install do |installer| - react_native_post_install(installer) - end -end -`; - -export const expoTemplatePodfile = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - - use_react_native!(:path => config["reactNativePath"]) - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end -end -`; - -export const customExpoTemplatePodfile = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - - use_react_native!(:path => config["reactNativePath"]) - - # pre_install do |installer| - # end - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end -end -`; - -// This tests that if an invalid revision is pushed, the plugin can correct it based on the ID. -export const expoTemplateWithRevisions = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - -# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id -INVALID_pre_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 - INVALID_$MLRN.pre_install(installer) -# @generated end @react-native-mapbox-gl/maps-pre_installer -end -# @generated end pre_installer -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 -INVALID_post_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 - INVALID_$MLRN.post_install(installer) -# @generated end @react-native-mapbox-gl/maps-post_installer -end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - # pre_install do |installer| - # end - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end -end -`; - -export const expoTemplateWithRevisionsAfterComments = ` -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - # pre_install do |installer| - # end - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end - -# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id -INVALID_pre_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 - INVALID_$MLRN.pre_install(installer) -# @generated end @react-native-mapbox-gl/maps-pre_installer -end -# @generated end pre_installer -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-00old-id-2 -INVALID_post_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-001 - INVALID_$MLRN.post_install(installer) -# @generated end @react-native-mapbox-gl/maps-post_installer -end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - -end -`; - -export const blankTemplatePodfile = ` -platform :ios, '12.0' - -target 'HelloWorld' do -end -`; diff --git a/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap b/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap deleted file mode 100644 index 84caa9b32..000000000 --- a/src/__tests__/plugin/__snapshots__/withMapLibre.test.ts.snap +++ /dev/null @@ -1,194 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile 1`] = ` -" -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end -end -" -`; - -exports[`applyCocoaPodsModifications adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` -" -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - # pre_install do |installer| - # end - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end -end -" -`; - -exports[`applyCocoaPodsModifications adds blocks to a react native template podfile 1`] = ` -" -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - config = use_native_modules! - - use_react_native!( - :path => config[:reactNativePath], - # to enable hermes on iOS, change \`false\` to \`true\` and then install pods - :hermes_enabled => false - ) - - target 'HelloWorldTests' do - inherit! :complete - # Pods for testing - end - - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable the next line. - use_flipper!() - - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - react_native_post_install(installer) - end -end -" -`; - -exports[`applyCocoaPodsModifications does not work with revisions to blocks after comments 1`] = ` -" -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - # pre_install do |installer| - # end - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - # flipper_post_install(installer) - # end - # end - -# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id -INVALID_pre_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 - INVALID_$MLRN.pre_install(installer) -# @generated end @react-native-mapbox-gl/maps-pre_installer -end -# @generated end pre_installer -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 - post_install do |installer| - end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - -end -" -`; - -exports[`applyCocoaPodsModifications works after revisions to blocks 1`] = ` -" -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -platform :ios, '12.0' - -target 'HelloWorld' do - use_unimodules! - config = use_native_modules! - -# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-00old-id -INVALID_pre_install do |installer| -# @generated begin @react-native-mapbox-gl/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-00 - INVALID_$MLRN.pre_install(installer) -# @generated end @react-native-mapbox-gl/maps-pre_installer -end -# @generated end pre_installer -# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403 - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - end -# @generated end post_installer - use_react_native!(:path => config["reactNativePath"]) - - # pre_install do |installer| - # end - - # Uncomment to opt-in to using Flipper - # - # if !ENV['CI'] - # use_flipper!('Flipper' => '0.75.1', 'Flipper-Folly' => '2.5.3', 'Flipper-RSocket' => '1.3.1') - # post_install do |installer| - # flipper_post_install(installer) - # end - # end -end -" -`; diff --git a/src/__tests__/plugin/ios/__fixtures__/Podfile.ts b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts new file mode 100644 index 000000000..e60b1bc55 --- /dev/null +++ b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts @@ -0,0 +1,133 @@ +export const reactNativeTemplatePodfile = ` +# Resolve react_native_pods.rb with node to allow for hoisting +require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + +platform :ios, min_ios_version_supported +prepare_react_native_project! + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end + +target 'HelloWorld' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + target 'HelloWorldTests' do + inherit! :complete + # Pods for testing + end + + post_install do |installer| + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) + end +end +`; + +export const expoTemplatePodfile = ` +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! + +target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) + + post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + end +end +`; + +export const expoTemplatePodfileCustomized = expoTemplatePodfile.replace( + "post_install do |installer|", + `post_install do |installer| + # Some possible customization`, +); + +// This tests that if an invalid revision is pushed, the plugin can correct it based on the ID +export const expoTemplateWithRevisions = expoTemplatePodfile.replace( + "post_install do |installer|", + `post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-001 + INVALID_$MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer`, +); + +export const blankTemplatePodfile = ` +platform :ios, '12.0' + +target 'HelloWorld' do +end +`; diff --git a/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap new file mode 100644 index 000000000..16beff36c --- /dev/null +++ b/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap @@ -0,0 +1,272 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Expo Plugin iOS – applyPodfileModifications adds blocks to a expo prebuild template podfile 1`] = ` +" +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! + +target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) + + post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + end +end +" +`; + +exports[`Expo Plugin iOS – applyPodfileModifications adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` +" +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! + +target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) + + post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + # Some possible customization + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + end +end +" +`; + +exports[`Expo Plugin iOS – applyPodfileModifications adds blocks to a react native template podfile 1`] = ` +" +# Resolve react_native_pods.rb with node to allow for hoisting +require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + +platform :ios, min_ios_version_supported +prepare_react_native_project! + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end + +target 'HelloWorld' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + target 'HelloWorldTests' do + inherit! :complete + # Pods for testing + end + + post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) + end +end +" +`; + +exports[`Expo Plugin iOS – applyPodfileModifications works after revisions to blocks 1`] = ` +" +require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") +require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] + +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' +install! 'cocoapods', + :deterministic_uuids => false + +prepare_react_native_project! + +target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'node', + '--no-warnings', + '--eval', + 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) + + post_install do |installer| +# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) +# @generated end @maplibre/maplibre-react-native-post_installer + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', + ) + + # This is necessary for Xcode 14, because it signs resource bundles by default + # when building for devices. + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + target_installation_result.resource_bundle_targets.each do |resource_bundle_target| + resource_bundle_target.build_configurations.each do |config| + config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' + end + end + end + end +end +" +`; diff --git a/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts b/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts new file mode 100644 index 000000000..be0960d0c --- /dev/null +++ b/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts @@ -0,0 +1,45 @@ +import * as podfileFixtures from "./__fixtures__/Podfile"; +import { applyPodfilePostInstall } from "../../../plugin/ios"; + +describe("Expo Plugin iOS – applyPodfileModifications", () => { + it("adds blocks to a react native template podfile", () => { + expect( + applyPodfilePostInstall(podfileFixtures.reactNativeTemplatePodfile), + ).toMatchSnapshot(); + }); + + it("adds blocks to a expo prebuild template podfile", () => { + expect( + applyPodfilePostInstall(podfileFixtures.expoTemplatePodfile), + ).toMatchSnapshot(); + }); + + it("adds blocks to a expo prebuild template podfile with custom modifications", () => { + expect( + applyPodfilePostInstall(podfileFixtures.expoTemplatePodfileCustomized), + ).toMatchSnapshot(); + }); + + it("fails to add blocks to a bare podfile", () => { + expect(() => + applyPodfilePostInstall(podfileFixtures.blankTemplatePodfile), + ).toThrow("Failed to match"); + expect(() => applyPodfilePostInstall("")).toThrow("Failed to match"); + }); + + it("does not re add blocks to an applied template podfile", () => { + const runOnce = applyPodfilePostInstall( + podfileFixtures.reactNativeTemplatePodfile, + ); + + expect(applyPodfilePostInstall(runOnce)).toMatch(runOnce); + }); + + it("works after revisions to blocks", () => { + const runOnce = applyPodfilePostInstall( + podfileFixtures.expoTemplateWithRevisions, + ); + + expect(runOnce).toMatchSnapshot(); + }); +}); diff --git a/src/__tests__/plugin/withMapLibre.test.ts b/src/__tests__/plugin/withMapLibre.test.ts deleted file mode 100644 index a145bed7f..000000000 --- a/src/__tests__/plugin/withMapLibre.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as fixtures from "./__fixtures__/cocoapodFiles"; -import { applyCocoaPodsModifications } from "../../plugin/withMapLibre"; - -describe("applyCocoaPodsModifications", () => { - it("adds blocks to a react native template podfile", () => { - expect( - applyCocoaPodsModifications(fixtures.reactNativeTemplatePodfile), - ).toMatchSnapshot(); - }); - it("adds blocks to a expo prebuild template podfile", () => { - expect( - applyCocoaPodsModifications(fixtures.expoTemplatePodfile), - ).toMatchSnapshot(); - }); - it("adds blocks to a expo prebuild template podfile with custom modifications", () => { - expect( - applyCocoaPodsModifications(fixtures.customExpoTemplatePodfile), - ).toMatchSnapshot(); - }); - it("fails to add blocks to a bare podfile", () => { - expect(() => - applyCocoaPodsModifications(fixtures.blankTemplatePodfile), - ).toThrow("Failed to match"); - expect(() => applyCocoaPodsModifications("")).toThrow("Failed to match"); - }); - it("does not re add blocks to an applied template podfile", () => { - const runOnce = applyCocoaPodsModifications( - fixtures.reactNativeTemplatePodfile, - ); - - expect(applyCocoaPodsModifications(runOnce)).toMatch(runOnce); - }); - it("works after revisions to blocks", () => { - const runOnce = applyCocoaPodsModifications( - fixtures.expoTemplateWithRevisions, - ); - - expect(runOnce).toMatchSnapshot(); - }); - // A known issue is that the regex won't work if the template - // has a pre_install/post_install block commented out, before the `use_react_native` function. - it("does not work with revisions to blocks after comments", () => { - const runOnce = applyCocoaPodsModifications( - fixtures.expoTemplateWithRevisionsAfterComments, - ); - - expect(runOnce).toMatchSnapshot(); - }); -}); diff --git a/src/plugin/ios.ts b/src/plugin/ios.ts new file mode 100644 index 000000000..e1391aae9 --- /dev/null +++ b/src/plugin/ios.ts @@ -0,0 +1,112 @@ +import { + type ConfigPlugin, + withPodfile, + withXcodeProject, + type XcodeProject, +} from "@expo/config-plugins"; +import { mergeContents } from "@expo/config-plugins/build/utils/generateCode"; + +/** + * Only the post-install block is required, the post installer block is used for SPM (Swift Package Manager) which Expo + * doesn't currently support. + */ +export function applyPodfilePostInstall(contents: string): string { + const result = mergeContents({ + tag: `@maplibre/maplibre-react-native-post_installer`, + src: contents, + newSrc: ` $MLRN.post_install(installer)`, + anchor: new RegExp(`post_install do \\|installer\\|`), + offset: 1, + comment: "#", + }); + + if (result.didMerge || result.didClear) { + return result.contents; + } + + return contents; +} + +const withPodfilePostInstall: ConfigPlugin = (config) => { + return withPodfile(config, (c) => { + c.modResults.contents = applyPodfilePostInstall(c.modResults.contents); + + return c; + }); +}; + +/** + * Exclude building for arm64 on simulator devices in the pbxproj project. + * Without this, production builds targeting simulators will fail. + */ +function setExcludedArchitectures(project: XcodeProject): XcodeProject { + const configurations = project.pbxXCBuildConfigurationSection(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + for (const { name, buildSettings } of Object.values(configurations || {})) { + // Guessing that this is the best way to emulate Xcode. + // Using `project.addToBuildSettings` modifies too many targets. + if ( + name === "Release" && + typeof buildSettings?.PRODUCT_NAME !== "undefined" + ) { + buildSettings['"EXCLUDED_ARCHS[sdk=iphonesimulator*]"'] = '"arm64"'; + } + } + + return project; +} + +const withoutSignatures: ConfigPlugin = (config) => { + return withXcodeProject(config, async (c) => { + c.modResults.addBuildPhase( + [], + "PBXShellScriptBuildPhase", + "Remove signature files (Xcode workaround)", + null, + { + shellPath: "/bin/sh", + shellScript: ` + echo "Remove signature files (Xcode workaround)"; + rm -rf "$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature"; + `, + }, + ); + + return c; + }); +}; + +/** + * Set the Debug Information Format to DWARF with dSYM File during EAS Build for Managed App + * https://github.com/expo/eas-cli/issues/968 + * + * Set `artifactPath` in `eas.json`: + * ```json + * "ios": { + * "artifactPath": "ios/build/*" + * } + * ``` + */ +const withDwarfDsym: ConfigPlugin = (config) => { + return withXcodeProject(config, async (c) => { + c.modResults.debugInformationFormat = "dwarf-with-dsym"; + + return c; + }); +}; + +const withExcludedSimulatorArchitectures: ConfigPlugin = (config) => { + return withXcodeProject(config, (c) => { + c.modResults = setExcludedArchitectures(c.modResults); + + return c; + }); +}; + +export const ios = { + withPodfilePostInstall, + withoutSignatures, + withDwarfDsym, + withExcludedSimulatorArchitectures, +}; diff --git a/src/plugin/withMapLibre.ts b/src/plugin/withMapLibre.ts index 4682ce095..662340071 100644 --- a/src/plugin/withMapLibre.ts +++ b/src/plugin/withMapLibre.ts @@ -1,16 +1,6 @@ -import { - type ConfigPlugin, - createRunOncePlugin, - withDangerousMod, - withXcodeProject, - type XcodeProject, -} from "@expo/config-plugins"; -import { - mergeContents, - removeGeneratedContents, -} from "@expo/config-plugins/build/utils/generateCode"; -import { promises } from "node:fs"; -import path from "node:path"; +import { type ConfigPlugin, createRunOncePlugin } from "@expo/config-plugins"; + +import { ios } from "./ios"; let pkg: { name: string; version?: string } = { name: "@maplibre/maplibre-react-native", @@ -21,161 +11,14 @@ try { // empty catch block } -type InstallerBlockName = "pre" | "post"; - -/** - * Dangerously adds the custom installer hooks to the Podfile. - * In the future this should be removed in favor of some custom hooks provided by Expo autolinking. - * - * https://github.com/maplibre/maplibre-react-native/blob/main/docs/guides/setup/iOS.md - */ -const withCocoaPodsInstallerBlocks: ConfigPlugin = (c) => { - return withDangerousMod(c, [ - "ios", - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - async (config) => { - const file = path.join(config.modRequest.platformProjectRoot, "Podfile"); - - const contents = await promises.readFile(file, "utf8"); - - await promises.writeFile( - file, - applyCocoaPodsModifications(contents), - "utf-8", - ); - - return config; - }, - ]); -}; - -// Only the post-install block is required, the post installer block is -// used for spm (swift package manager) which Expo doesn't currently support. -export function applyCocoaPodsModifications(contents: string): string { - // Ensure installer blocks exist - let src = addInstallerBlock(contents, "post"); - src = addMapLibreInstallerBlock(src, "post"); - - return src; -} - -export function addInstallerBlock( - src: string, - blockName: InstallerBlockName, -): string { - const matchBlock = new RegExp(`${blockName}_install do \\|installer\\|`); - const tag = `${blockName}_installer`; - for (const line of src.split("\n")) { - const contents = line.trim(); - // Ignore comments - if (!contents.startsWith("#")) { - // Prevent adding the block if it exists outside of comments. - if (contents.match(matchBlock)) { - // This helps to still allow revisions, since we enabled the block previously. - // Only continue if the generated block exists... - const modified = removeGeneratedContents(src, tag); - if (!modified) { - return src; - } - } - } - } - - return mergeContents({ - tag, - src, - newSrc: [` ${blockName}_install do |installer|`, " end"].join("\n"), - anchor: /use_react_native/, - // We can't go after the use_react_native block because it might have parameters, causing it to be multi-line (see react-native template). - offset: 0, - comment: "#", - }).contents; -} - -export function addMapLibreInstallerBlock( - src: string, - blockName: InstallerBlockName, -): string { - return mergeContents({ - tag: `@maplibre/maplibre-react-native-${blockName}_installer`, - src, - newSrc: ` $MLRN.${blockName}_install(installer)`, - anchor: new RegExp(`${blockName}_install do \\|installer\\|`), - offset: 1, - comment: "#", - }).contents; -} - -/** - * Exclude building for arm64 on simulator devices in the pbxproj project. - * Without this, production builds targeting simulators will fail. - */ -export function setExcludedArchitectures(project: XcodeProject): XcodeProject { - const configurations = project.pbxXCBuildConfigurationSection(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - for (const { name, buildSettings } of Object.values(configurations || {})) { - // Guessing that this is the best way to emulate Xcode. - // Using `project.addToBuildSettings` modifies too many targets. - if ( - name === "Release" && - typeof buildSettings?.PRODUCT_NAME !== "undefined" - ) { - buildSettings['"EXCLUDED_ARCHS[sdk=iphonesimulator*]"'] = '"arm64"'; - } - } - return project; -} - -const withoutSignatures: ConfigPlugin = (config) => { - const shellScript = ` - echo "Remove signature files (Xcode workaround)"; - rm -rf "$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature"; - `; - return withXcodeProject(config, async (config) => { - const xcodeProject = config.modResults; - xcodeProject.addBuildPhase( - [], - "PBXShellScriptBuildPhase", - "Remove signature files (Xcode workaround)", - null, - { - shellPath: "/bin/sh", - shellScript, - }, - ); - return config; - }); -}; - -/** - * Set the Debug Information Format to DWARF with dSYM File during EAS Build for Managed App - * https://github.com/expo/eas-cli/issues/968 - * // Set artifactPath in eas.json - * "ios": { - * "artifactPath": "ios/build/*" - * } - */ -const withDwarfDsym: ConfigPlugin = (config) => { - return withXcodeProject(config, async (config) => { - const xcodeProject = config.modResults; - xcodeProject.debugInformationFormat = "dwarf-with-dsym"; - return config; - }); -}; - -const withExcludedSimulatorArchitectures: ConfigPlugin = (c) => { - return withXcodeProject(c, (config) => { - config.modResults = setExcludedArchitectures(config.modResults); - return config; - }); -}; - const withMapLibre: ConfigPlugin = (config) => { - config = withoutSignatures( - withDwarfDsym(withExcludedSimulatorArchitectures(config)), - ); - return withCocoaPodsInstallerBlocks(config); + // iOS + config = ios.withExcludedSimulatorArchitectures(config); + config = ios.withDwarfDsym(config); + config = ios.withoutSignatures(config); + config = ios.withPodfilePostInstall(config); + + return config; }; export default createRunOncePlugin(withMapLibre, pkg.name, pkg.version); From 4aff7533f748a6182e3b6fbef9468f69c96dc058 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 17:54:55 +0100 Subject: [PATCH 18/36] test: use snapshot-diff for better plugin snapshots --- package.json | 1 + .../applyPodfilePostInstall.test.ts.snap | 320 ++++-------------- .../ios/applyPodfilePostInstall.test.ts | 53 +-- yarn.lock | 18 +- 4 files changed, 117 insertions(+), 275 deletions(-) diff --git a/package.json b/package.json index 2b96f6f23..e5eb325a0 100644 --- a/package.json +++ b/package.json @@ -165,6 +165,7 @@ "react-native-builder-bob": "^0.34.0", "react-test-renderer": "18.3.1", "semantic-release": "^24.2.0", + "snapshot-diff": "^0.10.0", "ts-node": "^10.9.2", "tsx": "^4.19.2", "typescript": "^5.7.2" diff --git a/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap index 16beff36c..1f8254eb6 100644 --- a/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap +++ b/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap @@ -1,272 +1,86 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Expo Plugin iOS – applyPodfileModifications adds blocks to a expo prebuild template podfile 1`] = ` -" -require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") -require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} - -ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' -install! 'cocoapods', - :deterministic_uuids => false - -prepare_react_native_project! - -target 'HelloWorld' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', +exports[`Expo Plugin iOS – applyPodfilePostInstall adds blocks to a expo prebuild template podfile 1`] = ` +"Snapshot Diff: +- First value ++ Second value + +@@ -44,10 +44,13 @@ + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', ) - # This is necessary for Xcode 14, because it signs resource bundles by default - # when building for devices. - installer.target_installation_results.pod_target_installation_results - .each do |pod_name, target_installation_result| - target_installation_result.resource_bundle_targets.each do |resource_bundle_target| - resource_bundle_target.build_configurations.each do |config| - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end - end -end -" + post_install do |installer| ++ # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 ++ $MLRN.post_install(installer) ++ # @generated end @maplibre/maplibre-react-native-post_installer + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true'," `; -exports[`Expo Plugin iOS – applyPodfileModifications adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` -" -require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") -require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} - -ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' -install! 'cocoapods', - :deterministic_uuids => false - -prepare_react_native_project! - -target 'HelloWorld' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] +exports[`Expo Plugin iOS – applyPodfilePostInstall adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` +"Snapshot Diff: +- First value ++ Second value - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - # Some possible customization - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', +@@ -44,10 +44,13 @@ + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', ) - # This is necessary for Xcode 14, because it signs resource bundles by default - # when building for devices. - installer.target_installation_results.pod_target_installation_results - .each do |pod_name, target_installation_result| - target_installation_result.resource_bundle_targets.each do |resource_bundle_target| - resource_bundle_target.build_configurations.each do |config| - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end - end -end -" + post_install do |installer| ++ # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 ++ $MLRN.post_install(installer) ++ # @generated end @maplibre/maplibre-react-native-post_installer + # Some possible customization + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false," `; -exports[`Expo Plugin iOS – applyPodfileModifications adds blocks to a react native template podfile 1`] = ` -" -# Resolve react_native_pods.rb with node to allow for hoisting -require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip - -platform :ios, min_ios_version_supported -prepare_react_native_project! - -linkage = ENV['USE_FRAMEWORKS'] -if linkage != nil - Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green - use_frameworks! :linkage => linkage.to_sym -end - -target 'HelloWorld' do - config = use_native_modules! +exports[`Expo Plugin iOS – applyPodfilePostInstall adds blocks to a react native template podfile 1`] = ` +"Snapshot Diff: +- First value ++ Second value - use_react_native!( - :path => config[:reactNativePath], - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - - target 'HelloWorldTests' do - inherit! :complete - # Pods for testing - end +@@ -28,10 +28,13 @@ + inherit! :complete + # Pods for testing + end - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - # :ccache_enabled => true - ) - end -end -" + post_install do |installer| ++ # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 ++ $MLRN.post_install(installer) ++ # @generated end @maplibre/maplibre-react-native-post_installer + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false," `; -exports[`Expo Plugin iOS – applyPodfileModifications works after revisions to blocks 1`] = ` -" -require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") -require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} +exports[`Expo Plugin iOS – applyPodfilePostInstall fixes invalid revisions 1`] = ` +"Snapshot Diff: +- First value ++ Second value -ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] - -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' -install! 'cocoapods', - :deterministic_uuids => false - -prepare_react_native_project! - -target 'HelloWorld' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'node', - '--no-warnings', - '--eval', - 'require(require.resolve(\\'expo-modules-autolinking\\', { paths: [require.resolve(\\'expo/package.json\\')] }))(process.argv.slice(1))', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 - $MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', +@@ -44,12 +44,12 @@ + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', ) - # This is necessary for Xcode 14, because it signs resource bundles by default - # when building for devices. - installer.target_installation_results.pod_target_installation_results - .each do |pod_name, target_installation_result| - target_installation_result.resource_bundle_targets.each do |resource_bundle_target| - resource_bundle_target.build_configurations.each do |config| - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end - end -end -" + post_install do |installer| +- # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-001 +- INVALID_$MLRN.post_install(installer) ++ # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 ++ $MLRN.post_install(installer) + # @generated end @maplibre/maplibre-react-native-post_installer + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false," `; diff --git a/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts b/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts index be0960d0c..c8dc8a1a0 100644 --- a/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts +++ b/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts @@ -1,22 +1,50 @@ +import { snapshotDiff } from "snapshot-diff/build"; + import * as podfileFixtures from "./__fixtures__/Podfile"; import { applyPodfilePostInstall } from "../../../plugin/ios"; -describe("Expo Plugin iOS – applyPodfileModifications", () => { +describe("Expo Plugin iOS – applyPodfilePostInstall", () => { it("adds blocks to a react native template podfile", () => { expect( - applyPodfilePostInstall(podfileFixtures.reactNativeTemplatePodfile), + snapshotDiff( + podfileFixtures.reactNativeTemplatePodfile, + applyPodfilePostInstall(podfileFixtures.reactNativeTemplatePodfile), + ), ).toMatchSnapshot(); }); it("adds blocks to a expo prebuild template podfile", () => { expect( - applyPodfilePostInstall(podfileFixtures.expoTemplatePodfile), + snapshotDiff( + podfileFixtures.expoTemplatePodfile, + applyPodfilePostInstall(podfileFixtures.expoTemplatePodfile), + ), ).toMatchSnapshot(); }); + it("does not re-add blocks to an applied template podfile", () => { + const runOnce = applyPodfilePostInstall( + podfileFixtures.expoTemplatePodfile, + ); + + expect(applyPodfilePostInstall(runOnce)).toMatch(runOnce); + }); + it("adds blocks to a expo prebuild template podfile with custom modifications", () => { expect( - applyPodfilePostInstall(podfileFixtures.expoTemplatePodfileCustomized), + snapshotDiff( + podfileFixtures.expoTemplatePodfileCustomized, + applyPodfilePostInstall(podfileFixtures.expoTemplatePodfileCustomized), + ), + ).toMatchSnapshot(); + }); + + it("fixes invalid revisions", () => { + expect( + snapshotDiff( + podfileFixtures.expoTemplateWithRevisions, + applyPodfilePostInstall(podfileFixtures.expoTemplateWithRevisions), + ), ).toMatchSnapshot(); }); @@ -24,22 +52,7 @@ describe("Expo Plugin iOS – applyPodfileModifications", () => { expect(() => applyPodfilePostInstall(podfileFixtures.blankTemplatePodfile), ).toThrow("Failed to match"); - expect(() => applyPodfilePostInstall("")).toThrow("Failed to match"); - }); - - it("does not re add blocks to an applied template podfile", () => { - const runOnce = applyPodfilePostInstall( - podfileFixtures.reactNativeTemplatePodfile, - ); - expect(applyPodfilePostInstall(runOnce)).toMatch(runOnce); - }); - - it("works after revisions to blocks", () => { - const runOnce = applyPodfilePostInstall( - podfileFixtures.expoTemplateWithRevisions, - ); - - expect(runOnce).toMatchSnapshot(); + expect(() => applyPodfilePostInstall("")).toThrow("Failed to match"); }); }); diff --git a/yarn.lock b/yarn.lock index f1ed6c890..9259ae12f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2781,6 +2781,7 @@ __metadata: react-native-builder-bob: "npm:^0.34.0" react-test-renderer: "npm:18.3.1" semantic-release: "npm:^24.2.0" + snapshot-diff: "npm:^0.10.0" ts-node: "npm:^10.9.2" tsx: "npm:^4.19.2" typescript: "npm:^5.7.2" @@ -10748,7 +10749,7 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.7.0": +"jest-diff@npm:^29.0.0, jest-diff@npm:^29.7.0": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" dependencies: @@ -11041,7 +11042,7 @@ __metadata: languageName: node linkType: hard -"jest-snapshot@npm:^29.2.1, jest-snapshot@npm:^29.7.0": +"jest-snapshot@npm:^29.0.0, jest-snapshot@npm:^29.2.1, jest-snapshot@npm:^29.7.0": version: 29.7.0 resolution: "jest-snapshot@npm:29.7.0" dependencies: @@ -16515,6 +16516,19 @@ __metadata: languageName: node linkType: hard +"snapshot-diff@npm:^0.10.0": + version: 0.10.0 + resolution: "snapshot-diff@npm:0.10.0" + dependencies: + jest-diff: "npm:^29.0.0" + jest-snapshot: "npm:^29.0.0" + pretty-format: "npm:^29.0.0" + peerDependencies: + jest: ">=16" + checksum: 10/056b0e5861c52d6cc52fe595c4673af56dfb30856eb337b23977250528c405ec2f67f671cc6b1679aacb7e6ea87923cce0640bf9b9f93915aa70b7a5d0ea4864 + languageName: node + linkType: hard + "socks-proxy-agent@npm:^8.0.3": version: 8.0.4 resolution: "socks-proxy-agent@npm:8.0.4" From 684b6f57362a6765ae1d9c22f3ffea69d1a6cd94 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 18:02:53 +0100 Subject: [PATCH 19/36] test: use snapshotDiff.getSnapshotDiffSerializer --- .../applyPodfilePostInstall.test.ts.snap | 16 ++++++++-------- .../plugin/ios/applyPodfilePostInstall.test.ts | 4 +++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap index 1f8254eb6..7018d601b 100644 --- a/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap +++ b/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Expo Plugin iOS – applyPodfilePostInstall adds blocks to a expo prebuild template podfile 1`] = ` -"Snapshot Diff: +Snapshot Diff: - First value + Second value @@ -18,11 +18,11 @@ exports[`Expo Plugin iOS – applyPodfilePostInstall adds blocks to a expo prebu installer, config[:reactNativePath], :mac_catalyst_enabled => false, - :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true'," + :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', `; exports[`Expo Plugin iOS – applyPodfilePostInstall adds blocks to a expo prebuild template podfile with custom modifications 1`] = ` -"Snapshot Diff: +Snapshot Diff: - First value + Second value @@ -39,11 +39,11 @@ exports[`Expo Plugin iOS – applyPodfilePostInstall adds blocks to a expo prebu react_native_post_install( installer, config[:reactNativePath], - :mac_catalyst_enabled => false," + :mac_catalyst_enabled => false, `; exports[`Expo Plugin iOS – applyPodfilePostInstall adds blocks to a react native template podfile 1`] = ` -"Snapshot Diff: +Snapshot Diff: - First value + Second value @@ -60,11 +60,11 @@ exports[`Expo Plugin iOS – applyPodfilePostInstall adds blocks to a react nati react_native_post_install( installer, config[:reactNativePath], - :mac_catalyst_enabled => false," + :mac_catalyst_enabled => false, `; exports[`Expo Plugin iOS – applyPodfilePostInstall fixes invalid revisions 1`] = ` -"Snapshot Diff: +Snapshot Diff: - First value + Second value @@ -82,5 +82,5 @@ exports[`Expo Plugin iOS – applyPodfilePostInstall fixes invalid revisions 1`] react_native_post_install( installer, config[:reactNativePath], - :mac_catalyst_enabled => false," + :mac_catalyst_enabled => false, `; diff --git a/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts b/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts index c8dc8a1a0..8e12a97a3 100644 --- a/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts +++ b/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts @@ -1,8 +1,10 @@ -import { snapshotDiff } from "snapshot-diff/build"; +import snapshotDiff from "snapshot-diff"; import * as podfileFixtures from "./__fixtures__/Podfile"; import { applyPodfilePostInstall } from "../../../plugin/ios"; +expect.addSnapshotSerializer(snapshotDiff.getSnapshotDiffSerializer()); + describe("Expo Plugin iOS – applyPodfilePostInstall", () => { it("adds blocks to a react native template podfile", () => { expect( From ae80f42cfe8f64d9ef281135bebf2f0b867cc0f3 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Thu, 2 Jan 2025 18:07:52 +0100 Subject: [PATCH 20/36] test: add Podfile fixture sources --- src/__tests__/plugin/ios/__fixtures__/Podfile.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/__tests__/plugin/ios/__fixtures__/Podfile.ts b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts index e60b1bc55..306e971b3 100644 --- a/src/__tests__/plugin/ios/__fixtures__/Podfile.ts +++ b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts @@ -1,3 +1,4 @@ +// https://github.com/react-native-community/rn-diff-purge/blob/release/0.76.5/RnDiffApp/ios/Podfile export const reactNativeTemplatePodfile = ` # Resolve react_native_pods.rb with node to allow for hoisting require Pod::Executable.execute_command('node', ['-p', @@ -41,6 +42,7 @@ target 'HelloWorld' do end `; +// https://github.com/expo/config-plugins/blob/main/fixtures/Podfile export const expoTemplatePodfile = ` require File.join(File.dirname(\`node --print "require.resolve('expo/package.json')"\`), "scripts/autolinking") require File.join(File.dirname(\`node --print "require.resolve('react-native/package.json')"\`), "scripts/react_native_pods") From d884c888060064b1606e120627bbc44bdc7fce88 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 13:03:51 +0100 Subject: [PATCH 21/36] feat: add locationEngine to Android plugin --- packages/expo-app/app.config.ts | 13 +++- src/MapLibreRN.ts | 2 + .../android/getGradleProperties.test.ts | 41 ++++++++++++ .../android/mergeGradleProperties.test.ts | 37 +++++++++++ src/plugin/MapLibrePluginProps.ts | 7 ++ src/plugin/android.ts | 64 +++++++++++++++++++ src/plugin/withMapLibre.ts | 7 +- 7 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 src/__tests__/plugin/android/getGradleProperties.test.ts create mode 100644 src/__tests__/plugin/android/mergeGradleProperties.test.ts create mode 100644 src/plugin/MapLibrePluginProps.ts create mode 100644 src/plugin/android.ts diff --git a/packages/expo-app/app.config.ts b/packages/expo-app/app.config.ts index 79f5b60aa..164858458 100644 --- a/packages/expo-app/app.config.ts +++ b/packages/expo-app/app.config.ts @@ -1,6 +1,8 @@ import "ts-node/register"; import { type ExpoConfig, type ConfigContext } from "expo/config"; +import { MapLibrePluginProps } from "../../src"; + export default ({ config }: ConfigContext): ExpoConfig => ({ ...config, name: "Expo App", @@ -33,5 +35,14 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ backgroundColor: "#285daa", translucent: false, }, - plugins: ["../../src/plugin/withMapLibre.ts"], + plugins: [ + [ + "../../src/plugin/withMapLibre.ts", + { + android: { + locationEngine: "default", + }, + } as MapLibrePluginProps, + ], + ], }); diff --git a/src/MapLibreRN.ts b/src/MapLibreRN.ts index f3bf3fc8b..70848c5ce 100644 --- a/src/MapLibreRN.ts +++ b/src/MapLibreRN.ts @@ -83,3 +83,5 @@ export type { BackgroundLayerStyle, LightLayerStyle, } from "./types/MapLibreRNStyles"; + +export type { MapLibrePluginProps } from "./plugin/MapLibrePluginProps"; diff --git a/src/__tests__/plugin/android/getGradleProperties.test.ts b/src/__tests__/plugin/android/getGradleProperties.test.ts new file mode 100644 index 000000000..8bb6ec020 --- /dev/null +++ b/src/__tests__/plugin/android/getGradleProperties.test.ts @@ -0,0 +1,41 @@ +import { getGradleProperties } from "../../../plugin/android"; + +describe("Expo Plugin Android – getGradleProperties", () => { + it("removes empty property keys", () => { + const result = getGradleProperties({ + android: { + // @ts-expect-error + "": "default", + }, + }); + + expect(result).toEqual([]); + }); + + it("removes empty property values", () => { + const result = getGradleProperties({ + android: { + // @ts-expect-error + locationEngine: "", + }, + }); + + expect(result).toEqual([]); + }); + + it("adds valid properties", () => { + const result = getGradleProperties({ + android: { + locationEngine: "google", + }, + }); + + expect(result).toEqual([ + { + type: "property", + key: "org.maplibre.reactnative.locationEngine", + value: "google", + }, + ]); + }); +}); diff --git a/src/__tests__/plugin/android/mergeGradleProperties.test.ts b/src/__tests__/plugin/android/mergeGradleProperties.test.ts new file mode 100644 index 000000000..b81cbd0eb --- /dev/null +++ b/src/__tests__/plugin/android/mergeGradleProperties.test.ts @@ -0,0 +1,37 @@ +import { mergeGradleProperties } from "../../../plugin/android"; + +const PROPERTY = { + type: "property", + key: "exampleProperty", + value: "value", +} as const; + +const NEW_PROPERTY = { + type: "property", + key: "newProperty", + value: "new", +} as const; + +describe("Expo Plugin Android – mergeGradleProperties", () => { + it("replaces duplicate property", () => { + expect( + mergeGradleProperties( + [ + PROPERTY, + { + ...NEW_PROPERTY, + value: "old", + }, + ], + [NEW_PROPERTY], + ), + ).toEqual([PROPERTY, NEW_PROPERTY]); + }); + + it("adds new property", () => { + expect(mergeGradleProperties([PROPERTY], [NEW_PROPERTY])).toEqual([ + PROPERTY, + NEW_PROPERTY, + ]); + }); +}); diff --git a/src/plugin/MapLibrePluginProps.ts b/src/plugin/MapLibrePluginProps.ts new file mode 100644 index 000000000..1d644a635 --- /dev/null +++ b/src/plugin/MapLibrePluginProps.ts @@ -0,0 +1,7 @@ +export type MapLibrePluginProps = + | { + android?: { + locationEngine?: "default" | "google"; + }; + } + | undefined; diff --git a/src/plugin/android.ts b/src/plugin/android.ts new file mode 100644 index 000000000..5e858b911 --- /dev/null +++ b/src/plugin/android.ts @@ -0,0 +1,64 @@ +import { + type ConfigPlugin, + withGradleProperties as withGradlePropertiesExpo, +} from "@expo/config-plugins"; +import type { PropertiesItem } from "@expo/config-plugins/build/android/Properties"; + +import type { MapLibrePluginProps } from "./MapLibrePluginProps"; + +type PropertyItem = { + type: "property"; + key: string; + value: string; +}; + +export const getGradleProperties = ( + props: MapLibrePluginProps, +): PropertyItem[] => { + return Object.entries(props?.android || {}).reduce( + (properties, [key, value]) => { + if (key && value) { + properties.push({ + type: "property", + key: `org.maplibre.reactnative.${key}`, + value: value.toString(), + }); + } + + return properties; + }, + [] as PropertyItem[], + ); +}; + +export const mergeGradleProperties = ( + oldProperties: PropertiesItem[], + newProperties: PropertyItem[], +): PropertiesItem[] => { + const newPropertiesKeys = newProperties.map(({ key }) => key); + const merged = oldProperties.filter( + (item) => + !(item.type === "property" && newPropertiesKeys.includes(item.key)), + ); + + merged.push(...newProperties); + + return merged; +}; + +export const withGradleProperties: ConfigPlugin = ( + config, + props, +) => { + const gradleProperties = getGradleProperties(props); + + return withGradlePropertiesExpo(config, (c) => { + c.modResults = mergeGradleProperties(c.modResults, gradleProperties); + + return c; + }); +}; + +export const android = { + withGradleProperties, +}; diff --git a/src/plugin/withMapLibre.ts b/src/plugin/withMapLibre.ts index 662340071..8b3e6a338 100644 --- a/src/plugin/withMapLibre.ts +++ b/src/plugin/withMapLibre.ts @@ -1,5 +1,7 @@ import { type ConfigPlugin, createRunOncePlugin } from "@expo/config-plugins"; +import type { MapLibrePluginProps } from "./MapLibrePluginProps"; +import { android } from "./android"; import { ios } from "./ios"; let pkg: { name: string; version?: string } = { @@ -11,7 +13,10 @@ try { // empty catch block } -const withMapLibre: ConfigPlugin = (config) => { +const withMapLibre: ConfigPlugin = (config, props) => { + // Android + config = android.withGradleProperties(config, props); + // iOS config = ios.withExcludedSimulatorArchitectures(config); config = ios.withDwarfDsym(config); From e22cfd52d901c590dcfd0edcd09167dcac594fd5 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 17:25:19 +0100 Subject: [PATCH 22/36] feat: add configurable versions on Android --- android/build.gradle | 22 +++++++++++----------- android/gradle.properties | 9 ++++++--- scripts/utils/getNativeVersion.ts | 4 ++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 621af27b1..6ff991537 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -116,24 +116,24 @@ dependencies { implementation "com.facebook.react:react-native:+" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - // MapLibre SDK - implementation "org.maplibre.gl:android-sdk:11.7.1" - implementation "org.maplibre.gl:android-sdk-turf:6.0.1" + // MapLibre Native + implementation "org.maplibre.gl:android-sdk:${getConfigurableExtOrDefault('nativeVersion')}" + + // MapLibre Plugins + implementation "org.maplibre.gl:android-plugin-localization-v9:${getConfigurableExtOrDefault('pluginVersion')}" + implementation "org.maplibre.gl:android-plugin-annotation-v9:${getConfigurableExtOrDefault('pluginVersion')}" + implementation "org.maplibre.gl:android-plugin-markerview-v9:${getConfigurableExtOrDefault('pluginVersion')}" // Dependencies + implementation "org.maplibre.gl:android-sdk-turf:${getConfigurableExtOrDefault('turfVersion')}" + implementation "com.squareup.okhttp3:okhttp:${getConfigurableExtOrDefault('okhttpVersion')}" + implementation "com.squareup.okhttp3:okhttp-urlconnection:${getConfigurableExtOrDefault('okhttpVersion')}" implementation "androidx.vectordrawable:vectordrawable:1.1.0" implementation "androidx.annotation:annotation:1.7.0" implementation "androidx.appcompat:appcompat:1.6.1" - implementation "com.squareup.okhttp3:okhttp:${getConfigurableExtOrDefault('okhttpVersion')}" - implementation "com.squareup.okhttp3:okhttp-urlconnection:${getConfigurableExtOrDefault('okhttpVersion')}" - - // MapLibre Plugins - implementation ("org.maplibre.gl:android-plugin-localization-v9:3.0.1") - implementation ("org.maplibre.gl:android-plugin-annotation-v9:3.0.1") - implementation ("org.maplibre.gl:android-plugin-markerview-v9:3.0.1") // Dependencies for Google Location Engine if (getConfigurableExtOrDefault("locationEngine") == "google") { - implementation "com.google.android.gms:play-services-location:21.3.0" + implementation "com.google.android.gms:play-services-location:${getConfigurableExtOrDefault('googlePlayServicesLocationVersion')}" } } diff --git a/android/gradle.properties b/android/gradle.properties index e172a7db6..b7eb7aebf 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -4,9 +4,12 @@ org.maplibre.reactnative.targetSdkVersion=31 org.maplibre.reactnative.compileSdkVersion=31 org.maplibre.reactnative.ndkVersion=21.4.7075529 -# MapLibre React Native Customizations - +# MapLibre React Native +org.maplibre.reactnative.nativeVersion=11.7.1 +org.maplibre.reactnative.pluginVersion=3.0.1 +org.maplibre.reactnative.turfVersion=6.0.1 org.maplibre.reactnative.okhttpVersion=4.9.0 - # Available values: default, google org.maplibre.reactnative.locationEngine=default +# Only applied if locationEngine=google +org.maplibre.reactnative.googlePlayServicesLocationVersion=21.3.0 diff --git a/scripts/utils/getNativeVersion.ts b/scripts/utils/getNativeVersion.ts index 5f1699e97..4849f8605 100644 --- a/scripts/utils/getNativeVersion.ts +++ b/scripts/utils/getNativeVersion.ts @@ -20,8 +20,8 @@ let cachedIosVersion: string; export const getAndroidVersion = async () => { if (!cachedAndroidVersion) { cachedAndroidVersion = await getNativeVersion( - ["android", "build.gradle"], - /^\s+implementation\s+"org.maplibre.gl:android-sdk:(\d+\.\d+\.\d+)"$/, + ["android", "gradle.properties"], + /^org\.maplibre\.reactnative\.nativeVersion=(\d+\.\d+\.\d+)$/, ); } From 69928a53cb6d68af14837bb0e691103ba1eee4fc Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 17:26:42 +0100 Subject: [PATCH 23/36] feat: add Android properties to Expo plugin --- src/plugin/MapLibrePluginProps.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/plugin/MapLibrePluginProps.ts b/src/plugin/MapLibrePluginProps.ts index 1d644a635..173b3c207 100644 --- a/src/plugin/MapLibrePluginProps.ts +++ b/src/plugin/MapLibrePluginProps.ts @@ -1,7 +1,17 @@ +type VersionString = + | `${number}.${number}.${number}` + | `${number}.${number}.${number}-${string}`; + export type MapLibrePluginProps = | { android?: { + nativeVersion?: VersionString; + pluginVersion?: VersionString; + turfVersion?: VersionString; + okhttpVersion?: VersionString; + locationEngine?: "default" | "google"; + googlePlayServicesLocationVersion?: VersionString; }; } | undefined; From f04adb88aacd93c43ad4fab375dfd1c40a671db2 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 17:38:39 +0100 Subject: [PATCH 24/36] docs: add doc comments to MapLibrePluginProps --- src/plugin/MapLibrePluginProps.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/plugin/MapLibrePluginProps.ts b/src/plugin/MapLibrePluginProps.ts index 173b3c207..7b6b05948 100644 --- a/src/plugin/MapLibrePluginProps.ts +++ b/src/plugin/MapLibrePluginProps.ts @@ -4,13 +4,39 @@ type VersionString = export type MapLibrePluginProps = | { + /** + * Properties relevant only for Android + * + * @platform android + */ android?: { + /** + * Version for org.maplibre.gl:android-sdk + */ nativeVersion?: VersionString; + /** + * Version for org.maplibre.gl:android-plugin-*-v9 + */ pluginVersion?: VersionString; + /** + * Version for org.maplibre.gl:android-sdk-turf + */ turfVersion?: VersionString; + /** + * Version for com.squareup.okhttp3:okhttp + */ okhttpVersion?: VersionString; + /** + * Location engine to be used + * + * - `default`: Used per default from MapLibre; F-Droid compatible + * - `google`: Google Play Services Location Engine for higher precision; F-Droid incompatible + */ locationEngine?: "default" | "google"; + /** + * Version for com.google.android.gms:play-services-location, only used with `locationEngine: "google"` + */ googlePlayServicesLocationVersion?: VersionString; }; } From 648440506064a26ff80964b99df2f739212c7731 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 22:51:20 +0100 Subject: [PATCH 25/36] feat: allow more Podfile customization --- docs/guides/setup/React-Native.md | 2 +- maplibre-react-native.podspec | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/guides/setup/React-Native.md b/docs/guides/setup/React-Native.md index 0c1dd21e2..96841f386 100644 --- a/docs/guides/setup/React-Native.md +++ b/docs/guides/setup/React-Native.md @@ -36,7 +36,7 @@ Now rebuild your app. If you want to modify the MapLibre Native iOS version, you can override as follows in your `Podfile`: ```rb -$MLRN_SPM_Spec = { +$MLRN_SPM_SPEC = { url: "https://github.com/maplibre/maplibre-gl-native-distribution", requirement: { kind: "upToNextMajorVersion", diff --git a/maplibre-react-native.podspec b/maplibre-react-native.podspec index d7ccac7c7..a918f6b4a 100644 --- a/maplibre-react-native.podspec +++ b/maplibre-react-native.podspec @@ -2,6 +2,17 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) +# Constants Defaults +$MLRN_NATIVE_VERSION ||= "6.9.0" +$MLRN_SPM_SPEC ||= { + url: "https://github.com/maplibre/maplibre-gl-native-distribution", + requirement: { + kind: "exactVersion", + version: $MLRN_NATIVE_VERSION + }, + product_name: "MapLibre" +} + $MLRN = Object.new def $MLRN._add_spm_to_target(project, target, url, requirement, product_name) @@ -24,18 +35,8 @@ def $MLRN._add_spm_to_target(project, target, url, requirement, product_name) end def $MLRN.post_install(installer) - spm_spec = { - url: "https://github.com/maplibre/maplibre-gl-native-distribution", - requirement: { - kind: "exactVersion", - version: "6.9.0" - }, - product_name: "MapLibre" - } + spm_spec = $MLRN_SPM_SPEC - if $MLRN_SPM_Spec.is_a?(Hash) - spm_spec = $MLRN_SPM_Spec - end project = installer.pods_project self._add_spm_to_target( project, @@ -61,9 +62,9 @@ def $MLRN.post_install(installer) end Pod::Spec.new do |s| - s.name = "maplibre-react-native" - s.summary = "React Native library for creating maps with MapLibre Native" - s.version = package['version'] + s.name = "maplibre-react-native" + s.summary = "React Native library for creating maps with MapLibre Native" + s.version = package['version'] s.authors = { "MapLibre" => "" } s.homepage = "https://github.com/maplibre/maplibre-react-native" s.source = { :git => "https://github.com/maplibre/maplibre-react-native.git" } From b24a2386f865f1946031db16e868c76d1f6459ab Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 22:52:31 +0100 Subject: [PATCH 26/36] feat: add iOS properties to Expo plugin --- packages/expo-app/app.config.ts | 5 +- .../plugin/ios/__fixtures__/Podfile.ts | 4 +- .../applyPodfileConstants.test.ts.snap | 109 ++++++++++++++++++ .../applyPodfilePostInstall.test.ts.snap | 18 +-- .../plugin/ios/applyPodfileConstants.test.ts | 98 ++++++++++++++++ .../ios/applyPodfilePostInstall.test.ts | 4 +- src/plugin/MapLibrePluginProps.ts | 39 ++++++- src/plugin/ios.ts | 55 ++++++++- src/plugin/withMapLibre.ts | 1 + 9 files changed, 310 insertions(+), 23 deletions(-) create mode 100644 src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap create mode 100644 src/__tests__/plugin/ios/applyPodfileConstants.test.ts diff --git a/packages/expo-app/app.config.ts b/packages/expo-app/app.config.ts index 164858458..5133ba3bf 100644 --- a/packages/expo-app/app.config.ts +++ b/packages/expo-app/app.config.ts @@ -39,9 +39,8 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ [ "../../src/plugin/withMapLibre.ts", { - android: { - locationEngine: "default", - }, + android: {}, + ios: {}, } as MapLibrePluginProps, ], ], diff --git a/src/__tests__/plugin/ios/__fixtures__/Podfile.ts b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts index 306e971b3..29d9bd0ec 100644 --- a/src/__tests__/plugin/ios/__fixtures__/Podfile.ts +++ b/src/__tests__/plugin/ios/__fixtures__/Podfile.ts @@ -122,9 +122,9 @@ export const expoTemplatePodfileCustomized = expoTemplatePodfile.replace( export const expoTemplateWithRevisions = expoTemplatePodfile.replace( "post_install do |installer|", `post_install do |installer| -# @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-001 +# @generated begin @maplibre/maplibre-react-native:post-install - expo prebuild (DO NOT MODIFY) sync-001 INVALID_$MLRN.post_install(installer) -# @generated end @maplibre/maplibre-react-native-post_installer`, +# @generated end @maplibre/maplibre-react-native:post-install`, ); export const blankTemplatePodfile = ` diff --git a/src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap new file mode 100644 index 000000000..1a0472234 --- /dev/null +++ b/src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap @@ -0,0 +1,109 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Expo Plugin iOS – applyPodfileConstants adds blocks to a blank podfile 1`] = ` +Snapshot Diff: +- First value ++ Second value + + + platform :ios, '12.0' + ++ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 ++ $MLRN_NATIVE_VERSION = "0.0.0" ++ # @generated end @maplibre/maplibre-react-native:constants + target 'HelloWorld' do + end + +`; + +exports[`Expo Plugin iOS – applyPodfileConstants adds blocks to a expo prebuild template podfile 1`] = ` +Snapshot Diff: +- First value ++ Second value + +@@ -12,10 +12,13 @@ + install! 'cocoapods', + :deterministic_uuids => false + + prepare_react_native_project! + ++ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 ++ $MLRN_NATIVE_VERSION = "0.0.0" ++ # @generated end @maplibre/maplibre-react-native:constants + target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; +`; + +exports[`Expo Plugin iOS – applyPodfileConstants adds blocks to a react native template podfile 1`] = ` +Snapshot Diff: +- First value ++ Second value + +@@ -13,10 +13,13 @@ + if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym + end + ++ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 ++ $MLRN_NATIVE_VERSION = "0.0.0" ++ # @generated end @maplibre/maplibre-react-native:constants + target 'HelloWorld' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], +`; + +exports[`Expo Plugin iOS – applyPodfileConstants only adds spmSpec if nativeVersion and spmSpec are set 1`] = ` +Snapshot Diff: +- First value ++ Second value + +@@ -12,10 +12,20 @@ + install! 'cocoapods', + :deterministic_uuids => false + + prepare_react_native_project! + ++ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-2aafc4c00c5b4c7096712decc138bf098cc5a942 ++ $MLRN_SPM_SPEC = { ++ url: "https://github.com/maplibre/maplibre-gl-native-distribution", ++ requirement: { ++ kind: "upToNextMajorVersion", ++ minimumVersion: "0.0.0" ++ }, ++ product_name: "MapLibre" ++ } ++ # @generated end @maplibre/maplibre-react-native:constants + target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; +`; + +exports[`Expo Plugin iOS – applyPodfileConstants updates block on change 1`] = ` +Snapshot Diff: +- First value ++ Second value + +@@ -12,12 +12,12 @@ + install! 'cocoapods', + :deterministic_uuids => false + + prepare_react_native_project! + +- # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 +- $MLRN_NATIVE_VERSION = "0.0.0" ++ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-32756e2e8076c293937f23fdf0b9979d21ebae95 ++ $MLRN_NATIVE_VERSION = "1.1.1" + # @generated end @maplibre/maplibre-react-native:constants + target 'HelloWorld' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' +`; diff --git a/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap index 7018d601b..80cca35b4 100644 --- a/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap +++ b/src/__tests__/plugin/ios/__snapshots__/applyPodfilePostInstall.test.ts.snap @@ -11,9 +11,9 @@ Snapshot Diff: ) post_install do |installer| -+ # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 ++ # @generated begin @maplibre/maplibre-react-native:post-install - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) -+ # @generated end @maplibre/maplibre-react-native-post_installer ++ # @generated end @maplibre/maplibre-react-native:post-install react_native_post_install( installer, config[:reactNativePath], @@ -32,9 +32,9 @@ Snapshot Diff: ) post_install do |installer| -+ # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 ++ # @generated begin @maplibre/maplibre-react-native:post-install - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) -+ # @generated end @maplibre/maplibre-react-native-post_installer ++ # @generated end @maplibre/maplibre-react-native:post-install # Some possible customization react_native_post_install( installer, @@ -53,9 +53,9 @@ Snapshot Diff: end post_install do |installer| -+ # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 ++ # @generated begin @maplibre/maplibre-react-native:post-install - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) -+ # @generated end @maplibre/maplibre-react-native-post_installer ++ # @generated end @maplibre/maplibre-react-native:post-install # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, @@ -74,11 +74,11 @@ Snapshot Diff: ) post_install do |installer| -- # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-001 +- # @generated begin @maplibre/maplibre-react-native:post-install - expo prebuild (DO NOT MODIFY) sync-001 - INVALID_$MLRN.post_install(installer) -+ # @generated begin @maplibre/maplibre-react-native-post_installer - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 ++ # @generated begin @maplibre/maplibre-react-native:post-install - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472 + $MLRN.post_install(installer) - # @generated end @maplibre/maplibre-react-native-post_installer + # @generated end @maplibre/maplibre-react-native:post-install react_native_post_install( installer, config[:reactNativePath], diff --git a/src/__tests__/plugin/ios/applyPodfileConstants.test.ts b/src/__tests__/plugin/ios/applyPodfileConstants.test.ts new file mode 100644 index 000000000..f97158282 --- /dev/null +++ b/src/__tests__/plugin/ios/applyPodfileConstants.test.ts @@ -0,0 +1,98 @@ +import snapshotDiff from "snapshot-diff"; + +import * as podfileFixtures from "./__fixtures__/Podfile"; +import type { MapLibrePluginProps } from "../../../plugin/MapLibrePluginProps"; +import { applyPodfileConstants } from "../../../plugin/ios"; + +expect.addSnapshotSerializer(snapshotDiff.getSnapshotDiffSerializer()); + +const PROPS: MapLibrePluginProps = { + ios: { nativeVersion: "0.0.0" }, +}; + +describe("Expo Plugin iOS – applyPodfileConstants", () => { + it("adds blocks to a react native template podfile", () => { + expect( + snapshotDiff( + podfileFixtures.reactNativeTemplatePodfile, + applyPodfileConstants( + podfileFixtures.reactNativeTemplatePodfile, + PROPS, + ), + ), + ).toMatchSnapshot(); + }); + + it("adds blocks to a expo prebuild template podfile", () => { + expect( + snapshotDiff( + podfileFixtures.expoTemplatePodfile, + applyPodfileConstants(podfileFixtures.expoTemplatePodfile, PROPS), + ), + ).toMatchSnapshot(); + }); + + it("does not re-add blocks to an applied template podfile", () => { + const runOnce = applyPodfileConstants( + podfileFixtures.expoTemplatePodfile, + PROPS, + ); + + expect(applyPodfileConstants(runOnce, PROPS)).toBe(runOnce); + }); + + it("updates block on change", () => { + const runOnce = applyPodfileConstants( + podfileFixtures.expoTemplatePodfile, + PROPS, + ); + + expect( + snapshotDiff( + runOnce, + applyPodfileConstants(runOnce, { ios: { nativeVersion: "1.1.1" } }), + ), + ).toMatchSnapshot(); + }); + + it("only adds spmSpec if nativeVersion and spmSpec are set", () => { + expect( + snapshotDiff( + podfileFixtures.expoTemplatePodfile, + applyPodfileConstants(podfileFixtures.expoTemplatePodfile, { + ios: { + ...PROPS.ios, + spmSpec: `{ + url: "https://github.com/maplibre/maplibre-gl-native-distribution", + requirement: { + kind: "upToNextMajorVersion", + minimumVersion: "0.0.0" + }, + product_name: "MapLibre" +}`, + }, + }), + ), + ).toMatchSnapshot(); + }); + + it("removes block without constant", () => { + const runOnce = applyPodfileConstants( + podfileFixtures.expoTemplatePodfile, + PROPS, + ); + + expect(applyPodfileConstants(runOnce, undefined)).toBe( + podfileFixtures.expoTemplatePodfile, + ); + }); + + it("adds blocks to a blank podfile", () => { + expect( + snapshotDiff( + podfileFixtures.blankTemplatePodfile, + applyPodfileConstants(podfileFixtures.blankTemplatePodfile, PROPS), + ), + ).toMatchSnapshot(); + }); +}); diff --git a/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts b/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts index 8e12a97a3..da185384c 100644 --- a/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts +++ b/src/__tests__/plugin/ios/applyPodfilePostInstall.test.ts @@ -29,7 +29,7 @@ describe("Expo Plugin iOS – applyPodfilePostInstall", () => { podfileFixtures.expoTemplatePodfile, ); - expect(applyPodfilePostInstall(runOnce)).toMatch(runOnce); + expect(applyPodfilePostInstall(runOnce)).toBe(runOnce); }); it("adds blocks to a expo prebuild template podfile with custom modifications", () => { @@ -50,7 +50,7 @@ describe("Expo Plugin iOS – applyPodfilePostInstall", () => { ).toMatchSnapshot(); }); - it("fails to add blocks to a bare podfile", () => { + it("fails to add blocks to a blank podfile", () => { expect(() => applyPodfilePostInstall(podfileFixtures.blankTemplatePodfile), ).toThrow("Failed to match"); diff --git a/src/plugin/MapLibrePluginProps.ts b/src/plugin/MapLibrePluginProps.ts index 7b6b05948..bc683e40e 100644 --- a/src/plugin/MapLibrePluginProps.ts +++ b/src/plugin/MapLibrePluginProps.ts @@ -11,19 +11,19 @@ export type MapLibrePluginProps = */ android?: { /** - * Version for org.maplibre.gl:android-sdk + * Version for `org.maplibre.gl:android-sdk` */ nativeVersion?: VersionString; /** - * Version for org.maplibre.gl:android-plugin-*-v9 + * Version for `org.maplibre.gl:android-plugin-*-v9` */ pluginVersion?: VersionString; /** - * Version for org.maplibre.gl:android-sdk-turf + * Version for `org.maplibre.gl:android-sdk-turf` */ turfVersion?: VersionString; /** - * Version for com.squareup.okhttp3:okhttp + * Version for `com.squareup.okhttp3:okhttp` */ okhttpVersion?: VersionString; @@ -35,9 +35,38 @@ export type MapLibrePluginProps = */ locationEngine?: "default" | "google"; /** - * Version for com.google.android.gms:play-services-location, only used with `locationEngine: "google"` + * Version for `com.google.android.gms:play-services-location`, only used with `locationEngine: "google"` */ googlePlayServicesLocationVersion?: VersionString; }; + + /** + * Properties relevant only for iOS + * + * @platform ios + */ + ios?: { + /** + * Version for `maplibre-gl-native-distribution` + */ + nativeVersion?: VersionString; + + /** + * Swift Package Manager spec to override the selected version range + * + * @example + * ```ts + * spmSpec: `{ + * url: "https://github.com/maplibre/maplibre-gl-native-distribution", + * requirement: { + * kind: "exactVersion", + * minimumVersion: $MLRN_NATIVE_VERSION + * }, + * product_name: "MapLibre" + * }` + * ``` + */ + spmSpec?: string; + }; } | undefined; diff --git a/src/plugin/ios.ts b/src/plugin/ios.ts index e1391aae9..e9ed41600 100644 --- a/src/plugin/ios.ts +++ b/src/plugin/ios.ts @@ -4,7 +4,14 @@ import { withXcodeProject, type XcodeProject, } from "@expo/config-plugins"; -import { mergeContents } from "@expo/config-plugins/build/utils/generateCode"; +import { + mergeContents, + removeGeneratedContents, +} from "@expo/config-plugins/build/utils/generateCode"; + +import type { MapLibrePluginProps } from "./MapLibrePluginProps"; + +const TAG_PREFIX = `@maplibre/maplibre-react-native`; /** * Only the post-install block is required, the post installer block is used for SPM (Swift Package Manager) which Expo @@ -12,7 +19,7 @@ import { mergeContents } from "@expo/config-plugins/build/utils/generateCode"; */ export function applyPodfilePostInstall(contents: string): string { const result = mergeContents({ - tag: `@maplibre/maplibre-react-native-post_installer`, + tag: `${TAG_PREFIX}:post-install`, src: contents, newSrc: ` $MLRN.post_install(installer)`, anchor: new RegExp(`post_install do \\|installer\\|`), @@ -35,6 +42,49 @@ const withPodfilePostInstall: ConfigPlugin = (config) => { }); }; +export const applyPodfileConstants = ( + contents: string, + props: MapLibrePluginProps, +): string => { + const tag = `${TAG_PREFIX}:constants`; + + const constants = []; + + if (props?.ios?.nativeVersion) { + constants.push(`$MLRN_NATIVE_VERSION = "${props.ios.nativeVersion}"`); + } + + if (props?.ios?.spmSpec) { + constants.push(`$MLRN_SPM_SPEC = ${props.ios.spmSpec}`); + } + + if (constants.length > 0) { + return mergeContents({ + tag, + src: contents, + newSrc: constants.join("\n"), + anchor: /target .+ do/, + offset: 0, + comment: "#", + }).contents; + } + + const modified = removeGeneratedContents(contents, tag); + + return modified ?? contents; +}; + +export const withPodfileConstants: ConfigPlugin = ( + config, + props, +) => { + return withPodfile(config, (c) => { + c.modResults.contents = applyPodfileConstants(c.modResults.contents, props); + + return c; + }); +}; + /** * Exclude building for arm64 on simulator devices in the pbxproj project. * Without this, production builds targeting simulators will fail. @@ -106,6 +156,7 @@ const withExcludedSimulatorArchitectures: ConfigPlugin = (config) => { export const ios = { withPodfilePostInstall, + withPodfileConstants, withoutSignatures, withDwarfDsym, withExcludedSimulatorArchitectures, diff --git a/src/plugin/withMapLibre.ts b/src/plugin/withMapLibre.ts index 8b3e6a338..ffd49fd2d 100644 --- a/src/plugin/withMapLibre.ts +++ b/src/plugin/withMapLibre.ts @@ -21,6 +21,7 @@ const withMapLibre: ConfigPlugin = (config, props) => { config = ios.withExcludedSimulatorArchitectures(config); config = ios.withDwarfDsym(config); config = ios.withoutSignatures(config); + config = ios.withPodfileConstants(config, props); config = ios.withPodfilePostInstall(config); return config; From a9aff35bf48dd874ca4b0cb72ef63faa4a718f53 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 22:54:47 +0100 Subject: [PATCH 27/36] style: fix typo --- maplibre-react-native.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maplibre-react-native.podspec b/maplibre-react-native.podspec index a918f6b4a..95227e887 100644 --- a/maplibre-react-native.podspec +++ b/maplibre-react-native.podspec @@ -2,7 +2,7 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) -# Constants Defaults +# Constant Defaults $MLRN_NATIVE_VERSION ||= "6.9.0" $MLRN_SPM_SPEC ||= { url: "https://github.com/maplibre/maplibre-gl-native-distribution", From 9f048ca6d042b2495719919f5fae2e480ae5d143 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 23:02:16 +0100 Subject: [PATCH 28/36] style: use slash regex --- src/plugin/ios.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/ios.ts b/src/plugin/ios.ts index e9ed41600..36ad0e962 100644 --- a/src/plugin/ios.ts +++ b/src/plugin/ios.ts @@ -22,7 +22,7 @@ export function applyPodfilePostInstall(contents: string): string { tag: `${TAG_PREFIX}:post-install`, src: contents, newSrc: ` $MLRN.post_install(installer)`, - anchor: new RegExp(`post_install do \\|installer\\|`), + anchor: /post_install do \|installer\|/, offset: 1, comment: "#", }); From eac7bb8aa03fc85f4941a9e442a7431f7f020aff Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 23:02:46 +0100 Subject: [PATCH 29/36] test: fix applyPodfileConstants behavior --- .../ios/__snapshots__/applyPodfileConstants.test.ts.snap | 7 ++++--- src/__tests__/plugin/ios/applyPodfileConstants.test.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap index 1a0472234..df468ac01 100644 --- a/src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap +++ b/src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap @@ -58,18 +58,19 @@ Snapshot Diff: :path => config[:reactNativePath], `; -exports[`Expo Plugin iOS – applyPodfileConstants only adds spmSpec if nativeVersion and spmSpec are set 1`] = ` +exports[`Expo Plugin iOS – applyPodfileConstants adds both spmSpec and nativeVersion when set 1`] = ` Snapshot Diff: - First value + Second value -@@ -12,10 +12,20 @@ +@@ -12,10 +12,21 @@ install! 'cocoapods', :deterministic_uuids => false prepare_react_native_project! -+ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-2aafc4c00c5b4c7096712decc138bf098cc5a942 ++ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-658affcd13c2a58a4d2f9c92a6ea5016350863c0 ++ $MLRN_NATIVE_VERSION = "0.0.0" + $MLRN_SPM_SPEC = { + url: "https://github.com/maplibre/maplibre-gl-native-distribution", + requirement: { diff --git a/src/__tests__/plugin/ios/applyPodfileConstants.test.ts b/src/__tests__/plugin/ios/applyPodfileConstants.test.ts index f97158282..7dabd0cf7 100644 --- a/src/__tests__/plugin/ios/applyPodfileConstants.test.ts +++ b/src/__tests__/plugin/ios/applyPodfileConstants.test.ts @@ -55,7 +55,7 @@ describe("Expo Plugin iOS – applyPodfileConstants", () => { ).toMatchSnapshot(); }); - it("only adds spmSpec if nativeVersion and spmSpec are set", () => { + it("adds both spmSpec and nativeVersion when set", () => { expect( snapshotDiff( podfileFixtures.expoTemplatePodfile, From 28e815df9f76df43420983cc568595b29184e755 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 23:03:04 +0100 Subject: [PATCH 30/36] fix: update iOS native version regex --- scripts/utils/getNativeVersion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/utils/getNativeVersion.ts b/scripts/utils/getNativeVersion.ts index 4849f8605..21ae25af7 100644 --- a/scripts/utils/getNativeVersion.ts +++ b/scripts/utils/getNativeVersion.ts @@ -32,7 +32,7 @@ export const getIosVersion = async () => { if (!cachedIosVersion) { cachedIosVersion = await getNativeVersion( ["maplibre-react-native.podspec"], - /^\s+version:\s*"(\d+\.\d+\.\d+)"$/, + /^\$MLRN_NATIVE_VERSION \|\|= "(\d+\.\d+\.\d+)"$/, ); } From 620153bfa08749a725907430e36d9090e18c161c Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 3 Jan 2025 23:13:36 +0100 Subject: [PATCH 31/36] style: fix linting import type --- packages/expo-app/app.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo-app/app.config.ts b/packages/expo-app/app.config.ts index 5133ba3bf..06aa7196b 100644 --- a/packages/expo-app/app.config.ts +++ b/packages/expo-app/app.config.ts @@ -1,7 +1,7 @@ import "ts-node/register"; import { type ExpoConfig, type ConfigContext } from "expo/config"; -import { MapLibrePluginProps } from "../../src"; +import type { MapLibrePluginProps } from "../../src"; export default ({ config }: ConfigContext): ExpoConfig => ({ ...config, From 82daefcbd7500467b93817494301f30d054cf4dd Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Sat, 4 Jan 2025 21:53:32 +0100 Subject: [PATCH 32/36] refactor: rename constants to global variables --- maplibre-react-native.podspec | 2 +- ... applyPodfileGlobalVariables.test.ts.snap} | 32 +++++++++---------- ...ts => applyPodfileGlobalVariables.test.ts} | 31 ++++++++++-------- src/plugin/ios.ts | 23 +++++++------ src/plugin/withMapLibre.ts | 2 +- 5 files changed, 49 insertions(+), 41 deletions(-) rename src/__tests__/plugin/ios/__snapshots__/{applyPodfileConstants.test.ts.snap => applyPodfileGlobalVariables.test.ts.snap} (52%) rename src/__tests__/plugin/ios/{applyPodfileConstants.test.ts => applyPodfileGlobalVariables.test.ts} (67%) diff --git a/maplibre-react-native.podspec b/maplibre-react-native.podspec index 95227e887..3bb02c6b9 100644 --- a/maplibre-react-native.podspec +++ b/maplibre-react-native.podspec @@ -2,7 +2,7 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) -# Constant Defaults +# Global Variable Defaults $MLRN_NATIVE_VERSION ||= "6.9.0" $MLRN_SPM_SPEC ||= { url: "https://github.com/maplibre/maplibre-gl-native-distribution", diff --git a/src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap b/src/__tests__/plugin/ios/__snapshots__/applyPodfileGlobalVariables.test.ts.snap similarity index 52% rename from src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap rename to src/__tests__/plugin/ios/__snapshots__/applyPodfileGlobalVariables.test.ts.snap index df468ac01..4debf836a 100644 --- a/src/__tests__/plugin/ios/__snapshots__/applyPodfileConstants.test.ts.snap +++ b/src/__tests__/plugin/ios/__snapshots__/applyPodfileGlobalVariables.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Expo Plugin iOS – applyPodfileConstants adds blocks to a blank podfile 1`] = ` +exports[`Expo Plugin iOS – applyPodfileGlobalVariables adds blocks to a blank podfile 1`] = ` Snapshot Diff: - First value + Second value @@ -8,15 +8,15 @@ Snapshot Diff: platform :ios, '12.0' -+ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 ++ # @generated begin @maplibre/maplibre-react-native:global-variables - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 + $MLRN_NATIVE_VERSION = "0.0.0" -+ # @generated end @maplibre/maplibre-react-native:constants ++ # @generated end @maplibre/maplibre-react-native:global-variables target 'HelloWorld' do end `; -exports[`Expo Plugin iOS – applyPodfileConstants adds blocks to a expo prebuild template podfile 1`] = ` +exports[`Expo Plugin iOS – applyPodfileGlobalVariables adds blocks to a expo prebuild template podfile 1`] = ` Snapshot Diff: - First value + Second value @@ -27,9 +27,9 @@ Snapshot Diff: prepare_react_native_project! -+ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 ++ # @generated begin @maplibre/maplibre-react-native:global-variables - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 + $MLRN_NATIVE_VERSION = "0.0.0" -+ # @generated end @maplibre/maplibre-react-native:constants ++ # @generated end @maplibre/maplibre-react-native:global-variables target 'HelloWorld' do use_expo_modules! @@ -37,7 +37,7 @@ Snapshot Diff: config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; `; -exports[`Expo Plugin iOS – applyPodfileConstants adds blocks to a react native template podfile 1`] = ` +exports[`Expo Plugin iOS – applyPodfileGlobalVariables adds blocks to a react native template podfile 1`] = ` Snapshot Diff: - First value + Second value @@ -48,9 +48,9 @@ Snapshot Diff: use_frameworks! :linkage => linkage.to_sym end -+ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 ++ # @generated begin @maplibre/maplibre-react-native:global-variables - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 + $MLRN_NATIVE_VERSION = "0.0.0" -+ # @generated end @maplibre/maplibre-react-native:constants ++ # @generated end @maplibre/maplibre-react-native:global-variables target 'HelloWorld' do config = use_native_modules! @@ -58,7 +58,7 @@ Snapshot Diff: :path => config[:reactNativePath], `; -exports[`Expo Plugin iOS – applyPodfileConstants adds both spmSpec and nativeVersion when set 1`] = ` +exports[`Expo Plugin iOS – applyPodfileGlobalVariables adds both spmSpec and nativeVersion when set 1`] = ` Snapshot Diff: - First value + Second value @@ -69,7 +69,7 @@ Snapshot Diff: prepare_react_native_project! -+ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-658affcd13c2a58a4d2f9c92a6ea5016350863c0 ++ # @generated begin @maplibre/maplibre-react-native:global-variables - expo prebuild (DO NOT MODIFY) sync-658affcd13c2a58a4d2f9c92a6ea5016350863c0 + $MLRN_NATIVE_VERSION = "0.0.0" + $MLRN_SPM_SPEC = { + url: "https://github.com/maplibre/maplibre-gl-native-distribution", @@ -79,7 +79,7 @@ Snapshot Diff: + }, + product_name: "MapLibre" + } -+ # @generated end @maplibre/maplibre-react-native:constants ++ # @generated end @maplibre/maplibre-react-native:global-variables target 'HelloWorld' do use_expo_modules! @@ -87,7 +87,7 @@ Snapshot Diff: config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; `; -exports[`Expo Plugin iOS – applyPodfileConstants updates block on change 1`] = ` +exports[`Expo Plugin iOS – applyPodfileGlobalVariables updates block on change 1`] = ` Snapshot Diff: - First value + Second value @@ -98,11 +98,11 @@ Snapshot Diff: prepare_react_native_project! -- # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 +- # @generated begin @maplibre/maplibre-react-native:global-variables - expo prebuild (DO NOT MODIFY) sync-532b7bef83234177a5be487c8a71864bd92b1dd0 - $MLRN_NATIVE_VERSION = "0.0.0" -+ # @generated begin @maplibre/maplibre-react-native:constants - expo prebuild (DO NOT MODIFY) sync-32756e2e8076c293937f23fdf0b9979d21ebae95 ++ # @generated begin @maplibre/maplibre-react-native:global-variables - expo prebuild (DO NOT MODIFY) sync-32756e2e8076c293937f23fdf0b9979d21ebae95 + $MLRN_NATIVE_VERSION = "1.1.1" - # @generated end @maplibre/maplibre-react-native:constants + # @generated end @maplibre/maplibre-react-native:global-variables target 'HelloWorld' do use_expo_modules! diff --git a/src/__tests__/plugin/ios/applyPodfileConstants.test.ts b/src/__tests__/plugin/ios/applyPodfileGlobalVariables.test.ts similarity index 67% rename from src/__tests__/plugin/ios/applyPodfileConstants.test.ts rename to src/__tests__/plugin/ios/applyPodfileGlobalVariables.test.ts index 7dabd0cf7..0ca01ad11 100644 --- a/src/__tests__/plugin/ios/applyPodfileConstants.test.ts +++ b/src/__tests__/plugin/ios/applyPodfileGlobalVariables.test.ts @@ -2,7 +2,7 @@ import snapshotDiff from "snapshot-diff"; import * as podfileFixtures from "./__fixtures__/Podfile"; import type { MapLibrePluginProps } from "../../../plugin/MapLibrePluginProps"; -import { applyPodfileConstants } from "../../../plugin/ios"; +import { applyPodfileGlobalVariables } from "../../../plugin/ios"; expect.addSnapshotSerializer(snapshotDiff.getSnapshotDiffSerializer()); @@ -10,12 +10,12 @@ const PROPS: MapLibrePluginProps = { ios: { nativeVersion: "0.0.0" }, }; -describe("Expo Plugin iOS – applyPodfileConstants", () => { +describe("Expo Plugin iOS – applyPodfileGlobalVariables", () => { it("adds blocks to a react native template podfile", () => { expect( snapshotDiff( podfileFixtures.reactNativeTemplatePodfile, - applyPodfileConstants( + applyPodfileGlobalVariables( podfileFixtures.reactNativeTemplatePodfile, PROPS, ), @@ -27,22 +27,22 @@ describe("Expo Plugin iOS – applyPodfileConstants", () => { expect( snapshotDiff( podfileFixtures.expoTemplatePodfile, - applyPodfileConstants(podfileFixtures.expoTemplatePodfile, PROPS), + applyPodfileGlobalVariables(podfileFixtures.expoTemplatePodfile, PROPS), ), ).toMatchSnapshot(); }); it("does not re-add blocks to an applied template podfile", () => { - const runOnce = applyPodfileConstants( + const runOnce = applyPodfileGlobalVariables( podfileFixtures.expoTemplatePodfile, PROPS, ); - expect(applyPodfileConstants(runOnce, PROPS)).toBe(runOnce); + expect(applyPodfileGlobalVariables(runOnce, PROPS)).toBe(runOnce); }); it("updates block on change", () => { - const runOnce = applyPodfileConstants( + const runOnce = applyPodfileGlobalVariables( podfileFixtures.expoTemplatePodfile, PROPS, ); @@ -50,7 +50,9 @@ describe("Expo Plugin iOS – applyPodfileConstants", () => { expect( snapshotDiff( runOnce, - applyPodfileConstants(runOnce, { ios: { nativeVersion: "1.1.1" } }), + applyPodfileGlobalVariables(runOnce, { + ios: { nativeVersion: "1.1.1" }, + }), ), ).toMatchSnapshot(); }); @@ -59,7 +61,7 @@ describe("Expo Plugin iOS – applyPodfileConstants", () => { expect( snapshotDiff( podfileFixtures.expoTemplatePodfile, - applyPodfileConstants(podfileFixtures.expoTemplatePodfile, { + applyPodfileGlobalVariables(podfileFixtures.expoTemplatePodfile, { ios: { ...PROPS.ios, spmSpec: `{ @@ -76,13 +78,13 @@ describe("Expo Plugin iOS – applyPodfileConstants", () => { ).toMatchSnapshot(); }); - it("removes block without constant", () => { - const runOnce = applyPodfileConstants( + it("removes block without global variable", () => { + const runOnce = applyPodfileGlobalVariables( podfileFixtures.expoTemplatePodfile, PROPS, ); - expect(applyPodfileConstants(runOnce, undefined)).toBe( + expect(applyPodfileGlobalVariables(runOnce, undefined)).toBe( podfileFixtures.expoTemplatePodfile, ); }); @@ -91,7 +93,10 @@ describe("Expo Plugin iOS – applyPodfileConstants", () => { expect( snapshotDiff( podfileFixtures.blankTemplatePodfile, - applyPodfileConstants(podfileFixtures.blankTemplatePodfile, PROPS), + applyPodfileGlobalVariables( + podfileFixtures.blankTemplatePodfile, + PROPS, + ), ), ).toMatchSnapshot(); }); diff --git a/src/plugin/ios.ts b/src/plugin/ios.ts index 36ad0e962..432e0a99c 100644 --- a/src/plugin/ios.ts +++ b/src/plugin/ios.ts @@ -42,27 +42,27 @@ const withPodfilePostInstall: ConfigPlugin = (config) => { }); }; -export const applyPodfileConstants = ( +export const applyPodfileGlobalVariables = ( contents: string, props: MapLibrePluginProps, ): string => { - const tag = `${TAG_PREFIX}:constants`; + const tag = `${TAG_PREFIX}:global-variables`; - const constants = []; + const globalVariables = []; if (props?.ios?.nativeVersion) { - constants.push(`$MLRN_NATIVE_VERSION = "${props.ios.nativeVersion}"`); + globalVariables.push(`$MLRN_NATIVE_VERSION = "${props.ios.nativeVersion}"`); } if (props?.ios?.spmSpec) { - constants.push(`$MLRN_SPM_SPEC = ${props.ios.spmSpec}`); + globalVariables.push(`$MLRN_SPM_SPEC = ${props.ios.spmSpec}`); } - if (constants.length > 0) { + if (globalVariables.length > 0) { return mergeContents({ tag, src: contents, - newSrc: constants.join("\n"), + newSrc: globalVariables.join("\n"), anchor: /target .+ do/, offset: 0, comment: "#", @@ -74,12 +74,15 @@ export const applyPodfileConstants = ( return modified ?? contents; }; -export const withPodfileConstants: ConfigPlugin = ( +export const withPodfileGlobalVariables: ConfigPlugin = ( config, props, ) => { return withPodfile(config, (c) => { - c.modResults.contents = applyPodfileConstants(c.modResults.contents, props); + c.modResults.contents = applyPodfileGlobalVariables( + c.modResults.contents, + props, + ); return c; }); @@ -156,7 +159,7 @@ const withExcludedSimulatorArchitectures: ConfigPlugin = (config) => { export const ios = { withPodfilePostInstall, - withPodfileConstants, + withPodfileGlobalVariables, withoutSignatures, withDwarfDsym, withExcludedSimulatorArchitectures, diff --git a/src/plugin/withMapLibre.ts b/src/plugin/withMapLibre.ts index ffd49fd2d..ca7613b15 100644 --- a/src/plugin/withMapLibre.ts +++ b/src/plugin/withMapLibre.ts @@ -21,7 +21,7 @@ const withMapLibre: ConfigPlugin = (config, props) => { config = ios.withExcludedSimulatorArchitectures(config); config = ios.withDwarfDsym(config); config = ios.withoutSignatures(config); - config = ios.withPodfileConstants(config, props); + config = ios.withPodfileGlobalVariables(config, props); config = ios.withPodfilePostInstall(config); return config; From 2a5216b01837a57924f8a0620f7c457f04b85cd8 Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Sat, 4 Jan 2025 23:06:51 +0100 Subject: [PATCH 33/36] docs: add customization docs --- README.md | 1 + docs/guides/setup/Customization.md | 97 ++++++++++++++++++++++++++++++ docs/guides/setup/Expo.md | 16 +++-- docs/guides/setup/React-Native.md | 18 ++---- src/plugin/MapLibrePluginProps.ts | 15 ++++- 5 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 docs/guides/setup/Customization.md diff --git a/README.md b/README.md index 553242347..53d6da6ea 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ diverged, it has become necessary to separate the projects into specific wrapper - Installation - [Expo](/docs/guides/setup/Expo.md) - [React Native](/docs/guides/setup/React-Native.md) +- [Customization](/docs/guides/setup/Customization.md) - Migrations - [Migrating to v10](/docs/guides/migrations/v10.md) diff --git a/docs/guides/setup/Customization.md b/docs/guides/setup/Customization.md new file mode 100644 index 000000000..6cc1f7733 --- /dev/null +++ b/docs/guides/setup/Customization.md @@ -0,0 +1,97 @@ +# Setup Customization + +It's possible to customize the native setup of the library. These props/options must be applied differently, based on +your project: + +## Expo + +If you are using Expo, simply add the customizations in the projects `app.json` or `app.config.{js,ts}`. Here is an +example customization for in a `app.config.ts`: + +```ts +import type { MapLibrePluginProps } from "@maplibre/maplibre-react-native"; + +export default ({ config }: ConfigContext): ExpoConfig => ({ + ...config, + plugins: [ + [ + "@maplibre/maplibre-react-native", + { + android: { + // Android Plugin Props + }, + ios: { + // iOS Plugin Props + }, + } as MapLibrePluginProps, + ], + ], +}); +``` + +## React Native + +If you are using React Native, the customizations have to be applied differently for each platform. + +On Android they are set in the `gradle.properties`, each of them prefixed with `org.maplibre.reactnative`. Example: + +```diff ++ org.maplibre.reactnative.nativeVersion=x.x.x +``` + +On iOS global variables in the `Podfile` are used, prefixed with `$MLRN`. + +```diff ++ $MLRN_NATIVE_VERSION="x.x.x" +``` + +## Android + +| Plugin Prop | `gradle.properties` Key | Type | Description | +| ------------------------------------------- | ------------------------------------------------------------ | ----------------------- | ----------------------------------------------------------------------------------------------------------- | +| `android.nativeVersion` | `org.maplibre.reactnative.nativeVersion` | `VersionString` | Version for [`org.maplibre.gl:android-sdk`](https://mvnrepository.com/artifact/org.maplibre.gl/android-sdk) | +| `android.pluginVersion` | `org.maplibre.reactnative.pluginVersion` | `VersionString` | Version for `org.maplibre.gl:android-plugin-*-v9` | +| `android.turfVersion` | `org.maplibre.reactnative.turfVersion` | `VersionString` | Version for `org.maplibre.gl:android-sdk-turf` | +| `android.okhttpVersion` | `org.maplibre.reactnative.okhttpVersion` | `VersionString` | Version for `com.squareup.okhttp3:okhttp` | +| `android.locationEngine` | `org.maplibre.reactnative.locationEngine` | `"default" \| "google"` | [Location engine to be used](#choosing-a-location-engine) | +| `android.googlePlayServicesLocationVersion` | `org.maplibre.reactnative.googlePlayServicesLocationVersion` | `VersionString` | Version for `com.google.android.gms:play-services-location`, only used with `locationEngine: "google"` | + +For default values see [`gradle.properties` of the library](/android/gradle.properties). + +### Choosing a Location Engine + +You can choose between the following two location engines on Android: + +- `default` + - Default location engine provided by the device + - Doesn't work with emulator + - F-Droid compatible +- `google` + - Google Play Services Location Engine + - Possibly more accurate + - Works with emulator + - Not F-Droid compatible + +## iOS + +| Plugin Prop | `Podfile` Global Variable | Type | Description | +| ------------------- | ------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------- | +| `ios.nativeVersion` | `$MLRN_NATIVE_VERSION` | `VersionString` | Version for [`maplibre-gl-native-distribution`](https://github.com/maplibre/maplibre-gl-native-distribution/releases) | +| `ios.spmSpec` | `$MLRN_SPM_SPEC` | `string` | [Swift Package Manager Spec](#setting-a-spm-spec) | + +For default values see [`maplibre-react-native.podspec` of the library](/maplibre-react-native.podspec). + +### Setting a SPM Spec + +Setting a Swift Package Manager Spec allows more customization thn + +```rb +$MLRN_SPM_SPEC = { + url: "https://github.com/maplibre/maplibre-gl-native-distribution", + requirement: { + kind: "upToNextMajorVersion", + minimumVersion: "x.x.x" + }, + product_name: "MapLibre" +} +``` diff --git a/docs/guides/setup/Expo.md b/docs/guides/setup/Expo.md index 4c28f4609..9d09d86a1 100644 --- a/docs/guides/setup/Expo.md +++ b/docs/guides/setup/Expo.md @@ -17,15 +17,19 @@ After installing the package, add the [config plugin](https://docs.expo.io/guide ```json { "expo": { - "plugins": ["@maplibre/maplibre-react-native"] + "plugins": [ + "@maplibre/maplibre-react-native" + ] } } ``` -Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) -guide. +Next, rebuild your app as described in the ["Add custom native code"](https://docs.expo.io/workflow/customizing/) guide. -## Plugin Properties +The plugin is required to properly install MapLibre Native on iOS, where it adds `$MLRN.post_install(installer)` to the +`post_install` block in the `ios/Podfile`. On Android it only serves customizations. -This plugin doesn't currently provide any additional properties for customization. The plugin simply generates the -post-install block in the `ios/Podfile`. No additional changes are done on Android. +## Plugin Props + +The plugin allows to customize the setup of MapLibre React Native through plugin props. Find out more in +the [customization guide](/docs/guides/setup/Customization.md). diff --git a/docs/guides/setup/React-Native.md b/docs/guides/setup/React-Native.md index 96841f386..e1ec3c7bd 100644 --- a/docs/guides/setup/React-Native.md +++ b/docs/guides/setup/React-Native.md @@ -31,17 +31,7 @@ pod install Now rebuild your app. -### Installing a specific version - -If you want to modify the MapLibre Native iOS version, you can override as follows in your `Podfile`: - -```rb -$MLRN_SPM_SPEC = { - url: "https://github.com/maplibre/maplibre-gl-native-distribution", - requirement: { - kind: "upToNextMajorVersion", - minimumVersion: "" - }, - product_name: "MapLibre" -} -``` +## Customzations + +You can customize the setup of MapLibre React Native through `gradle.properties` on Android or global variables in the +`Podfile` on iOS. Find out more in the [customization guide](/docs/guides/setup/Customization.md). diff --git a/src/plugin/MapLibrePluginProps.ts b/src/plugin/MapLibrePluginProps.ts index bc683e40e..4dce26a2f 100644 --- a/src/plugin/MapLibrePluginProps.ts +++ b/src/plugin/MapLibrePluginProps.ts @@ -32,6 +32,8 @@ export type MapLibrePluginProps = * * - `default`: Used per default from MapLibre; F-Droid compatible * - `google`: Google Play Services Location Engine for higher precision; F-Droid incompatible + * + * @default "default" */ locationEngine?: "default" | "google"; /** @@ -54,13 +56,22 @@ export type MapLibrePluginProps = /** * Swift Package Manager spec to override the selected version range * + * @default `{ + * url: "https://github.com/maplibre/maplibre-gl-native-distribution", + * requirement: { + * kind: "exactVersion", + * version: $MLRN_NATIVE_VERSION + * }, + * product_name: "MapLibre" + * }` + * * @example * ```ts * spmSpec: `{ * url: "https://github.com/maplibre/maplibre-gl-native-distribution", * requirement: { - * kind: "exactVersion", - * minimumVersion: $MLRN_NATIVE_VERSION + * kind: "upToNextMajorVersion", + * minimumVersion: "x.x.x" * }, * product_name: "MapLibre" * }` From a2c87ca2130a9168444464dfb2c84cf38adaec4d Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Sat, 4 Jan 2025 23:20:57 +0100 Subject: [PATCH 34/36] docs: improve customization wordings --- docs/guides/setup/Customization.md | 50 ++++++++++++++++-------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/docs/guides/setup/Customization.md b/docs/guides/setup/Customization.md index 6cc1f7733..9a12153ce 100644 --- a/docs/guides/setup/Customization.md +++ b/docs/guides/setup/Customization.md @@ -1,11 +1,11 @@ # Setup Customization It's possible to customize the native setup of the library. These props/options must be applied differently, based on -your project: +the project setup. ## Expo -If you are using Expo, simply add the customizations in the projects `app.json` or `app.config.{js,ts}`. Here is an +When using Expo, simply add the customizations in the projects `app.json` or `app.config.{js,ts}`. Here is an example customization for in a `app.config.ts`: ```ts @@ -18,10 +18,10 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ "@maplibre/maplibre-react-native", { android: { - // Android Plugin Props + nativeVersion: "x.x.x", }, ios: { - // iOS Plugin Props + nativeVersion: "x.x.x", }, } as MapLibrePluginProps, ], @@ -31,7 +31,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ ## React Native -If you are using React Native, the customizations have to be applied differently for each platform. +When using React Native, the customizations have to be applied differently for each platform. On Android they are set in the `gradle.properties`, each of them prefixed with `org.maplibre.reactnative`. Example: @@ -43,24 +43,28 @@ On iOS global variables in the `Podfile` are used, prefixed with `$MLRN`. ```diff + $MLRN_NATIVE_VERSION="x.x.x" + +target "AppName" do ``` -## Android +## Available Customizations + +### Android -| Plugin Prop | `gradle.properties` Key | Type | Description | -| ------------------------------------------- | ------------------------------------------------------------ | ----------------------- | ----------------------------------------------------------------------------------------------------------- | -| `android.nativeVersion` | `org.maplibre.reactnative.nativeVersion` | `VersionString` | Version for [`org.maplibre.gl:android-sdk`](https://mvnrepository.com/artifact/org.maplibre.gl/android-sdk) | -| `android.pluginVersion` | `org.maplibre.reactnative.pluginVersion` | `VersionString` | Version for `org.maplibre.gl:android-plugin-*-v9` | -| `android.turfVersion` | `org.maplibre.reactnative.turfVersion` | `VersionString` | Version for `org.maplibre.gl:android-sdk-turf` | -| `android.okhttpVersion` | `org.maplibre.reactnative.okhttpVersion` | `VersionString` | Version for `com.squareup.okhttp3:okhttp` | -| `android.locationEngine` | `org.maplibre.reactnative.locationEngine` | `"default" \| "google"` | [Location engine to be used](#choosing-a-location-engine) | -| `android.googlePlayServicesLocationVersion` | `org.maplibre.reactnative.googlePlayServicesLocationVersion` | `VersionString` | Version for `com.google.android.gms:play-services-location`, only used with `locationEngine: "google"` | +| Prop Key | Type | Description | +| ----------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------- | +| `nativeVersion` | `VersionString` | Version for [`org.maplibre.gl:android-sdk`](https://mvnrepository.com/artifact/org.maplibre.gl/android-sdk) | +| `pluginVersion` | `VersionString` | Version for `org.maplibre.gl:android-plugin-*-v9` | +| `turfVersion` | `VersionString` | Version for `org.maplibre.gl:android-sdk-turf` | +| `okhttpVersion` | `VersionString` | Version for `com.squareup.okhttp3:okhttp` | +| `locationEngine` | `"default" \| "google"` | [Location engine to be used](#location-engine) | +| `googlePlayServicesLocationVersion` | `VersionString` | Version for `com.google.android.gms:play-services-location`, only used with `locationEngine: "google"` | For default values see [`gradle.properties` of the library](/android/gradle.properties). -### Choosing a Location Engine +#### Location Engine -You can choose between the following two location engines on Android: +Two location engines are available on Android: - `default` - Default location engine provided by the device @@ -72,18 +76,18 @@ You can choose between the following two location engines on Android: - Works with emulator - Not F-Droid compatible -## iOS +### iOS -| Plugin Prop | `Podfile` Global Variable | Type | Description | -| ------------------- | ------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------- | -| `ios.nativeVersion` | `$MLRN_NATIVE_VERSION` | `VersionString` | Version for [`maplibre-gl-native-distribution`](https://github.com/maplibre/maplibre-gl-native-distribution/releases) | -| `ios.spmSpec` | `$MLRN_SPM_SPEC` | `string` | [Swift Package Manager Spec](#setting-a-spm-spec) | +| Prop Key | `Podfile` Global Variable | Type | Description | +| --------------- | ------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------- | +| `nativeVersion` | `$MLRN_NATIVE_VERSION` | `VersionString` | Version for [`maplibre-gl-native-distribution`](https://github.com/maplibre/maplibre-gl-native-distribution/releases) | +| `spmSpec` | `$MLRN_SPM_SPEC` | `string` | [Swift Package Manager Spec](#spm-spec) | For default values see [`maplibre-react-native.podspec` of the library](/maplibre-react-native.podspec). -### Setting a SPM Spec +#### SPM Spec -Setting a Swift Package Manager Spec allows more customization thn +Setting a Swift Package Manager Spec allows further customization over setting the native version: ```rb $MLRN_SPM_SPEC = { From d29f6526c15612a944c7fc1bca7046a1ffa3845e Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Sat, 4 Jan 2025 23:22:51 +0100 Subject: [PATCH 35/36] docs: customization wording --- docs/guides/setup/Customization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/setup/Customization.md b/docs/guides/setup/Customization.md index 9a12153ce..9067b3cd5 100644 --- a/docs/guides/setup/Customization.md +++ b/docs/guides/setup/Customization.md @@ -1,6 +1,6 @@ # Setup Customization -It's possible to customize the native setup of the library. These props/options must be applied differently, based on +It's possible to customize the native setup of the library. These props must be applied differently, based on the project setup. ## Expo From af57075ead8c0c1494a98028c653ac607fa43fda Mon Sep 17 00:00:00 2001 From: Kilian Finger Date: Fri, 10 Jan 2025 08:48:07 +0100 Subject: [PATCH 36/36] feat: upgrade MapLibre Native iOS to v6.10.0 --- docs/guides/setup/Getting-Started.md | 2 +- maplibre-react-native.podspec | 2 +- .../ios/MapLibreReactNativeExample.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/guides/setup/Getting-Started.md b/docs/guides/setup/Getting-Started.md index 51825727f..182c7bb57 100644 --- a/docs/guides/setup/Getting-Started.md +++ b/docs/guides/setup/Getting-Started.md @@ -13,7 +13,7 @@ This package wraps MapLibre Native for Android and iOS, these are the currently
iOS
- 6.9.0 + 6.10.0
diff --git a/maplibre-react-native.podspec b/maplibre-react-native.podspec index 3bb02c6b9..9ca2c7337 100644 --- a/maplibre-react-native.podspec +++ b/maplibre-react-native.podspec @@ -3,7 +3,7 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) # Global Variable Defaults -$MLRN_NATIVE_VERSION ||= "6.9.0" +$MLRN_NATIVE_VERSION ||= "6.10.0" $MLRN_SPM_SPEC ||= { url: "https://github.com/maplibre/maplibre-gl-native-distribution", requirement: { diff --git a/packages/react-native-app/ios/MapLibreReactNativeExample.xcodeproj/project.pbxproj b/packages/react-native-app/ios/MapLibreReactNativeExample.xcodeproj/project.pbxproj index 542524186..ef422421a 100644 --- a/packages/react-native-app/ios/MapLibreReactNativeExample.xcodeproj/project.pbxproj +++ b/packages/react-native-app/ios/MapLibreReactNativeExample.xcodeproj/project.pbxproj @@ -502,7 +502,7 @@ repositoryURL = "https://github.com/maplibre/maplibre-gl-native-distribution"; requirement = { kind = exactVersion; - version = 6.9.0; + version = 6.10.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/packages/react-native-app/ios/MapLibreReactNativeExample.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/react-native-app/ios/MapLibreReactNativeExample.xcworkspace/xcshareddata/swiftpm/Package.resolved index 2992eff4c..980f2224a 100644 --- a/packages/react-native-app/ios/MapLibreReactNativeExample.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/packages/react-native-app/ios/MapLibreReactNativeExample.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/maplibre/maplibre-gl-native-distribution", "state" : { - "revision" : "93cb32c95b48f62edb27c75792038849cdfdaa35", - "version" : "6.9.0" + "revision" : "3615e3cc81b09b78b58b183660815b0f36107b3b", + "version" : "6.10.0" } } ],