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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ jobs:
yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"

build-ios:
runs-on: macos-14
runs-on: macos-15
env:
TURBO_CACHE_DIR: .turbo/ios
steps:
Expand Down
63 changes: 63 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// swift-tools-version: 6.0
// Experimental: for React Native's Swift Package Manager mode (React Native 0.87 or later), which
// React Native itself marks as not for production. CocoaPods apps use ReactNativePortals.podspec
// and never read this file.
//
// React Native resolves this manifest through a symlink at
// `<app>/ios/build/generated/autolinking/libs/ReactNativePortals`, and SwiftPM evaluates relative
// package paths against that symlink, so `../../../../xcframeworks` is the app's prebuilt React
// Native package. The name is pinned by `spm.name` in react-native.config.js.

import PackageDescription

let package = Package(
name: "ReactNativePortals",
platforms: [.iOS(.v15)],
products: [
.library(name: "ReactNativePortals", targets: ["ReactNativePortals"])
],
dependencies: [
.package(name: "ReactNative", path: "../../../../xcframeworks"),
.package(url: "https://github.com/ionic-team/ionic-portals-ios", "0.13.1"..<"0.14.0"),
.package(url: "https://github.com/ionic-team/ionic-live-updates-releases", "0.5.7"..<"0.6.0"),
// Declared directly because the Swift sources import Capacitor. Same URL Capacitor plugins
// use, so SwiftPM resolves a single Capacitor for the whole app.
.package(url: "https://github.com/ionic-team/capacitor-swift-pm", "8.0.0"..<"9.0.0")
],
targets: [
// SwiftPM cannot mix Swift and Objective-C++ in one target, so the Swift module compiles on
// its own. The .mm files only reference its classes by Objective-C runtime name.
.target(
name: "ReactNativePortalsSwift",
dependencies: [
.product(name: "ReactHeaders", package: "ReactNative"),
.product(name: "IonicPortals", package: "ionic-portals-ios"),
.product(name: "IonicLiveUpdates", package: "ionic-live-updates-releases"),
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm")
],
path: "ios/Swift",
swiftSettings: [.swiftLanguageMode(.v5)]
),
.target(
name: "ReactNativePortals",
dependencies: [
"ReactNativePortalsSwift",
.product(name: "ReactHeaders", package: "ReactNative"),
.product(name: "ReactNativeHeaders", package: "ReactNative"),
.product(name: "ReactNativeDependenciesHeaders", package: "ReactNative")
],
path: "ios",
exclude: ["Swift"],
publicHeadersPath: "include",
cSettings: [.define("RCT_NEW_ARCH_ENABLED", to: "1")],
cxxSettings: [
.define("RCT_NEW_ARCH_ENABLED", to: "1"),
// Must match the prebuilt React framework's configuration-gated C++ ABI.
.define("DEBUG", .when(configuration: .debug)),
.define("NDEBUG", .when(configuration: .release))
]
)
],
cxxLanguageStandard: .cxx20
)
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ Ionic Portals is a supercharged native Web View component for React Native that

