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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions .github/workflows/ios-spm-build-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: Build iOS (SPM)

on:
pull_request:
paths:
- packages/react-native-gesture-handler/Package.swift
- packages/react-native-gesture-handler/package.json
- packages/react-native-gesture-handler/react-native.config.js
- packages/react-native-gesture-handler/package.json
- packages/react-native-gesture-handler/RNGestureHandler.podspec
- packages/react-native-gesture-handler/apple/**
- packages/react-native-gesture-handler/shared/**
- packages/react-native-gesture-handler/src/**
- rnrepo.config.json
- yarn.lock
- '!packages/react-native-gesture-handler/src/**/*.test.*'
- .github/workflows/ios-spm-build-test.yml
push:
branches:
- main
workflow_dispatch:

concurrency:
group: ios-spm-${{ github.ref }}
cancel-in-progress: true

jobs:
ios-spm:
if: github.repository == 'software-mansion/react-native-gesture-handler'

runs-on: macos-26
timeout-minutes: 60

strategy:
fail-fast: false
matrix:
worklets: [false, true]

env:
APP_NAME: GHSpmTest
WORKLETS_VERSION: 0.13.0-nightly-20260913-5336bb7f4

steps:
- name: checkout
uses: actions/checkout@v4

- name: Select Xcode
run: |
XCODE_APP="/Applications/Xcode_26.4.1.app"
if [ ! -d "$XCODE_APP" ]; then
echo "Xcode 26.4.1 is not installed on this runner. Available:" >&2
ls -d /Applications/Xcode*.app >&2
fi
echo "Using $XCODE_APP"
echo "DEVELOPER_DIR=$XCODE_APP/Contents/Developer" >> "$GITHUB_ENV"
DEVELOPER_DIR="$XCODE_APP/Contents/Developer" xcodebuild -version

- name: Use Node.js 24
uses: actions/setup-node@v6
with:
node-version: 24
cache: yarn

- name: Read React Native version
id: rn-version
run: |
RN_VERSION=$(node -p "require('./packages/react-native-gesture-handler/package.json').devDependencies['react-native']")
echo "version=$RN_VERSION" >> "$GITHUB_OUTPUT"

- name: Install monorepo dependencies
run: yarn --immutable

- name: Build and pack library
working-directory: packages/react-native-gesture-handler
run: |
yarn build
mkdir -p /tmp/packs
npm pack --pack-destination /tmp/packs
echo "GH_TARBALL=$(ls /tmp/packs/react-native-gesture-handler-*.tgz)" >> "$GITHUB_ENV"

- name: Create blank RN app
run: |
npx @react-native-community/cli@latest init "${{ env.APP_NAME }}" \
--version "${{ steps.rn-version.outputs.version }}"

- name: Install GH from tarball
working-directory: ${{ env.APP_NAME }}
run: npm install "${{ env.GH_TARBALL }}"

- name: Install react-native-worklets
if: matrix.worklets
working-directory: ${{ env.APP_NAME }}
run: npm install "react-native-worklets@${{ env.WORKLETS_VERSION }}"

- name: SPM scaffold
working-directory: ${{ env.APP_NAME }}
run: npx react-native spm scaffold --deintegrate --yes

- name: Assert worklets are linked into RNGestureHandler
if: matrix.worklets
working-directory: ${{ env.APP_NAME }}
run: |
swift package --package-path ios/build/generated/autolinking show-dependencies --format dot \
| grep 'libs/RNGestureHandler" -> ".*libs/RNWorklets"'

- name: Build iOS (SPM)
working-directory: ${{ env.APP_NAME }}/ios
run: |
xcodebuild build \
-project "${{ env.APP_NAME }}.xcodeproj" \
-scheme "${{ env.APP_NAME }}" \
-configuration Debug \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-quiet
5 changes: 5 additions & 0 deletions packages/react-native-gesture-handler/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ jsconfig.json
!.yarn/versions


# Swift Package Manager
.build/
.swiftpm/
Package.resolved

# TS
dist/

Expand Down
84 changes: 84 additions & 0 deletions packages/react-native-gesture-handler/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// swift-tools-version: 6.0

import Foundation
import PackageDescription

