Skip to content

Commit 3e76ae0

Browse files
feat(RN): Add wiring for protocol events - add ec.start (#170)
### What changes are you making? Part of the scope of this PR is to add a translation layer between the the core native SDKs and the React native JavaScript runtime. The models that are generated for Swift and Kotlin automatically camel case at their encode decode boundary, which means the raw json is read as snake case and the Swift and Kotlin properties are then turned into camel case when they are turned back into raw json, they're back in snake_case. QuickType now generates some functions we can use to convert between the raw snake_case and camelCase ### How to test <!-- Please outline the steps to test your changes --> --- ### Before you merge > [!IMPORTANT] > > - [ ] I've added tests to support my implementation > - [ ] I have read and agree with the [Contribution Guidelines](./CONTRIBUTING.md) > - [ ] I have read and agree with the [Code of Conduct](./CODE_OF_CONDUCT.md) > - [ ] I've updated the relevant platform README (`platforms/swift/README.md` and/or `platforms/android/README.md`) --- <details> <summary>Releasing a new Swift version?</summary> - [ ] I have bumped the version in `ShopifyCheckoutKit.podspec` - [ ] I have bumped the version in `platforms/swift/Sources/ShopifyCheckoutKit/ShopifyCheckoutKit.swift` - [ ] I have updated `platforms/swift/CHANGELOG.md` - [ ] I have updated the SwiftPM/CocoaPods version snippets in `platforms/swift/README.md` (major version only) </details> <details> <summary>Releasing a new Android version?</summary> - [ ] I have bumped the `versionName` in `platforms/android/lib/build.gradle` - [ ] I have updated `platforms/android/CHANGELOG.md` - [ ] I have updated the Gradle/Maven version snippets in `platforms/android/README.md` </details> > [!TIP] > See the [Contributing documentation](./CONTRIBUTING.md) for the full release process per platform.
1 parent 75cd9c8 commit 3e76ae0

47 files changed

Lines changed: 6042 additions & 582 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ apollo-ios-cli
3131

3232
# Android / Gradle
3333
.gradle/
34+
.kotlin/
3435
build/
3536
captures/
3637
.externalNativeBuild

AGENTS.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,56 @@ protocol/ # cross-platform communication layer based on UCP
99
e2e/ # cross-platform end-to-end tests
1010
.github/ # workflows, issue templates, CODEOWNERS
1111
```
12+
13+
## React Native development with local native SDK changes
14+
15+
Until the new native SDK libraries have stable released versions, assume React Native validation needs the local native SDK workflow. Use `--local` whenever running the React Native sample or native React Native tests that depend on the in-repo Swift/Kotlin SDKs.
16+
17+
Use the React Native `--local` workflow when you need to test React Native against native SDK changes that exist in this repository but have not been released as a SemVer/CocoaPods/Maven version yet.
18+
19+
This applies when changes are made under:
20+
21+
- `platforms/swift/` — the iOS Swift SDK / CocoaPods sources
22+
- `platforms/android/` — the Android SDK / Maven artifact sources
23+
24+
It does **not** refer to the React Native wrapper platform folders:
25+
26+
- `platforms/react-native/modules/@shopify/checkout-kit-react-native/ios/`
27+
- `platforms/react-native/modules/@shopify/checkout-kit-react-native/android/`
28+
29+
### What `--local` does
30+
31+
- For React Native iOS, `--local` wires CocoaPods to the in-repo `platforms/swift/` sources via a local path instead of a released pod version.
32+
- For React Native Android, `--local` publishes/uses the in-repo `platforms/android/` SDK through Maven Local so Gradle resolves the local SDK artifact instead of a released Maven version.
33+
34+
### When to use it
35+
36+
Use `--local` whenever you are validating React Native behavior that depends on unreleased native SDK changes, for example:
37+
38+
- a new Swift SDK API that the React Native iOS bridge calls
39+
- a new Android SDK API that the React Native Android bridge calls
40+
- generated protocol/model changes under the native SDKs that the React Native module consumes
41+
- any change in `platforms/swift/` or `platforms/android/` that has not yet been released and consumed through normal dependency versions
42+
43+
Re-run the relevant local workflow whenever `platforms/swift/` or `platforms/android/` changes, because the React Native sample/tests need to re-resolve those local native SDK sources/artifacts.
44+
45+
```bash
46+
# iOS sample using local platforms/swift sources
47+
dev rn ios --local
48+
49+
# Android sample using local platforms/android via Maven Local
50+
dev rn android --local
51+
52+
# React Native Android unit tests using local platforms/android via Maven Local
53+
# `dev rn test android` publishes platforms/android/lib to ~/.m2 first, then runs the RN module tests.
54+
dev rn test android
55+
```
56+
57+
For ad-hoc Android Gradle test commands, publish the local Android SDK first and set `USE_LOCAL_SDK=1` so the React Native module resolves `com.shopify:checkout-kit:1.0.0` from Maven Local instead of the unreleased placeholder artifact:
58+
59+
```bash
60+
cd platforms/react-native
61+
USE_LOCAL_SDK=1 ./scripts/publish_android_snapshot
62+
cd sample/android
63+
USE_LOCAL_SDK=1 ./gradlew :shopify_checkout-kit-react-native:testDebugUnitTest
64+
```

dev.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,35 @@ commands:
276276
build:
277277
desc: Build the @shopify/checkout-kit-react-native module
278278
run: cd platforms/react-native && pnpm module build
279+
test:
280+
desc: Run React Native module tests (JS + iOS + Android)
281+
long_desc: |
282+
Runs unit tests across all three React Native targets:
283+
- JS: Jest tests in `platforms/react-native/modules/@shopify/checkout-kit-react-native/tests/`
284+
- iOS: Swift Package tests at `platforms/react-native/modules/@shopify/checkout-kit-react-native/ios/`
285+
- Android: Gradle JVM tests for `:shopify_checkout-kit-react-native` (requires a local Maven publish of `:lib`)
286+
run: |
287+
set -e
288+
cd platforms/react-native && pnpm test
289+
cd modules/@shopify/checkout-kit-react-native/ios && swift test
290+
cd ../../../../
291+
USE_LOCAL_SDK=1 ./scripts/publish_android_snapshot
292+
cd sample/android && USE_LOCAL_SDK=1 ./gradlew :shopify_checkout-kit-react-native:test
293+
subcommands:
294+
js:
295+
desc: Run JS unit tests via jest
296+
run: cd platforms/react-native && pnpm test
297+
ios:
298+
desc: Run native iOS unit tests (Swift Package at modules/.../ios)
299+
run: cd platforms/react-native/modules/@shopify/checkout-kit-react-native/ios && swift test
300+
android:
301+
desc: Run native Android unit tests for the RN module (publishes/uses local platforms/android SDK)
302+
run: |
303+
set -e
304+
cd platforms/react-native
305+
USE_LOCAL_SDK=1 ./scripts/publish_android_snapshot
306+
cd sample/android
307+
USE_LOCAL_SDK=1 ./gradlew :shopify_checkout-kit-react-native:test
279308
lint:
280309
desc: Run all React Native lint checks (Swift, module, sample)
281310
aliases: [style]

platforms/react-native/README.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -621,11 +621,6 @@ shopify.present(checkoutUrl, {
621621
`onClose` and `onFail` are mutually exclusive — exactly one of them fires
622622
per `present(...)` call, after which both handles are released.
623623

624-
> Protocol-level callbacks (`start`, `complete`, `error` on the protocol
625-
> client) are not part of this section and will land in a follow-up release
626-
> alongside a `<CheckoutSheet>` component. Checkout completion is not
627-
> currently surfaced through the per-call callbacks.
628-
629624
## Identity & customer accounts
630625

631626
Buyer-aware checkout experience reduces friction and increases conversion.

platforms/react-native/__mocks__/react-native.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,17 @@ const exampleConfig = {
4949
colorScheme: 'automatic',
5050
logLevel: 'error',
5151
};
52+
const shopifyCheckoutKitEventEmitter = createMockEmitter();
5253

5354
const ShopifyCheckoutKit = {
5455
version: '0.7.0',
5556
getConstants: jest.fn(() => ({
5657
version: '0.7.0',
5758
dispatchEventTypes: ['close', 'fail', 'geolocationRequest'],
5859
})),
60+
onDispatch: jest.fn((callback: (envelopeJson: string) => void) =>
61+
shopifyCheckoutKitEventEmitter.addListener('onDispatch', callback),
62+
),
5963
preload: jest.fn(),
6064
present: jest.fn(),
6165
dismiss: jest.fn(),
@@ -76,7 +80,7 @@ module.exports = {
7680
PermissionsAndroid: {
7781
requestMultiple: jest.fn(async () => ({})),
7882
},
79-
NativeEventEmitter: jest.fn(() => createMockEmitter()),
83+
NativeEventEmitter: jest.fn(() => shopifyCheckoutKitEventEmitter),
8084
requireNativeComponent,
8185
codegenNativeComponent,
8286
TurboModuleRegistry: {
@@ -90,7 +94,7 @@ module.exports = {
9094
NativeModules: {
9195
ShopifyCheckoutKit: {
9296
...ShopifyCheckoutKit,
93-
eventEmitter: createMockEmitter(),
97+
eventEmitter: shopifyCheckoutKitEventEmitter,
9498
},
9599
},
96100
StyleSheet,

platforms/react-native/jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module.exports = {
22
preset: 'react-native',
33
modulePathIgnorePatterns: ['modules/@shopify/checkout-kit-react-native/lib'],
4-
modulePaths: ['<rootDir>/sample/node_modules'],
4+
modulePaths: ['<rootDir>/node_modules', '<rootDir>/sample/node_modules'],
55
setupFiles: ['<rootDir>/jest.setup.ts'],
66
transform: {
77
'\\.[jt]sx?$': 'babel-jest',

platforms/react-native/metro.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
33

44
const root = path.resolve(__dirname);
55
const sample = path.resolve(root, 'sample');
6+
const protocol = path.resolve(root, '../../protocol/languages/typescript');
67

78
/**
89
* Metro configuration
@@ -13,7 +14,7 @@ const sample = path.resolve(root, 'sample');
1314
const config = mergeConfig(getDefaultConfig(__dirname), {
1415
projectRoot: sample,
1516

16-
watchFolders: [root],
17+
watchFolders: [root, protocol],
1718

1819
resolver: {
1920
resolveRequest: (context, moduleName, platform) => {
@@ -46,6 +47,8 @@ const config = mergeConfig(getDefaultConfig(__dirname), {
4647
'modules',
4748
'@shopify/checkout-kit-react-native',
4849
),
50+
'@shopify/checkout-kit-protocol': protocol,
51+
'@babel/runtime': path.resolve(root, 'node_modules', '@babel/runtime'),
4952
},
5053
},
5154

platforms/react-native/modules/@shopify/checkout-kit-react-native/RNShopifyCheckoutKit.podspec

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ Pod::Spec.new do |s|
1414
s.source = { :git => "https://github.com/Shopify/checkout-kit.git", :tag => "#{s.version}" }
1515

1616
s.source_files = "ios/*.{h,m,mm,swift}"
17+
# `ios/Package.swift` is the manifest for the nested SwiftPM test package
18+
# (ProtocolRelay unit tests). It imports `PackageDescription` which only
19+
# exists in the SwiftPM toolchain, so it must not be compiled by
20+
# CocoaPods/Xcode when the RN module is consumed from an iOS app.
21+
s.exclude_files = "ios/Package.swift"
1722

1823
s.dependency "React-Core"
1924

platforms/react-native/modules/@shopify/checkout-kit-react-native/android/build.gradle

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
buildscript {
2+
ext.kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : "2.1.20"
3+
24
repositories {
35
google()
46
mavenCentral()
57
}
68

79
dependencies {
810
classpath "com.android.tools.build:gradle:8.11.0"
11+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12+
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
913
}
1014
}
1115

1216
apply plugin: "com.android.library"
1317
apply plugin: "com.facebook.react"
18+
apply plugin: "org.jetbrains.kotlin.android"
19+
apply plugin: "org.jetbrains.kotlin.plugin.serialization"
1420

1521
def getExtOrIntegerDefault(name) {
1622
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties[name]).toInteger()
@@ -73,8 +79,17 @@ android {
7379
sourceCompatibility JavaVersion.VERSION_1_8
7480
targetCompatibility JavaVersion.VERSION_1_8
7581
}
82+
83+
kotlinOptions {
84+
jvmTarget = "1.8"
85+
}
86+
87+
testOptions {
88+
unitTests.includeAndroidResources = true
89+
}
7690
}
7791

92+
7893
repositories {
7994
mavenLocal()
8095
mavenCentral()
@@ -97,5 +112,10 @@ dependencies {
97112

98113
implementation(shopifySdkArtifact)
99114
implementation("com.fasterxml.jackson.core:jackson-databind:2.12.5")
115+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
100116
debugImplementation(shopifySdkArtifact)
117+
118+
testImplementation "junit:junit:4.13.2"
119+
testImplementation "org.assertj:assertj-core:3.27.7"
120+
testImplementation "org.robolectric:robolectric:4.16.1"
101121
}

platforms/react-native/modules/@shopify/checkout-kit-react-native/android/gradle.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,8 @@ targetSdkVersion=35
33
compileSdkVersion=36
44
ndkVersion=23.1.7779620
55
buildToolsVersion = "35.0.0"
6+
7+
# Opt out of the React Native Gradle plugin's JdkConfiguratorUtils, which otherwise
8+
# silently rewrites compileOptions to 17 and pins the Kotlin JVM toolchain to 17 for
9+
# every com.android.library it sees. We mirror :lib's pinned JVM 1.8 contract instead.
10+
react.internal.disableJavaVersionAlignment=true

0 commit comments

Comments
 (0)