See our docs to [get started with Portals](https://ionic.io/docs/portals/getting-started/guide).

## Experimental: Swift Package Manager (React Native 0.87+)

React Native 0.87 added an opt-in mode where an iOS app uses Swift Package Manager instead of CocoaPods (`npx react-native spm`). This package ships a `Package.swift` for that mode. React Native marks the mode as experimental and not for production, and CocoaPods remains the supported integration.

In SwiftPM mode, Portals depends on Capacitor through [`capacitor-swift-pm`](https://github.com/ionic-team/capacitor-swift-pm). Add any Capacitor plugins your portals use through SwiftPM too, so the app links a single copy of Capacitor. If a plugin comes from CocoaPods while Portals comes from SwiftPM, the app links two copies of Capacitor and Portals skips the plugin with a `not a CAPPlugin subclass` message in the Xcode console.

React Native autolinks Capacitor plugins installed from npm, but it derives the Swift package name from the npm name (`@capacitor/preferences` becomes `Preferences`). Capacitor plugins name their package after the podspec (`CapacitorPreferences`), so resolution fails with `product 'Preferences' ... not found`. Until the plugin ships its own `react-native.config.js`, add one to it with [`patch-package`](https://github.com/ds300/patch-package), using the product name from the plugin's `Package.swift`:

```js
// node_modules/@capacitor/preferences/react-native.config.js
module.exports = { spm: { name: 'CapacitorPreferences' } };
```

## Registration

The Ionic Portals library for React Native requires a license key to use. Once you have integrated Portals into your project, login to your ionic account to get a key. See our doc on [how to register for free and get your Portals license key](https://ionic.io/docs/portals/how-to/get-a-product-key) and refer to the [React Native](https://ionic.io/docs/portals/getting-started/react-native) getting started guides to see where to add your key.
Expand Down
31 changes: 3 additions & 28 deletions ReactNativePortals.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require "json"

package = JSON.parse(File.read(File.join(__dir__, "package.json")))
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'

Pod::Spec.new do |s|
s.name = "ReactNativePortals"
Expand All @@ -16,31 +15,7 @@ Pod::Spec.new do |s|

s.source_files = "ios/**/*.{h,m,mm,swift}"

# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
if respond_to?(:install_modules_dependencies, true)
s.dependency 'IonicPortals', '~> 0.13.0'
s.dependency 'IonicLiveUpdates', '~> 0.5.7'
install_modules_dependencies(s)

else
s.dependency "React-Core"
s.dependency 'IonicPortals', '~> 0.13.0'
s.dependency 'IonicLiveUpdates', '~> 0.5.7'

# Don't install the dependencies when we run `pod install` in the old architecture.
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
s.pod_target_xcconfig = {
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
}
s.dependency "React-Codegen"
s.dependency "RCT-Folly"
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core"
end
end
s.dependency 'IonicPortals', '~> 0.13.1'
s.dependency 'IonicLiveUpdates', '~> 0.5.7'
install_modules_dependencies(s)
end
8 changes: 4 additions & 4 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ buildscript {
}

dependencies {
classpath "com.android.tools.build:gradle:8.13.0"
classpath "com.android.tools.build:gradle:9.2.1"
// noinspection DifferentKotlinGradleVersion
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
Expand Down Expand Up @@ -77,8 +77,8 @@ android {
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}

Expand All @@ -95,7 +95,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-android"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "io.ionic:portals:0.13.0-rn.1"
implementation "io.ionic:portals:0.13.1"
implementation "io.ionic:liveupdates:0.5.+"
}

6 changes: 3 additions & 3 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PortalsReactNative_kotlinVersion=2.1.20
PortalsReactNative_kotlinVersion=2.2.0
PortalsReactNative_minSdkVersion=24
PortalsReactNative_targetSdkVersion=36
PortalsReactNative_compileSdkVersion=36
PortalsReactNative_ndkversion=21.4.7075529
PortalsReactNative_compileSdkVersion=37
PortalsReactNative_ndkversion=27.1.12297006
android.useAndroidX=true
1 change: 1 addition & 0 deletions example/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ ruby ">= 2.6.10"
# Exclude problematic versions of cocoapods and activesupport that causes build failures.
gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
gem 'nkf'
6 changes: 3 additions & 3 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ react {

/* Variants */
// The list of variants to that are debuggable. For those we're going to
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
// skip the bundling of the JS bundle and the assets. Default is "debug", "debugOptimized".
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
// debuggableVariants = ["liteDebug", "prodDebug"]
// debuggableVariants = ["liteDebug", "liteDebugOptimized", "prodDebug", "prodDebugOptimized"]

/* Autolinking */
autolinkLibrariesWithApp()
Expand Down Expand Up @@ -104,7 +104,7 @@ android {
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
}
}
}
Expand Down
9 changes: 0 additions & 9 deletions example/android/app/src/debug/AndroidManifest.xml

This file was deleted.

1 change: 1 addition & 0 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="${usesCleartextTraffic}"
android:supportsRtl="true">
<activity
android:name=".MainActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,24 @@ import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader

class MainApplication : Application(), ReactApplication {

override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
}

override fun getJSMainModuleName(): String = "index"

override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG

override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}

override val reactHost: ReactHost
get() = getDefaultReactHost(applicationContext, reactNativeHost)
override val reactHost: ReactHost by lazy {
getDefaultReactHost(
context = applicationContext,
packageList =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
},
)
}

override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
}
loadReactNative(this)
}
}
10 changes: 5 additions & 5 deletions example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
buildscript {
ext {
buildToolsVersion = "34.0.0"
buildToolsVersion = "37.0.0"
minSdkVersion = 24
compileSdkVersion = 36
compileSdkVersion = 37
targetSdkVersion = 36
ndkVersion = "26.1.10909125"
kotlinVersion = "2.1.20"
ndkVersion = "27.1.12297006"
kotlinVersion = "2.2.0"
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:8.13.0")
classpath("com.android.tools.build:gradle")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
}
Expand Down
12 changes: 11 additions & 1 deletion example/android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,18 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
newArchEnabled=true

# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
hermesEnabled=true

# Use this property to enable edge-to-edge display support.
# This allows your app to draw behind system bars for an immersive UI.
# Note: Only works with ReactActivity and should not be used with custom Activity.
edgeToEdgeEnabled=true

# Opt out of built-in kotlin and new DSL behavior that ships with AGP 9.
# Starting from AGP 10.x these opt outs will be removed.
android.builtInKotlin=false
android.newDsl=false
2 changes: 1 addition & 1 deletion example/android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
1 change: 0 additions & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ target 'PortalsReactNativeExample' do
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],
Expand Down
Loading
Loading