Skip to content

[INTER-2397] SPM Support - #269

Merged
TheUnderScorer merged 31 commits into
mainfrom
feature/INTER-2397-spm-support
Sep 22, 2026
Merged

TheUnderScorer merged 31 commits into
mainfrom
feature/INTER-2397-spm-support

Conversation

@TheUnderScorer

@TheUnderScorer TheUnderScorer commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

SPM support

Experimental Swift Package Manager autolinking for iOS, targeting RN 0.87's react-native spm workflow.

#270 introduces a bare RN app that can be used for testing the SPM support.

SPM manifest (sdk/Package.swift)

react-native spm scaffold skips this library because ios/ mixes Swift and Objective-C++, and a single SwiftPM target can't contain both. The manifest is hand-written with a two-target split:

  • RNFingerprintjsProSwift - pure Swift, all business logic and Fingerprint iOS SDK calls.
  • RNFingerprintjsPro - thin ObjC++ glue that registers the TurboModule and returns the codegen'd C++ JSI spec. Depends on the Swift target.

Module name pinning (sdk/react-native.config.js)

spm.name: 'RNFingerprintjsPro' pins the autolinker's module name to match the CocoaPods pod and native module name. Without it the autolinker derives a different name from the npm package.

Podspec (sdk/RNFingerprintjsPro.podspec)

  • Excluded react-native-spm-prefix.h from CocoaPods sources (SwiftPM-only shim).

iOS source

  • RNFingerprintjsPro.swift: added #if RCT_SPM / import React so the Swift target resolves RCTPromiseResolveBlock/RCTPromiseRejectBlock without a bridging header.
  • Added react-native-spm-prefix.h, a minimal prefix header that imports Foundation for the ObjC++ glue target. Force-included via -include in Package.swift; excluded from the podspec.

Strict TypeScript API

Replaced the internal react-native/Libraries/Types/CodegenTypes import path with the public CodegenTypes namespace from react-native:

  • UnsafeObject -> CodegenTypes.UnsafeObject
  • Double -> CodegenTypes.Double

sdk/tsconfig.sdk.json now uses "moduleResolution": "Bundler" to resolve RN's exports map. eslint.config.mjs points SDK files at that tsconfig.

Minimum version bump

RN minimum raised from >=0.79 to >=0.80 (Expo >=54.0.0). The Strict TypeScript API isn't available before 0.80. The E2E CI matrix and compatibility.mjs drop the 0.79/Expo 53 row.

@TheUnderScorer TheUnderScorer self-assigned this Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report

St.❔
Category Percentage Covered / Total
🟢 Statements 91.74% 222/242
🟢 Branches 89.77% 193/215
🟢 Functions 85.19% 46/54
🟢 Lines 91.34% 211/231

Test suite run success

85 tests passing in 5 suites.

Report generated by 🧪jest coverage report action from 6f19b44

Show full coverage report
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🟢 All files 91.73 89.76 85.18 91.34
🟢  src 91.73 89.76 85.18 91.34
🟢   FingerprintClient.ts 100 92.85 100 100 21,53
🔴   FingerprintClient.web.ts 0 0 0 0 5-58
🟡   FingerprintContext.ts 66.66 100 0 66.66 6
🟢   FingerprintProvider.tsx 100 100 100 100
🟡   errors.ts 75 50 50 75 3,121
🟢   getOptionsCacheKey.ts 84 83.33 100 83.33 10,33,43,52
🔴   index.ts 0 0 0 0
🔴   types.ts 0 0 0 0
🟢   unwrapError.ts 100 88.46 100 100 18-19,40
🟢   unwrapError.web.ts 100 100 100 100
🔴   useFingerprint.ts 0 0 0 0 24-28
🟢   usePromiseStore.ts 100 100 100 100
🟢   useVisitorData.ts 95.91 85.71 90 95.74 166-167
🟢   utils.ts 100 100 100 100
🔴  src/specs 0 0 0 0
🔴   NativeRNFingerprintjsPro.ts 0 0 0 0

@TheUnderScorer
TheUnderScorer force-pushed the feature/INTER-2397-spm-support branch from 83eeb67 to a89648b Compare September 4, 2026 10:41
@TheUnderScorer
TheUnderScorer force-pushed the feature/INTER-2397-spm-support branch 4 times, most recently from f53e561 to 65d7264 Compare September 7, 2026 10:38
@TheUnderScorer
TheUnderScorer force-pushed the feature/INTER-2397-spm-support branch from 924d36b to b61e973 Compare September 8, 2026 10:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The central SPM integration lacks a consuming Xcode build, and the CocoaPods dependency constraint is unintentionally narrowed.

Pull request overview