let packageDirectory = Context.packageDirectory

func rnWorkletsPackageExists() -> Bool {
let url = URL(fileURLWithPath: packageDirectory)
.deletingLastPathComponent()
.appendingPathComponent("RNWorklets/Package.swift")
return FileManager.default.fileExists(atPath: url.path)
}
Comment thread
m-bert marked this conversation as resolved.

let useWorklets = rnWorkletsPackageExists()
Comment thread
m-bert marked this conversation as resolved.

let reactHeaders: [Target.Dependency] = [
.product(name: "ReactHeaders", package: "ReactNative"),
.product(name: "ReactNativeHeaders", package: "ReactNative"),
.product(name: "ReactNativeDependenciesHeaders", package: "ReactNative"),
.product(name: "ReactAppHeaders", package: "React-GeneratedCode"),
]

var packageDependencies: [Package.Dependency] = [
.package(name: "ReactNative", path: "../../../../xcframeworks"),
.package(name: "React-GeneratedCode", path: "../../../ios"),
]

var targetDependencies: [Target.Dependency] = reactHeaders

if useWorklets {
packageDependencies.append(.package(name: "RNWorklets", path: "../RNWorklets"))
targetDependencies.append(.product(name: "RNWorklets", package: "RNWorklets"))
}

var cSettings: [CSetting] = [
.headerSearchPath("apple"),
.headerSearchPath("shared/runtime"),
.headerSearchPath("shared/shadowNodes"),
.headerSearchPath(
"shared/shadowNodes/react/renderer/components/rngesturehandler_codegen"),
.define("DEBUG", .when(configuration: .debug)),
.define("NDEBUG", .when(configuration: .release)),
]

var cxxSettings: [CXXSetting] = [
.headerSearchPath("apple"),
.headerSearchPath("shared/runtime"),
.headerSearchPath("shared/shadowNodes"),
.headerSearchPath(
"shared/shadowNodes/react/renderer/components/rngesturehandler_codegen"),
.define("DEBUG", .when(configuration: .debug)),
.define("NDEBUG", .when(configuration: .release)),
]

if useWorklets {
cxxSettings.append(.define("RNGH_USE_WORKLETS", to: "1"))
}

let package = Package(
Comment thread
kacperzolkiewski marked this conversation as resolved.
name: "RNGestureHandler",
platforms: [.iOS(.v15)],
products: [
.library(name: "RNGestureHandler", targets: ["RNGestureHandler"]),
],
dependencies: packageDependencies,
targets: [
.target(
name: "RNGestureHandler",
dependencies: targetDependencies,
path: ".",
exclude: ["apple/RNGestureHandler.xcodeproj"],
sources: ["apple", "shared"],
cSettings: cSettings,
cxxSettings: cxxSettings,
linkerSettings: [
.linkedFramework("UIKit"),
.linkedFramework("Foundation"),
.linkedFramework("CoreGraphics"),
]
),
],
cxxLanguageStandard: .cxx20
)
4 changes: 4 additions & 0 deletions packages/react-native-gesture-handler/apple/RNGHStylusData.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
#ifndef RNGHStylusData_h
#define RNGHStylusData_h

#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>
#import <math.h>

@interface RNGHStylusData : NSObject

@property (atomic, assign) double tiltX;
Expand Down
3 changes: 3 additions & 0 deletions packages/react-native-gesture-handler/include/.gitkeep
Comment thread
m-bert marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Gesture Handler exposes no SPM-facing headers, so Package.swift omits publicHeadersPath.
// Without publicHeadersPath, SPM defaults to the "include/" directory.
// We need to provide the "include/" directory so the iOS SPM build succeeds.
2 changes: 2 additions & 0 deletions packages/react-native-gesture-handler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"module": "lib/module/index.js",
"types": "lib/typescript/index.d.ts",
"files": [
"Package.swift",
"include/",
"src",
"lib",
"!**/__tests__",
Expand Down
3 changes: 3 additions & 0 deletions packages/react-native-gesture-handler/react-native.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
module.exports = {
spm: {
name: 'RNGestureHandler',
},
dependency: {
platforms: {
android: {
Expand Down
Loading