Adds experimental Swift Package Manager support for React Native 0.87 while adopting React Native’s strict TypeScript API and raising minimum supported versions.

Changes:

  • Adds a two-target SwiftPM manifest and autolinking configuration.
  • Migrates codegen types to the public CodegenTypes API.
  • Updates dependencies, compatibility matrices, documentation, and release notes.
File summaries
File Description
sdk/tsup/esbuild-plugin-license.d.ts Adds plugin typings.
sdk/tsconfig.sdk.json Enables bundler resolution for SDK sources.
sdk/tsconfig.json Enables bundler resolution for project references.
sdk/src/specs/NativeRNFingerprintjsPro.ts Uses public codegen types.
sdk/src/FingerprintClient.ts Uses public UnsafeObject type.
sdk/RNFingerprintjsPro.podspec Excludes the SPM shim and adjusts dependency constraints.
sdk/README.md Updates requirements and formatting.
sdk/react-native.config.js Pins the SPM module name.
sdk/Package.swift Defines the SwiftPM package and targets.
sdk/package.json Publishes SPM files and updates dependencies.
sdk/ios/turbomodule/RNFingerprintjsPro.mm Adjusts TurboModule glue imports.
sdk/ios/turbomodule/react-native-spm-prefix.h Adds the SPM Objective-C prefix header.
sdk/ios/RNFingerprintjsPro.swift Imports React for SwiftPM builds.
README.md Updates minimum supported versions.
pnpm-lock.yaml Locks updated React Native dependencies.
eslint.config.mjs Uses the SDK-specific TypeScript project.
e2e-app/scripts/compatibility.mjs Drops React Native 0.79 compatibility data.
contributing.md Updates the untested-version list.
.github/workflows/e2e-tests.yml Removes React Native 0.79 from CI.
.changeset/spm-support.md Documents experimental SPM support.
.changeset/new-architecture-support.md Updates migration requirements.
Review details
  • Files reviewed: 19/21 changed files
  • Comments generated: 4
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread sdk/Package.swift
Comment thread sdk/RNFingerprintjsPro.podspec Outdated
Comment thread .changeset/new-architecture-support.md Outdated
Comment thread .changeset/spm-support.md Outdated
@TheUnderScorer TheUnderScorer changed the title [WIP][INTER-2397] SPM Support [INTER-2397] SPM Support Sep 8, 2026
TheUnderScorer and others added 2 commits September 8, 2026 13:15
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@TheUnderScorer
TheUnderScorer marked this pull request as ready for review September 8, 2026 11:29
@TheUnderScorer
TheUnderScorer removed this pull request from stack #268 September 10, 2026 10:17
…spm-support

# Conflicts:
#	sdk/src/FingerprintClient.ts
@TheUnderScorer
TheUnderScorer added this pull request to stack #271 September 10, 2026 10:21
@TheUnderScorer
TheUnderScorer requested review from a team and kevinmia as code owners September 18, 2026 08:37
Comment thread sdk/ios/RNFingerprintjsPro.mm
Comment thread sdk/Package.swift Outdated
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Following releases will be created using changesets from this PR:

@fingerprintjs/fingerprintjs-pro-react-native@4.0.0-rc.1

Major Changes

  • Migrated the SDK to Fingerprint API v4 and realigned the public API with @fingerprint/react. This is a breaking change on every platform (web, iOS, Android).

    Renamed API

    • FingerprintJsProProvider → FingerprintProvider.
    • FingerprintJsProAgent (constructed with new) → start(options), returning a client with a single get(options) method (getVisitorId/getVisitorData are removed).
    • Added useFingerprint() to read the client from context.

    Single options object

    • getData(tags, linkedId, options) → getData({ tags, linkedId, timeout }).
    • useVisitorData now returns a discriminated union (data/isLoading/isFetched/error), and getData always throws on error. The throwOnError option is removed. immediate defaults to false.

    Grouped provider/start options

    • Platform-only options are nested: android (allowUseOfLocationData, locationTimeoutMillis), ios (allowUseOfLocationData), web (storageKeyPrefix, urlHashing, cache). locationTimeoutMillisAndroid moves to android.locationTimeoutMillis.
    • endpointUrl + fallbackEndpointUrls are replaced by a single endpoints (string or string[]).

    snake_case response

    • The response is now the flat, snake_case FingerprintResponse (visitor_id, event_id, suspect_score?, sealed_result), matching the Server API v4 and JS agent. requestId is replaced by event_id, confidence is removed, and suspect_score is a new optional Smart Signals value; sealedResult is renamed to sealed_result. The nested extended fields (ipLocation, firstSeenAt, etc.) and the extendedResponseFormat option are removed (v4 always returns the flat format).

    Single error type

    • The ~28 error classes are replaced by a single FingerprintError ({ name, code, event_id }) plus an isFingerprintError type guard. Discriminate on error.code (e.g. too_many_requests).
    • Every network failure reports code: 'network_error' on all platforms. The platform-specific network_unavailable (Android), network_connection and network_abort (web) codes are not emitted.

    Web

    • The web implementation now uses @fingerprint/agent (v4) instead of @fingerprintjs/fingerprintjs-pro-spa. Install @fingerprint/agent as the web peer dependency.
    • ️Caching is available only on web and is disabled by default. To enable caching on web, pass the JavaScript agent cache option:
      <FingerprintProvider
        apiKey={'your-fpjs-public-api-key'}
        region={'eu'}
        web={{cache: {storage: 'sessionStorage', duration: 'optimize-cost'}}}
      >
        <App />
      </FingerprintProvider>

    iOS:

    • The new version floor for iOS is 15.1.

    Android:

    • The new version floor for Android is Android 7.0 (API level 24+).

    Migration

    Provider

    - import { FingerprintJsProProvider } from '@fingerprintjs/fingerprintjs-pro-react-native'
    + import { FingerprintProvider } from '@fingerprintjs/fingerprintjs-pro-react-native'
    
    - <FingerprintJsProProvider apiKey="PUBLIC_API_KEY" region="eu">
    + <FingerprintProvider apiKey="PUBLIC_API_KEY" region="eu">
        <App />
    - </FingerprintJsProProvider>
    + </FingerprintProvider>

    Hook (useVisitorData)

    - const { isLoading, error, data, getData } = useVisitorData()
    + const { isLoading, isFetched, error, data, getData } = useVisitorData()
    
      // positional args + opt-in throwing → single options object, always throws
    - await getData({ userAction: 'login' }, 'user_1234', { timeout: 5000, throwOnError: true })
    + await getData({ tags: { userAction: 'login' }, linkedId: 'user_1234', timeout: 5000 })
    
    - data?.visitorId
    - data?.confidence.score
    + data?.visitor_id
    + data?.suspect_score

    Imperative client

    - import { FingerprintJsProAgent } from '@fingerprintjs/fingerprintjs-pro-react-native'
    + import { start } from '@fingerprintjs/fingerprintjs-pro-react-native'
    
    - const client = new FingerprintJsProAgent({ apiKey: 'PUBLIC_API_KEY', region: 'eu' })
    - const visitorId = await client.getVisitorId()
    - const data = await client.getVisitorData()
    + const fp = start({ apiKey: 'PUBLIC_API_KEY', region: 'eu' })
    + const result = await fp.get()
    + result.visitor_id

    Provider / start options

      {
        apiKey: 'PUBLIC_API_KEY',
        region: 'eu',
    -   endpointUrl: 'https://metrics.example.com',
    -   fallbackEndpointUrls: ['https://metrics2.example.com'],
    +   endpoints: ['https://metrics.example.com', 'https://metrics2.example.com'],
    -   extendedResponseFormat: true,
    -   allowUseOfLocationData: true,
    -   locationTimeoutMillisAndroid: 5000,
    +   android: { allowUseOfLocationData: true, locationTimeoutMillis: 5000 },
    +   ios: { allowUseOfLocationData: true },
      }

    Errors

    - import { TooManyRequestError } from '@fingerprintjs/fingerprintjs-pro-react-native'
    + import { isFingerprintError } from '@fingerprintjs/fingerprintjs-pro-react-native'
    
      try {
        await fp.get()
      } catch (error) {
    -   if (error instanceof TooManyRequestError) {
    +   if (isFingerprintError(error) && error.code === 'too_many_requests') {
          // handle rate limiting
        }
      }

    Web peer dependency

    - npm install @fingerprintjs/fingerprintjs-pro-spa
    + npm install @fingerprint/agent

    --- (64f1dfb)

Minor Changes

  • Added experimental support for Swift Package Manager. The library now ships with its own Package.swift.

    If you are on React Native >= 0.87, you can run:

    cd ios
    # deintegrate will remove CocoaPods from your project
    npx react-native spm --deintegrate

    to consume the library via Swift Package Manager. (7af274f)

@kevinmia kevinmia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm! Let me know if re-approval is needed later

Base automatically changed from feature/INTER-2190-v4-support to main September 22, 2026 18:57
@TheUnderScorer
TheUnderScorer merged commit e719849 into main Sep 22, 2026
45 of 46 checks passed
@TheUnderScorer
TheUnderScorer deleted the feature/INTER-2397-spm-support branch September 22, 2026 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants