├── .editorconfig
├── .gitattributes
├── .gitignore
├── .npmignore
├── Example
├── .bundle
│ └── config
├── .eslintrc.js
├── .gitignore
├── .prettierrc.js
├── .watchmanconfig
├── Gemfile
├── Gemfile.lock
├── README.md
├── android
│ ├── app
│ │ ├── build.gradle
│ │ ├── debug.keystore
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── iaphub
│ │ │ │ └── example
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── MainApplication.kt
│ │ │ └── res
│ │ │ ├── drawable
│ │ │ └── rn_edit_text_material.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
├── app.json
├── babel.config.js
├── index.js
├── ios
│ ├── .xcode.env
│ ├── Example.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Example.xcscheme
│ ├── Example.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── Example
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.mm
│ │ ├── Images.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Info.plist
│ │ ├── LaunchScreen.storyboard
│ │ ├── PrivacyInfo.xcprivacy
│ │ └── main.m
│ ├── ExampleTests
│ │ ├── ExampleTests.m
│ │ └── Info.plist
│ ├── Podfile
│ └── Podfile.lock
├── jest.config.js
├── metro.config.js
├── package-lock.json
├── package.json
├── src
│ ├── index.js
│ ├── pages
│ │ ├── iap.js
│ │ └── login.js
│ └── stores
│ │ ├── app.js
│ │ └── iap.js
└── tsconfig.json
├── LICENSE
├── README.md
├── android
├── build.gradle
├── gradle.properties
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── iaphub
│ └── RNIaphub
│ ├── RNIaphubModule.kt
│ └── RNIaphubPackage.kt
├── babel.config.js
├── guides
├── migrate-v6-to-v7.md
└── migrate-v7-to-v8.md
├── ios
├── RNIaphub-Bridging-Header.h
├── RNIaphub.m
├── RNIaphub.swift
└── RNIaphub.xcodeproj
│ └── project.pbxproj
├── package-lock.json
├── package.json
├── react-native-iaphub.podspec
├── src
├── config.tsx
├── iaphub.tsx
├── index.tsx
└── models
│ ├── active-product.tsx
│ ├── billing-status.tsx
│ ├── iaphub-error.tsx
│ ├── product.tsx
│ ├── subscription-intro-phase.tsx
│ └── transaction.tsx
├── tsconfig.build.json
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | indent_style = space
10 | indent_size = 2
11 |
12 | end_of_line = lf
13 | charset = utf-8
14 | trim_trailing_whitespace = true
15 | insert_final_newline = true
16 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 | # specific for windows script files
3 | *.bat text eol=crlf
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # XDE
6 | .expo/
7 |
8 | # VSCode
9 | .vscode/
10 | jsconfig.json
11 |
12 | # Xcode
13 | #
14 | build/
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 | xcuserdata
24 | *.xccheckout
25 | *.moved-aside
26 | DerivedData
27 | *.hmap
28 | *.ipa
29 | *.xcuserstate
30 | project.xcworkspace
31 |
32 | # Android/IJ
33 | #
34 | .classpath
35 | .cxx
36 | .gradle
37 | .idea
38 | .project
39 | .settings
40 | local.properties
41 | android.iml
42 |
43 | # Cocoapods
44 | #
45 | example/ios/Pods
46 |
47 | # node.js
48 | #
49 | node_modules/
50 | npm-debug.log
51 | yarn-debug.log
52 | yarn-error.log
53 |
54 | # BUCK
55 | buck-out/
56 | \.buckd/
57 | android/app/libs
58 | android/keystores/debug.keystore
59 |
60 | # Expo
61 | .expo/*
62 |
63 | # generated by bob
64 | lib/
65 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | npm-debug.log*
2 | .DS_Store
3 | .npmrc
4 | node_modules
5 | *.todo
6 | *~
7 | example
8 | guides
--------------------------------------------------------------------------------
/Example/.bundle/config:
--------------------------------------------------------------------------------
1 | BUNDLE_PATH: "vendor/bundle"
2 | BUNDLE_FORCE_RUBY_PLATFORM: 1
3 |
--------------------------------------------------------------------------------
/Example/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@react-native',
4 | };
5 |
--------------------------------------------------------------------------------
/Example/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | **/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 | *.keystore
35 | !debug.keystore
36 |
37 | # node.js
38 | #
39 | node_modules/
40 | npm-debug.log
41 | yarn-error.log
42 |
43 | # fastlane
44 | #
45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
46 | # screenshots whenever they are needed.
47 | # For more information about the recommended setup visit:
48 | # https://docs.fastlane.tools/best-practices/source-control/
49 |
50 | **/fastlane/report.xml
51 | **/fastlane/Preview.html
52 | **/fastlane/screenshots
53 | **/fastlane/test_output
54 |
55 | # Bundle artifact
56 | *.jsbundle
57 |
58 | # Ruby / CocoaPods
59 | **/Pods/
60 | /vendor/bundle/
61 |
62 | # Temporary files created by Metro to check the health of the file watcher
63 | .metro-health-check*
64 |
65 | # testing
66 | /coverage
67 |
68 | # Yarn
69 | .yarn/*
70 | !.yarn/patches
71 | !.yarn/plugins
72 | !.yarn/releases
73 | !.yarn/sdks
74 | !.yarn/versions
75 |
--------------------------------------------------------------------------------
/Example/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: 'avoid',
3 | bracketSameLine: true,
4 | bracketSpacing: false,
5 | singleQuote: true,
6 | trailingComma: 'all',
7 | };
8 |
--------------------------------------------------------------------------------
/Example/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/Example/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4 | ruby ">= 2.6.10"
5 |
6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures.
7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
9 | gem 'xcodeproj', '< 1.26.0'
10 |
--------------------------------------------------------------------------------
/Example/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.7)
5 | base64
6 | nkf
7 | rexml
8 | activesupport (7.1.5.1)
9 | base64
10 | benchmark (>= 0.3)
11 | bigdecimal
12 | concurrent-ruby (~> 1.0, >= 1.0.2)
13 | connection_pool (>= 2.2.5)
14 | drb
15 | i18n (>= 1.6, < 2)
16 | logger (>= 1.4.2)
17 | minitest (>= 5.1)
18 | mutex_m
19 | securerandom (>= 0.3)
20 | tzinfo (~> 2.0)
21 | addressable (2.8.7)
22 | public_suffix (>= 2.0.2, < 7.0)
23 | algoliasearch (1.27.5)
24 | httpclient (~> 2.8, >= 2.8.3)
25 | json (>= 1.5.1)
26 | atomos (0.1.3)
27 | base64 (0.2.0)
28 | benchmark (0.4.0)
29 | bigdecimal (3.1.8)
30 | claide (1.1.0)
31 | cocoapods (1.15.2)
32 | addressable (~> 2.8)
33 | claide (>= 1.0.2, < 2.0)
34 | cocoapods-core (= 1.15.2)
35 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
36 | cocoapods-downloader (>= 2.1, < 3.0)
37 | cocoapods-plugins (>= 1.0.0, < 2.0)
38 | cocoapods-search (>= 1.0.0, < 2.0)
39 | cocoapods-trunk (>= 1.6.0, < 2.0)
40 | cocoapods-try (>= 1.1.0, < 2.0)
41 | colored2 (~> 3.1)
42 | escape (~> 0.0.4)
43 | fourflusher (>= 2.3.0, < 3.0)
44 | gh_inspector (~> 1.0)
45 | molinillo (~> 0.8.0)
46 | nap (~> 1.0)
47 | ruby-macho (>= 2.3.0, < 3.0)
48 | xcodeproj (>= 1.23.0, < 2.0)
49 | cocoapods-core (1.15.2)
50 | activesupport (>= 5.0, < 8)
51 | addressable (~> 2.8)
52 | algoliasearch (~> 1.0)
53 | concurrent-ruby (~> 1.1)
54 | fuzzy_match (~> 2.0.4)
55 | nap (~> 1.0)
56 | netrc (~> 0.11)
57 | public_suffix (~> 4.0)
58 | typhoeus (~> 1.0)
59 | cocoapods-deintegrate (1.0.5)
60 | cocoapods-downloader (2.1)
61 | cocoapods-plugins (1.0.0)
62 | nap
63 | cocoapods-search (1.0.1)
64 | cocoapods-trunk (1.6.0)
65 | nap (>= 0.8, < 2.0)
66 | netrc (~> 0.11)
67 | cocoapods-try (1.2.0)
68 | colored2 (3.1.2)
69 | concurrent-ruby (1.3.4)
70 | connection_pool (2.4.1)
71 | drb (2.2.1)
72 | escape (0.0.4)
73 | ethon (0.16.0)
74 | ffi (>= 1.15.0)
75 | ffi (1.17.0)
76 | fourflusher (2.3.1)
77 | fuzzy_match (2.0.4)
78 | gh_inspector (1.1.3)
79 | httpclient (2.8.3)
80 | i18n (1.14.6)
81 | concurrent-ruby (~> 1.0)
82 | json (2.9.1)
83 | logger (1.6.4)
84 | minitest (5.25.4)
85 | molinillo (0.8.0)
86 | mutex_m (0.3.0)
87 | nanaimo (0.3.0)
88 | nap (1.1.0)
89 | netrc (0.11.0)
90 | nkf (0.2.0)
91 | public_suffix (4.0.7)
92 | rexml (3.4.0)
93 | ruby-macho (2.5.1)
94 | securerandom (0.3.2)
95 | typhoeus (1.4.1)
96 | ethon (>= 0.9.0)
97 | tzinfo (2.0.6)
98 | concurrent-ruby (~> 1.0)
99 | xcodeproj (1.25.1)
100 | CFPropertyList (>= 2.3.3, < 4.0)
101 | atomos (~> 0.1.3)
102 | claide (>= 1.0.2, < 2.0)
103 | colored2 (~> 3.1)
104 | nanaimo (~> 0.3.0)
105 | rexml (>= 3.3.6, < 4.0)
106 |
107 | PLATFORMS
108 | ruby
109 |
110 | DEPENDENCIES
111 | activesupport (>= 6.1.7.5, != 7.1.0)
112 | cocoapods (>= 1.13, != 1.15.1, != 1.15.0)
113 | xcodeproj (< 1.26.0)
114 |
115 | RUBY VERSION
116 | ruby 3.0.0p0
117 |
118 | BUNDLED WITH
119 | 2.2.3
120 |
--------------------------------------------------------------------------------
/Example/README.md:
--------------------------------------------------------------------------------
1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).
2 |
3 | # Getting Started
4 |
5 | >**Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding.
6 |
7 | ## Step 1: Start the Metro Server
8 |
9 | First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native.
10 |
11 | To start Metro, run the following command from the _root_ of your React Native project:
12 |
13 | ```bash
14 | # using npm
15 | npm start
16 |
17 | # OR using Yarn
18 | yarn start
19 | ```
20 |
21 | ## Step 2: Start your Application
22 |
23 | Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app:
24 |
25 | ### For Android
26 |
27 | ```bash
28 | # using npm
29 | npm run android
30 |
31 | # OR using Yarn
32 | yarn android
33 | ```
34 |
35 | ### For iOS
36 |
37 | ```bash
38 | # using npm
39 | npm run ios
40 |
41 | # OR using Yarn
42 | yarn ios
43 | ```
44 |
45 | If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly.
46 |
47 | This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively.
48 |
49 | ## Step 3: Modifying your App
50 |
51 | Now that you have successfully run the app, let's modify it.
52 |
53 | 1. Open `App.tsx` in your text editor of choice and edit some lines.
54 | 2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes!
55 |
56 | For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes!
57 |
58 | ## Congratulations! :tada:
59 |
60 | You've successfully run and modified your React Native App. :partying_face:
61 |
62 | ### Now what?
63 |
64 | - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
65 | - If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started).
66 |
67 | # Troubleshooting
68 |
69 | If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
70 |
71 | # Learn More
72 |
73 | To learn more about React Native, take a look at the following resources:
74 |
75 | - [React Native Website](https://reactnative.dev) - learn more about React Native.
76 | - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
77 | - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
78 | - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
79 | - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
80 |
--------------------------------------------------------------------------------
/Example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "org.jetbrains.kotlin.android"
3 | apply plugin: "com.facebook.react"
4 |
5 | /**
6 | * This is the configuration block to customize your React Native Android app.
7 | * By default you don't need to apply any configuration, just uncomment the lines you need.
8 | */
9 | react {
10 | /* Folders */
11 | // The root of your project, i.e. where "package.json" lives. Default is '../..'
12 | // root = file("../../")
13 | // The folder where the react-native NPM package is. Default is ../../node_modules/react-native
14 | // reactNativeDir = file("../../node_modules/react-native")
15 | // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
16 | // codegenDir = file("../../node_modules/@react-native/codegen")
17 | // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js
18 | // cliFile = file("../../node_modules/react-native/cli.js")
19 |
20 | /* Variants */
21 | // The list of variants to that are debuggable. For those we're going to
22 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'.
23 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
24 | // debuggableVariants = ["liteDebug", "prodDebug"]
25 |
26 | /* Bundling */
27 | // A list containing the node command and its flags. Default is just 'node'.
28 | // nodeExecutableAndArgs = ["node"]
29 | //
30 | // The command to run when bundling. By default is 'bundle'
31 | // bundleCommand = "ram-bundle"
32 | //
33 | // The path to the CLI configuration file. Default is empty.
34 | // bundleConfig = file(../rn-cli.config.js)
35 | //
36 | // The name of the generated asset file containing your JS bundle
37 | // bundleAssetName = "MyApplication.android.bundle"
38 | //
39 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
40 | // entryFile = file("../js/MyApplication.android.js")
41 | //
42 | // A list of extra flags to pass to the 'bundle' commands.
43 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
44 | // extraPackagerArgs = []
45 |
46 | /* Hermes Commands */
47 | // The hermes compiler command to run. By default it is 'hermesc'
48 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
49 | //
50 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
51 | // hermesFlags = ["-O", "-output-source-map"]
52 |
53 | /* Autolinking */
54 | autolinkLibrariesWithApp()
55 | }
56 |
57 | /**
58 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
59 | */
60 | def enableProguardInReleaseBuilds = false
61 |
62 | /**
63 | * The preferred build flavor of JavaScriptCore (JSC)
64 | *
65 | * For example, to use the international variant, you can use:
66 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
67 | *
68 | * The international variant includes ICU i18n library and necessary data
69 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
70 | * give correct results when using with locales other than en-US. Note that
71 | * this variant is about 6MiB larger per architecture than default.
72 | */
73 | def jscFlavor = 'org.webkit:android-jsc:+'
74 |
75 | android {
76 | ndkVersion rootProject.ext.ndkVersion
77 | buildToolsVersion rootProject.ext.buildToolsVersion
78 | compileSdk rootProject.ext.compileSdkVersion
79 |
80 | namespace "com.iaphub.example"
81 | defaultConfig {
82 | applicationId "com.iaphub.example"
83 | minSdkVersion rootProject.ext.minSdkVersion
84 | targetSdkVersion rootProject.ext.targetSdkVersion
85 | versionCode 1
86 | versionName "1.0"
87 | }
88 | signingConfigs {
89 | debug {
90 | storeFile file('debug.keystore')
91 | storePassword 'android'
92 | keyAlias 'androiddebugkey'
93 | keyPassword 'android'
94 | }
95 | }
96 | buildTypes {
97 | debug {
98 | signingConfig signingConfigs.debug
99 | }
100 | release {
101 | // Caution! In production, you need to generate your own keystore file.
102 | // see https://reactnative.dev/docs/signed-apk-android.
103 | signingConfig signingConfigs.debug
104 | minifyEnabled enableProguardInReleaseBuilds
105 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
106 | }
107 | }
108 | }
109 |
110 | dependencies {
111 | // The version of react-native is set by the React Native Gradle Plugin
112 | implementation("com.facebook.react:react-android")
113 |
114 | if (hermesEnabled.toBoolean()) {
115 | implementation("com.facebook.react:hermes-android")
116 | } else {
117 | implementation jscFlavor
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/Example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/debug.keystore
--------------------------------------------------------------------------------
/Example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/Example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/java/com/iaphub/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.iaphub.example
2 |
3 | import com.facebook.react.ReactActivity
4 | import com.facebook.react.ReactActivityDelegate
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate
7 |
8 | class MainActivity : ReactActivity() {
9 |
10 | /**
11 | * Returns the name of the main component registered from JavaScript. This is used to schedule
12 | * rendering of the component.
13 | */
14 | override fun getMainComponentName(): String = "Example"
15 |
16 | /**
17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
19 | */
20 | override fun createReactActivityDelegate(): ReactActivityDelegate =
21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
22 | }
23 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/java/com/iaphub/example/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.iaphub.example
2 |
3 | import android.app.Application
4 | import com.facebook.react.PackageList
5 | import com.facebook.react.ReactApplication
6 | import com.facebook.react.ReactHost
7 | import com.facebook.react.ReactNativeHost
8 | import com.facebook.react.ReactPackage
9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
11 | import com.facebook.react.defaults.DefaultReactNativeHost
12 | import com.facebook.react.soloader.OpenSourceMergedSoMapping
13 | import com.facebook.soloader.SoLoader
14 |
15 | class MainApplication : Application(), ReactApplication {
16 |
17 | override val reactNativeHost: ReactNativeHost =
18 | object : DefaultReactNativeHost(this) {
19 | override fun getPackages(): List =
20 | PackageList(this).packages.apply {
21 | // Packages that cannot be autolinked yet can be added manually here, for example:
22 | // add(MyReactNativePackage())
23 | }
24 |
25 | override fun getJSMainModuleName(): String = "index"
26 |
27 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
28 |
29 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
30 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
31 | }
32 |
33 | override val reactHost: ReactHost
34 | get() = getDefaultReactHost(applicationContext, reactNativeHost)
35 |
36 | override fun onCreate() {
37 | super.onCreate()
38 | SoLoader.init(this, OpenSourceMergedSoMapping)
39 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
40 | // If you opted-in for the New Architecture, we load the native entry point for this app.
41 | load()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
22 |
23 |
24 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
--------------------------------------------------------------------------------
/Example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | buildToolsVersion = "35.0.0"
4 | minSdkVersion = 24
5 | compileSdkVersion = 35
6 | targetSdkVersion = 34
7 | ndkVersion = "26.1.10909125"
8 | kotlinVersion = "1.9.24"
9 | }
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | dependencies {
15 | classpath("com.android.tools.build:gradle")
16 | classpath("com.facebook.react:react-native-gradle-plugin")
17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
18 | }
19 | }
20 |
21 | apply plugin: "com.facebook.react.rootproject"
22 |
--------------------------------------------------------------------------------
/Example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 |
25 | # Use this property to specify which architecture you want to build.
26 | # You can also override it from the CLI using
27 | # ./gradlew -PreactNativeArchitectures=x86_64
28 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
29 |
30 | # Use this property to enable support to the new architecture.
31 | # This will allow you to use TurboModules and the Fabric render in
32 | # your application. You should enable this flag either if you want
33 | # to write custom TurboModules/Fabric components OR use libraries that
34 | # are providing them.
35 | newArchEnabled=true
36 |
37 | # Use this property to enable or disable the Hermes JS engine.
38 | # If set to false, you will be using JSC instead.
39 | hermesEnabled=true
40 |
--------------------------------------------------------------------------------
/Example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iaphub/react-native-iaphub/1eb248519bb16565694fcbec83bfda6e08a7bb49/Example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/Example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/Example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
90 | ' "$PWD" ) || exit
91 |
92 | # Use the maximum available, or set MAX_FD != -1 to use that value.
93 | MAX_FD=maximum
94 |
95 | warn () {
96 | echo "$*"
97 | } >&2
98 |
99 | die () {
100 | echo
101 | echo "$*"
102 | echo
103 | exit 1
104 | } >&2
105 |
106 | # OS specific support (must be 'true' or 'false').
107 | cygwin=false
108 | msys=false
109 | darwin=false
110 | nonstop=false
111 | case "$( uname )" in #(
112 | CYGWIN* ) cygwin=true ;; #(
113 | Darwin* ) darwin=true ;; #(
114 | MSYS* | MINGW* ) msys=true ;; #(
115 | NONSTOP* ) nonstop=true ;;
116 | esac
117 |
118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
119 |
120 |
121 | # Determine the Java command to use to start the JVM.
122 | if [ -n "$JAVA_HOME" ] ; then
123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
124 | # IBM's JDK on AIX uses strange locations for the executables
125 | JAVACMD=$JAVA_HOME/jre/sh/java
126 | else
127 | JAVACMD=$JAVA_HOME/bin/java
128 | fi
129 | if [ ! -x "$JAVACMD" ] ; then
130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
131 |
132 | Please set the JAVA_HOME variable in your environment to match the
133 | location of your Java installation."
134 | fi
135 | else
136 | JAVACMD=java
137 | if ! command -v java >/dev/null 2>&1
138 | then
139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
140 |
141 | Please set the JAVA_HOME variable in your environment to match the
142 | location of your Java installation."
143 | fi
144 | fi
145 |
146 | # Increase the maximum file descriptors if we can.
147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
148 | case $MAX_FD in #(
149 | max*)
150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
151 | # shellcheck disable=SC2039,SC3045
152 | MAX_FD=$( ulimit -H -n ) ||
153 | warn "Could not query maximum file descriptor limit"
154 | esac
155 | case $MAX_FD in #(
156 | '' | soft) :;; #(
157 | *)
158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
159 | # shellcheck disable=SC2039,SC3045
160 | ulimit -n "$MAX_FD" ||
161 | warn "Could not set maximum file descriptor limit to $MAX_FD"
162 | esac
163 | fi
164 |
165 | # Collect all arguments for the java command, stacking in reverse order:
166 | # * args from the command line
167 | # * the main class name
168 | # * -classpath
169 | # * -D...appname settings
170 | # * --module-path (only if needed)
171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
172 |
173 | # For Cygwin or MSYS, switch paths to Windows format before running java
174 | if "$cygwin" || "$msys" ; then
175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
177 |
178 | JAVACMD=$( cygpath --unix "$JAVACMD" )
179 |
180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
181 | for arg do
182 | if
183 | case $arg in #(
184 | -*) false ;; # don't mess with options #(
185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
186 | [ -e "$t" ] ;; #(
187 | *) false ;;
188 | esac
189 | then
190 | arg=$( cygpath --path --ignore --mixed "$arg" )
191 | fi
192 | # Roll the args list around exactly as many times as the number of
193 | # args, so each arg winds up back in the position where it started, but
194 | # possibly modified.
195 | #
196 | # NB: a `for` loop captures its iteration list before it begins, so
197 | # changing the positional parameters here affects neither the number of
198 | # iterations, nor the values presented in `arg`.
199 | shift # remove old arg
200 | set -- "$@" "$arg" # push replacement arg
201 | done
202 | fi
203 |
204 |
205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
207 |
208 | # Collect all arguments for the java command:
209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
210 | # and any embedded shellness will be escaped.
211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
212 | # treated as '${Hostname}' itself on the command line.
213 |
214 | set -- \
215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
216 | -classpath "$CLASSPATH" \
217 | org.gradle.wrapper.GradleWrapperMain \
218 | "$@"
219 |
220 | # Stop when "xargs" is not available.
221 | if ! command -v xargs >/dev/null 2>&1
222 | then
223 | die "xargs is not available"
224 | fi
225 |
226 | # Use "xargs" to parse quoted args.
227 | #
228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
229 | #
230 | # In Bash we could simply go:
231 | #
232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
233 | # set -- "${ARGS[@]}" "$@"
234 | #
235 | # but POSIX shell has neither arrays nor command substitution, so instead we
236 | # post-process each arg (as a line of input to sed) to backslash-escape any
237 | # character that might be a shell metacharacter, then use eval to reverse
238 | # that process (while maintaining the separation between arguments), and wrap
239 | # the whole thing up as a single "set" statement.
240 | #
241 | # This will of course break if any of these variables contains a newline or
242 | # an unmatched quote.
243 | #
244 |
245 | eval "set -- $(
246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
247 | xargs -n1 |
248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
249 | tr '\n' ' '
250 | )" '"$@"'
251 |
252 | exec "$JAVACMD" "$@"
253 |
--------------------------------------------------------------------------------
/Example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/Example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
2 | plugins { id("com.facebook.react.settings") }
3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
4 | rootProject.name = 'com.iaphub.example'
5 | include ':app'
6 | includeBuild('../node_modules/@react-native/gradle-plugin')
7 |
--------------------------------------------------------------------------------
/Example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Example",
3 | "displayName": "Example"
4 | }
5 |
--------------------------------------------------------------------------------
/Example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:@react-native/babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/Example/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import {AppRegistry} from 'react-native';
6 | import App from './src';
7 | import {name as appName} from './app.json';
8 |
9 | AppRegistry.registerComponent(appName, () => App);
10 |
--------------------------------------------------------------------------------
/Example/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/Example/ios/Example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 00E356F31AD99517003FC87E /* ExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ExampleTests.m */; };
11 | 0C80B921A6F3F58F76C31292 /* libPods-Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-Example.a */; };
12 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
14 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
15 | 7699B88040F8A987B510C191 /* libPods-Example-ExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-Example-ExampleTests.a */; };
16 | 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
17 | FD6C61F02DCE0E09E2787BF6 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXContainerItemProxy section */
21 | 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
22 | isa = PBXContainerItemProxy;
23 | containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
24 | proxyType = 1;
25 | remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
26 | remoteInfo = Example;
27 | };
28 | /* End PBXContainerItemProxy section */
29 |
30 | /* Begin PBXFileReference section */
31 | 00E356EE1AD99517003FC87E /* ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
32 | 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
33 | 00E356F21AD99517003FC87E /* ExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExampleTests.m; sourceTree = ""; };
34 | 13B07F961A680F5B00A75B9A /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
35 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Example/AppDelegate.h; sourceTree = ""; };
36 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = Example/AppDelegate.mm; sourceTree = ""; };
37 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Example/Images.xcassets; sourceTree = ""; };
38 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Example/Info.plist; sourceTree = ""; };
39 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Example/main.m; sourceTree = ""; };
40 | 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = Example/PrivacyInfo.xcprivacy; sourceTree = ""; };
41 | 19F6CBCC0A4E27FBF8BF4A61 /* libPods-Example-ExampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example-ExampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
42 | 3B4392A12AC88292D35C810B /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = ""; };
43 | 5709B34CF0A7D63546082F79 /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; };
44 | 5B7EB9410499542E8C5724F5 /* Pods-Example-ExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-ExampleTests.debug.xcconfig"; path = "Target Support Files/Pods-Example-ExampleTests/Pods-Example-ExampleTests.debug.xcconfig"; sourceTree = ""; };
45 | 5DCACB8F33CDC322A6C60F78 /* libPods-Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
46 | 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = Example/LaunchScreen.storyboard; sourceTree = ""; };
47 | 89C6BE57DB24E9ADA2F236DE /* Pods-Example-ExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-ExampleTests.release.xcconfig"; path = "Target Support Files/Pods-Example-ExampleTests/Pods-Example-ExampleTests.release.xcconfig"; sourceTree = ""; };
48 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
49 | /* End PBXFileReference section */
50 |
51 | /* Begin PBXFrameworksBuildPhase section */
52 | 00E356EB1AD99517003FC87E /* Frameworks */ = {
53 | isa = PBXFrameworksBuildPhase;
54 | buildActionMask = 2147483647;
55 | files = (
56 | 7699B88040F8A987B510C191 /* libPods-Example-ExampleTests.a in Frameworks */,
57 | );
58 | runOnlyForDeploymentPostprocessing = 0;
59 | };
60 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
61 | isa = PBXFrameworksBuildPhase;
62 | buildActionMask = 2147483647;
63 | files = (
64 | 0C80B921A6F3F58F76C31292 /* libPods-Example.a in Frameworks */,
65 | );
66 | runOnlyForDeploymentPostprocessing = 0;
67 | };
68 | /* End PBXFrameworksBuildPhase section */
69 |
70 | /* Begin PBXGroup section */
71 | 00E356EF1AD99517003FC87E /* ExampleTests */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 00E356F21AD99517003FC87E /* ExampleTests.m */,
75 | 00E356F01AD99517003FC87E /* Supporting Files */,
76 | );
77 | path = ExampleTests;
78 | sourceTree = "";
79 | };
80 | 00E356F01AD99517003FC87E /* Supporting Files */ = {
81 | isa = PBXGroup;
82 | children = (
83 | 00E356F11AD99517003FC87E /* Info.plist */,
84 | );
85 | name = "Supporting Files";
86 | sourceTree = "";
87 | };
88 | 13B07FAE1A68108700A75B9A /* Example */ = {
89 | isa = PBXGroup;
90 | children = (
91 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
92 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */,
93 | 13B07FB51A68108700A75B9A /* Images.xcassets */,
94 | 13B07FB61A68108700A75B9A /* Info.plist */,
95 | 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
96 | 13B07FB71A68108700A75B9A /* main.m */,
97 | 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */,
98 | );
99 | name = Example;
100 | sourceTree = "";
101 | };
102 | 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
103 | isa = PBXGroup;
104 | children = (
105 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
106 | 5DCACB8F33CDC322A6C60F78 /* libPods-Example.a */,
107 | 19F6CBCC0A4E27FBF8BF4A61 /* libPods-Example-ExampleTests.a */,
108 | );
109 | name = Frameworks;
110 | sourceTree = "";
111 | };
112 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
113 | isa = PBXGroup;
114 | children = (
115 | );
116 | name = Libraries;
117 | sourceTree = "";
118 | };
119 | 83CBB9F61A601CBA00E9B192 = {
120 | isa = PBXGroup;
121 | children = (
122 | 13B07FAE1A68108700A75B9A /* Example */,
123 | 832341AE1AAA6A7D00B99B32 /* Libraries */,
124 | 00E356EF1AD99517003FC87E /* ExampleTests */,
125 | 83CBBA001A601CBA00E9B192 /* Products */,
126 | 2D16E6871FA4F8E400B85C8A /* Frameworks */,
127 | BBD78D7AC51CEA395F1C20DB /* Pods */,
128 | );
129 | indentWidth = 2;
130 | sourceTree = "";
131 | tabWidth = 2;
132 | usesTabs = 0;
133 | };
134 | 83CBBA001A601CBA00E9B192 /* Products */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 13B07F961A680F5B00A75B9A /* Example.app */,
138 | 00E356EE1AD99517003FC87E /* ExampleTests.xctest */,
139 | );
140 | name = Products;
141 | sourceTree = "";
142 | };
143 | BBD78D7AC51CEA395F1C20DB /* Pods */ = {
144 | isa = PBXGroup;
145 | children = (
146 | 3B4392A12AC88292D35C810B /* Pods-Example.debug.xcconfig */,
147 | 5709B34CF0A7D63546082F79 /* Pods-Example.release.xcconfig */,
148 | 5B7EB9410499542E8C5724F5 /* Pods-Example-ExampleTests.debug.xcconfig */,
149 | 89C6BE57DB24E9ADA2F236DE /* Pods-Example-ExampleTests.release.xcconfig */,
150 | );
151 | path = Pods;
152 | sourceTree = "";
153 | };
154 | /* End PBXGroup section */
155 |
156 | /* Begin PBXNativeTarget section */
157 | 00E356ED1AD99517003FC87E /* ExampleTests */ = {
158 | isa = PBXNativeTarget;
159 | buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ExampleTests" */;
160 | buildPhases = (
161 | A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */,
162 | 00E356EA1AD99517003FC87E /* Sources */,
163 | 00E356EB1AD99517003FC87E /* Frameworks */,
164 | 00E356EC1AD99517003FC87E /* Resources */,
165 | C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */,
166 | F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */,
167 | );
168 | buildRules = (
169 | );
170 | dependencies = (
171 | 00E356F51AD99517003FC87E /* PBXTargetDependency */,
172 | );
173 | name = ExampleTests;
174 | productName = ExampleTests;
175 | productReference = 00E356EE1AD99517003FC87E /* ExampleTests.xctest */;
176 | productType = "com.apple.product-type.bundle.unit-test";
177 | };
178 | 13B07F861A680F5B00A75B9A /* Example */ = {
179 | isa = PBXNativeTarget;
180 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Example" */;
181 | buildPhases = (
182 | C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */,
183 | 13B07F871A680F5B00A75B9A /* Sources */,
184 | 13B07F8C1A680F5B00A75B9A /* Frameworks */,
185 | 13B07F8E1A680F5B00A75B9A /* Resources */,
186 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
187 | 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
188 | E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
189 | );
190 | buildRules = (
191 | );
192 | dependencies = (
193 | );
194 | name = Example;
195 | productName = Example;
196 | productReference = 13B07F961A680F5B00A75B9A /* Example.app */;
197 | productType = "com.apple.product-type.application";
198 | };
199 | /* End PBXNativeTarget section */
200 |
201 | /* Begin PBXProject section */
202 | 83CBB9F71A601CBA00E9B192 /* Project object */ = {
203 | isa = PBXProject;
204 | attributes = {
205 | LastUpgradeCheck = 1210;
206 | TargetAttributes = {
207 | 00E356ED1AD99517003FC87E = {
208 | CreatedOnToolsVersion = 6.2;
209 | TestTargetID = 13B07F861A680F5B00A75B9A;
210 | };
211 | 13B07F861A680F5B00A75B9A = {
212 | LastSwiftMigration = 1120;
213 | };
214 | };
215 | };
216 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Example" */;
217 | compatibilityVersion = "Xcode 12.0";
218 | developmentRegion = en;
219 | hasScannedForEncodings = 0;
220 | knownRegions = (
221 | en,
222 | Base,
223 | );
224 | mainGroup = 83CBB9F61A601CBA00E9B192;
225 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
226 | projectDirPath = "";
227 | projectRoot = "";
228 | targets = (
229 | 13B07F861A680F5B00A75B9A /* Example */,
230 | 00E356ED1AD99517003FC87E /* ExampleTests */,
231 | );
232 | };
233 | /* End PBXProject section */
234 |
235 | /* Begin PBXResourcesBuildPhase section */
236 | 00E356EC1AD99517003FC87E /* Resources */ = {
237 | isa = PBXResourcesBuildPhase;
238 | buildActionMask = 2147483647;
239 | files = (
240 | );
241 | runOnlyForDeploymentPostprocessing = 0;
242 | };
243 | 13B07F8E1A680F5B00A75B9A /* Resources */ = {
244 | isa = PBXResourcesBuildPhase;
245 | buildActionMask = 2147483647;
246 | files = (
247 | 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
248 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
249 | FD6C61F02DCE0E09E2787BF6 /* PrivacyInfo.xcprivacy in Resources */,
250 | );
251 | runOnlyForDeploymentPostprocessing = 0;
252 | };
253 | /* End PBXResourcesBuildPhase section */
254 |
255 | /* Begin PBXShellScriptBuildPhase section */
256 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
257 | isa = PBXShellScriptBuildPhase;
258 | buildActionMask = 2147483647;
259 | files = (
260 | );
261 | inputPaths = (
262 | "$(SRCROOT)/.xcode.env.local",
263 | "$(SRCROOT)/.xcode.env",
264 | );
265 | name = "Bundle React Native code and images";
266 | outputPaths = (
267 | );
268 | runOnlyForDeploymentPostprocessing = 0;
269 | shellPath = /bin/sh;
270 | shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
271 | };
272 | 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
273 | isa = PBXShellScriptBuildPhase;
274 | buildActionMask = 2147483647;
275 | files = (
276 | );
277 | inputFileListPaths = (
278 | "${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-frameworks-${CONFIGURATION}-input-files.xcfilelist",
279 | );
280 | name = "[CP] Embed Pods Frameworks";
281 | outputFileListPaths = (
282 | "${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-frameworks-${CONFIGURATION}-output-files.xcfilelist",
283 | );
284 | runOnlyForDeploymentPostprocessing = 0;
285 | shellPath = /bin/sh;
286 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-frameworks.sh\"\n";
287 | showEnvVarsInLog = 0;
288 | };
289 | A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = {
290 | isa = PBXShellScriptBuildPhase;
291 | buildActionMask = 2147483647;
292 | files = (
293 | );
294 | inputFileListPaths = (
295 | );
296 | inputPaths = (
297 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
298 | "${PODS_ROOT}/Manifest.lock",
299 | );
300 | name = "[CP] Check Pods Manifest.lock";
301 | outputFileListPaths = (
302 | );
303 | outputPaths = (
304 | "$(DERIVED_FILE_DIR)/Pods-Example-ExampleTests-checkManifestLockResult.txt",
305 | );
306 | runOnlyForDeploymentPostprocessing = 0;
307 | shellPath = /bin/sh;
308 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
309 | showEnvVarsInLog = 0;
310 | };
311 | C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
312 | isa = PBXShellScriptBuildPhase;
313 | buildActionMask = 2147483647;
314 | files = (
315 | );
316 | inputFileListPaths = (
317 | );
318 | inputPaths = (
319 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
320 | "${PODS_ROOT}/Manifest.lock",
321 | );
322 | name = "[CP] Check Pods Manifest.lock";
323 | outputFileListPaths = (
324 | );
325 | outputPaths = (
326 | "$(DERIVED_FILE_DIR)/Pods-Example-checkManifestLockResult.txt",
327 | );
328 | runOnlyForDeploymentPostprocessing = 0;
329 | shellPath = /bin/sh;
330 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
331 | showEnvVarsInLog = 0;
332 | };
333 | C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = {
334 | isa = PBXShellScriptBuildPhase;
335 | buildActionMask = 2147483647;
336 | files = (
337 | );
338 | inputFileListPaths = (
339 | "${PODS_ROOT}/Target Support Files/Pods-Example-ExampleTests/Pods-Example-ExampleTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
340 | );
341 | name = "[CP] Embed Pods Frameworks";
342 | outputFileListPaths = (
343 | "${PODS_ROOT}/Target Support Files/Pods-Example-ExampleTests/Pods-Example-ExampleTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
344 | );
345 | runOnlyForDeploymentPostprocessing = 0;
346 | shellPath = /bin/sh;
347 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-ExampleTests/Pods-Example-ExampleTests-frameworks.sh\"\n";
348 | showEnvVarsInLog = 0;
349 | };
350 | E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
351 | isa = PBXShellScriptBuildPhase;
352 | buildActionMask = 2147483647;
353 | files = (
354 | );
355 | inputFileListPaths = (
356 | "${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-resources-${CONFIGURATION}-input-files.xcfilelist",
357 | );
358 | name = "[CP] Copy Pods Resources";
359 | outputFileListPaths = (
360 | "${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-resources-${CONFIGURATION}-output-files.xcfilelist",
361 | );
362 | runOnlyForDeploymentPostprocessing = 0;
363 | shellPath = /bin/sh;
364 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-resources.sh\"\n";
365 | showEnvVarsInLog = 0;
366 | };
367 | F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = {
368 | isa = PBXShellScriptBuildPhase;
369 | buildActionMask = 2147483647;
370 | files = (
371 | );
372 | inputFileListPaths = (
373 | "${PODS_ROOT}/Target Support Files/Pods-Example-ExampleTests/Pods-Example-ExampleTests-resources-${CONFIGURATION}-input-files.xcfilelist",
374 | );
375 | name = "[CP] Copy Pods Resources";
376 | outputFileListPaths = (
377 | "${PODS_ROOT}/Target Support Files/Pods-Example-ExampleTests/Pods-Example-ExampleTests-resources-${CONFIGURATION}-output-files.xcfilelist",
378 | );
379 | runOnlyForDeploymentPostprocessing = 0;
380 | shellPath = /bin/sh;
381 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-ExampleTests/Pods-Example-ExampleTests-resources.sh\"\n";
382 | showEnvVarsInLog = 0;
383 | };
384 | /* End PBXShellScriptBuildPhase section */
385 |
386 | /* Begin PBXSourcesBuildPhase section */
387 | 00E356EA1AD99517003FC87E /* Sources */ = {
388 | isa = PBXSourcesBuildPhase;
389 | buildActionMask = 2147483647;
390 | files = (
391 | 00E356F31AD99517003FC87E /* ExampleTests.m in Sources */,
392 | );
393 | runOnlyForDeploymentPostprocessing = 0;
394 | };
395 | 13B07F871A680F5B00A75B9A /* Sources */ = {
396 | isa = PBXSourcesBuildPhase;
397 | buildActionMask = 2147483647;
398 | files = (
399 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
400 | 13B07FC11A68108700A75B9A /* main.m in Sources */,
401 | );
402 | runOnlyForDeploymentPostprocessing = 0;
403 | };
404 | /* End PBXSourcesBuildPhase section */
405 |
406 | /* Begin PBXTargetDependency section */
407 | 00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
408 | isa = PBXTargetDependency;
409 | target = 13B07F861A680F5B00A75B9A /* Example */;
410 | targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
411 | };
412 | /* End PBXTargetDependency section */
413 |
414 | /* Begin XCBuildConfiguration section */
415 | 00E356F61AD99517003FC87E /* Debug */ = {
416 | isa = XCBuildConfiguration;
417 | baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-Example-ExampleTests.debug.xcconfig */;
418 | buildSettings = {
419 | BUNDLE_LOADER = "$(TEST_HOST)";
420 | GCC_PREPROCESSOR_DEFINITIONS = (
421 | "DEBUG=1",
422 | "$(inherited)",
423 | );
424 | INFOPLIST_FILE = ExampleTests/Info.plist;
425 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
426 | LD_RUNPATH_SEARCH_PATHS = (
427 | "$(inherited)",
428 | "@executable_path/Frameworks",
429 | "@loader_path/Frameworks",
430 | );
431 | OTHER_LDFLAGS = (
432 | "-ObjC",
433 | "-lc++",
434 | "$(inherited)",
435 | );
436 | PRODUCT_BUNDLE_IDENTIFIER = com.iaphub.example;
437 | PRODUCT_NAME = "$(TARGET_NAME)";
438 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example";
439 | };
440 | name = Debug;
441 | };
442 | 00E356F71AD99517003FC87E /* Release */ = {
443 | isa = XCBuildConfiguration;
444 | baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-Example-ExampleTests.release.xcconfig */;
445 | buildSettings = {
446 | BUNDLE_LOADER = "$(TEST_HOST)";
447 | COPY_PHASE_STRIP = NO;
448 | INFOPLIST_FILE = ExampleTests/Info.plist;
449 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
450 | LD_RUNPATH_SEARCH_PATHS = (
451 | "$(inherited)",
452 | "@executable_path/Frameworks",
453 | "@loader_path/Frameworks",
454 | );
455 | OTHER_LDFLAGS = (
456 | "-ObjC",
457 | "-lc++",
458 | "$(inherited)",
459 | );
460 | PRODUCT_BUNDLE_IDENTIFIER = com.iaphub.example;
461 | PRODUCT_NAME = "$(TARGET_NAME)";
462 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example";
463 | };
464 | name = Release;
465 | };
466 | 13B07F941A680F5B00A75B9A /* Debug */ = {
467 | isa = XCBuildConfiguration;
468 | baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-Example.debug.xcconfig */;
469 | buildSettings = {
470 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
471 | CLANG_ENABLE_MODULES = YES;
472 | CURRENT_PROJECT_VERSION = 1;
473 | DEVELOPMENT_TEAM = ZT88ZUX49N;
474 | ENABLE_BITCODE = NO;
475 | INFOPLIST_FILE = Example/Info.plist;
476 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
477 | LD_RUNPATH_SEARCH_PATHS = (
478 | "$(inherited)",
479 | "@executable_path/Frameworks",
480 | );
481 | MARKETING_VERSION = 1.0;
482 | OTHER_LDFLAGS = (
483 | "$(inherited)",
484 | "-ObjC",
485 | "-lc++",
486 | );
487 | PRODUCT_BUNDLE_IDENTIFIER = com.iaphub.example;
488 | PRODUCT_NAME = Example;
489 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
490 | SWIFT_VERSION = 5.0;
491 | VERSIONING_SYSTEM = "apple-generic";
492 | };
493 | name = Debug;
494 | };
495 | 13B07F951A680F5B00A75B9A /* Release */ = {
496 | isa = XCBuildConfiguration;
497 | baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-Example.release.xcconfig */;
498 | buildSettings = {
499 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
500 | CLANG_ENABLE_MODULES = YES;
501 | CURRENT_PROJECT_VERSION = 1;
502 | DEVELOPMENT_TEAM = ZT88ZUX49N;
503 | INFOPLIST_FILE = Example/Info.plist;
504 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
505 | LD_RUNPATH_SEARCH_PATHS = (
506 | "$(inherited)",
507 | "@executable_path/Frameworks",
508 | );
509 | MARKETING_VERSION = 1.0;
510 | OTHER_LDFLAGS = (
511 | "$(inherited)",
512 | "-ObjC",
513 | "-lc++",
514 | );
515 | PRODUCT_BUNDLE_IDENTIFIER = com.iaphub.example;
516 | PRODUCT_NAME = Example;
517 | SWIFT_VERSION = 5.0;
518 | VERSIONING_SYSTEM = "apple-generic";
519 | };
520 | name = Release;
521 | };
522 | 83CBBA201A601CBA00E9B192 /* Debug */ = {
523 | isa = XCBuildConfiguration;
524 | buildSettings = {
525 | ALWAYS_SEARCH_USER_PATHS = NO;
526 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
527 | CLANG_CXX_LANGUAGE_STANDARD = "c++20";
528 | CLANG_CXX_LIBRARY = "libc++";
529 | CLANG_ENABLE_MODULES = YES;
530 | CLANG_ENABLE_OBJC_ARC = YES;
531 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
532 | CLANG_WARN_BOOL_CONVERSION = YES;
533 | CLANG_WARN_COMMA = YES;
534 | CLANG_WARN_CONSTANT_CONVERSION = YES;
535 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
536 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
537 | CLANG_WARN_EMPTY_BODY = YES;
538 | CLANG_WARN_ENUM_CONVERSION = YES;
539 | CLANG_WARN_INFINITE_RECURSION = YES;
540 | CLANG_WARN_INT_CONVERSION = YES;
541 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
542 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
543 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
544 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
545 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
546 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
547 | CLANG_WARN_STRICT_PROTOTYPES = YES;
548 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
549 | CLANG_WARN_UNREACHABLE_CODE = YES;
550 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
551 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
552 | COPY_PHASE_STRIP = NO;
553 | ENABLE_STRICT_OBJC_MSGSEND = YES;
554 | ENABLE_TESTABILITY = YES;
555 | "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
556 | GCC_C_LANGUAGE_STANDARD = gnu99;
557 | GCC_DYNAMIC_NO_PIC = NO;
558 | GCC_NO_COMMON_BLOCKS = YES;
559 | GCC_OPTIMIZATION_LEVEL = 0;
560 | GCC_PREPROCESSOR_DEFINITIONS = (
561 | "DEBUG=1",
562 | "$(inherited)",
563 | );
564 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
565 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
566 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
567 | GCC_WARN_UNDECLARED_SELECTOR = YES;
568 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
569 | GCC_WARN_UNUSED_FUNCTION = YES;
570 | GCC_WARN_UNUSED_VARIABLE = YES;
571 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
572 | LD_RUNPATH_SEARCH_PATHS = (
573 | /usr/lib/swift,
574 | "$(inherited)",
575 | );
576 | LIBRARY_SEARCH_PATHS = (
577 | "\"$(SDKROOT)/usr/lib/swift\"",
578 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
579 | "\"$(inherited)\"",
580 | );
581 | MTL_ENABLE_DEBUG_INFO = YES;
582 | ONLY_ACTIVE_ARCH = YES;
583 | OTHER_CPLUSPLUSFLAGS = (
584 | "$(OTHER_CFLAGS)",
585 | "-DFOLLY_NO_CONFIG",
586 | "-DFOLLY_MOBILE=1",
587 | "-DFOLLY_USE_LIBCPP=1",
588 | "-DFOLLY_CFG_NO_COROUTINES=1",
589 | "-DFOLLY_HAVE_CLOCK_GETTIME=1",
590 | );
591 | OTHER_LDFLAGS = (
592 | "$(inherited)",
593 | " ",
594 | );
595 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
596 | SDKROOT = iphoneos;
597 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
598 | USE_HERMES = true;
599 | };
600 | name = Debug;
601 | };
602 | 83CBBA211A601CBA00E9B192 /* Release */ = {
603 | isa = XCBuildConfiguration;
604 | buildSettings = {
605 | ALWAYS_SEARCH_USER_PATHS = NO;
606 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
607 | CLANG_CXX_LANGUAGE_STANDARD = "c++20";
608 | CLANG_CXX_LIBRARY = "libc++";
609 | CLANG_ENABLE_MODULES = YES;
610 | CLANG_ENABLE_OBJC_ARC = YES;
611 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
612 | CLANG_WARN_BOOL_CONVERSION = YES;
613 | CLANG_WARN_COMMA = YES;
614 | CLANG_WARN_CONSTANT_CONVERSION = YES;
615 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
616 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
617 | CLANG_WARN_EMPTY_BODY = YES;
618 | CLANG_WARN_ENUM_CONVERSION = YES;
619 | CLANG_WARN_INFINITE_RECURSION = YES;
620 | CLANG_WARN_INT_CONVERSION = YES;
621 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
622 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
623 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
624 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
625 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
626 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
627 | CLANG_WARN_STRICT_PROTOTYPES = YES;
628 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
629 | CLANG_WARN_UNREACHABLE_CODE = YES;
630 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
631 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
632 | COPY_PHASE_STRIP = YES;
633 | ENABLE_NS_ASSERTIONS = NO;
634 | ENABLE_STRICT_OBJC_MSGSEND = YES;
635 | "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
636 | GCC_C_LANGUAGE_STANDARD = gnu99;
637 | GCC_NO_COMMON_BLOCKS = YES;
638 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
639 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
640 | GCC_WARN_UNDECLARED_SELECTOR = YES;
641 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
642 | GCC_WARN_UNUSED_FUNCTION = YES;
643 | GCC_WARN_UNUSED_VARIABLE = YES;
644 | IPHONEOS_DEPLOYMENT_TARGET = 15.1;
645 | LD_RUNPATH_SEARCH_PATHS = (
646 | /usr/lib/swift,
647 | "$(inherited)",
648 | );
649 | LIBRARY_SEARCH_PATHS = (
650 | "\"$(SDKROOT)/usr/lib/swift\"",
651 | "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"",
652 | "\"$(inherited)\"",
653 | );
654 | MTL_ENABLE_DEBUG_INFO = NO;
655 | OTHER_CPLUSPLUSFLAGS = (
656 | "$(OTHER_CFLAGS)",
657 | "-DFOLLY_NO_CONFIG",
658 | "-DFOLLY_MOBILE=1",
659 | "-DFOLLY_USE_LIBCPP=1",
660 | "-DFOLLY_CFG_NO_COROUTINES=1",
661 | "-DFOLLY_HAVE_CLOCK_GETTIME=1",
662 | );
663 | OTHER_LDFLAGS = (
664 | "$(inherited)",
665 | " ",
666 | );
667 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
668 | SDKROOT = iphoneos;
669 | USE_HERMES = true;
670 | VALIDATE_PRODUCT = YES;
671 | };
672 | name = Release;
673 | };
674 | /* End XCBuildConfiguration section */
675 |
676 | /* Begin XCConfigurationList section */
677 | 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ExampleTests" */ = {
678 | isa = XCConfigurationList;
679 | buildConfigurations = (
680 | 00E356F61AD99517003FC87E /* Debug */,
681 | 00E356F71AD99517003FC87E /* Release */,
682 | );
683 | defaultConfigurationIsVisible = 0;
684 | defaultConfigurationName = Release;
685 | };
686 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Example" */ = {
687 | isa = XCConfigurationList;
688 | buildConfigurations = (
689 | 13B07F941A680F5B00A75B9A /* Debug */,
690 | 13B07F951A680F5B00A75B9A /* Release */,
691 | );
692 | defaultConfigurationIsVisible = 0;
693 | defaultConfigurationName = Release;
694 | };
695 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Example" */ = {
696 | isa = XCConfigurationList;
697 | buildConfigurations = (
698 | 83CBBA201A601CBA00E9B192 /* Debug */,
699 | 83CBBA211A601CBA00E9B192 /* Release */,
700 | );
701 | defaultConfigurationIsVisible = 0;
702 | defaultConfigurationName = Release;
703 | };
704 | /* End XCConfigurationList section */
705 | };
706 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
707 | }
708 |
--------------------------------------------------------------------------------
/Example/ios/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/Example/ios/Example.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/ios/Example/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : RCTAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/Example/ios/Example/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 |
3 | #import
4 |
5 | @implementation AppDelegate
6 |
7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
8 | {
9 | self.moduleName = @"Example";
10 | // You can add your custom initial props in the dictionary below.
11 | // They will be passed down to the ViewController used by React Native.
12 | self.initialProps = @{};
13 |
14 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
15 | }
16 |
17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
18 | {
19 | return [self bundleURL];
20 | }
21 |
22 | - (NSURL *)bundleURL
23 | {
24 | #if DEBUG
25 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
26 | #else
27 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
28 | #endif
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/Example/ios/Example/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "scale" : "1x",
46 | "size" : "1024x1024"
47 | }
48 | ],
49 | "info" : {
50 | "author" : "xcode",
51 | "version" : 1
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Example/ios/Example/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/ios/Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Example
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 |
30 | NSAllowsArbitraryLoads
31 |
32 | NSAllowsLocalNetworking
33 |
34 |
35 | NSLocationWhenInUseUsageDescription
36 |
37 | UILaunchStoryboardName
38 | LaunchScreen
39 | UIRequiredDeviceCapabilities
40 |
41 | arm64
42 |
43 | UISupportedInterfaceOrientations
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 | UIViewControllerBasedStatusBarAppearance
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Example/ios/Example/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Example/ios/Example/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyAccessedAPITypes
6 |
7 |
8 | NSPrivacyAccessedAPIType
9 | NSPrivacyAccessedAPICategoryUserDefaults
10 | NSPrivacyAccessedAPITypeReasons
11 |
12 | CA92.1
13 |
14 |
15 |
16 | NSPrivacyAccessedAPIType
17 | NSPrivacyAccessedAPICategoryFileTimestamp
18 | NSPrivacyAccessedAPITypeReasons
19 |
20 | C617.1
21 |
22 |
23 |
24 | NSPrivacyAccessedAPIType
25 | NSPrivacyAccessedAPICategorySystemBootTime
26 | NSPrivacyAccessedAPITypeReasons
27 |
28 | 35F9.1
29 |
30 |
31 |
32 | NSPrivacyCollectedDataTypes
33 |
34 | NSPrivacyTracking
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Example/ios/Example/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | @autoreleasepool {
8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Example/ios/ExampleTests/ExampleTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface ExampleTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation ExampleTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(
38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
39 | if (level >= RCTLogLevelError) {
40 | redboxError = message;
41 | }
42 | });
43 | #endif
44 |
45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
48 |
49 | foundElement = [self findSubviewInView:vc.view
50 | matching:^BOOL(UIView *view) {
51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
52 | return YES;
53 | }
54 | return NO;
55 | }];
56 | }
57 |
58 | #ifdef DEBUG
59 | RCTSetLogFunction(RCTDefaultLogFunction);
60 | #endif
61 |
62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/Example/ios/ExampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Resolve react_native_pods.rb with node to allow for hoisting
2 | require Pod::Executable.execute_command('node', ['-p',
3 | 'require.resolve(
4 | "react-native/scripts/react_native_pods.rb",
5 | {paths: [process.argv[1]]},
6 | )', __dir__]).strip
7 |
8 | platform :ios, min_ios_version_supported
9 | prepare_react_native_project!
10 |
11 | linkage = ENV['USE_FRAMEWORKS']
12 | if linkage != nil
13 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
14 | use_frameworks! :linkage => linkage.to_sym
15 | end
16 |
17 | target 'Example' do
18 | config = use_native_modules!
19 |
20 | use_react_native!(
21 | :path => config[:reactNativePath],
22 | # An absolute path to your application root.
23 | :app_path => "#{Pod::Config.instance.installation_root}/.."
24 | )
25 |
26 | target 'ExampleTests' do
27 | inherit! :complete
28 | # Pods for testing
29 | end
30 |
31 | post_install do |installer|
32 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
33 | react_native_post_install(
34 | installer,
35 | config[:reactNativePath],
36 | :mac_catalyst_enabled => false,
37 | # :ccache_enabled => true
38 | )
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/Example/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'react-native',
3 | };
4 |
--------------------------------------------------------------------------------
/Example/metro.config.js:
--------------------------------------------------------------------------------
1 | const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
2 | const path = require('path');
3 | const exclusionList = require('metro-config/src/defaults/exclusionList');
4 | const escape = require('escape-string-regexp');
5 | const rootPath = path.resolve(__dirname, '..');
6 |
7 | const exclude = ['react', 'react-native', '@babel/runtime'];
8 |
9 | const extraNodeModules = {
10 | 'react': path.resolve(__dirname + '/node_modules/react'),
11 | 'react-native': path.resolve(__dirname + '/node_modules/react-native'),
12 | '@babel/runtime': path.resolve(__dirname + '/node_modules/@babel/runtime'),
13 | 'react-native-iaphub': path.resolve(__dirname + '/..'),
14 | 'react-native-iaphub-ui': path.resolve(__dirname + '/..')
15 | };
16 |
17 | const watchFolders = [
18 | path.resolve(__dirname + '/..')
19 | ];
20 |
21 | const config = {
22 | resolver: {
23 | blacklistRE: exclusionList(exclude.map((name) => new RegExp(`^${escape(path.join(rootPath, 'node_modules', name))}\\/.*$`))),
24 | extraNodeModules: extraNodeModules
25 | },
26 | watchFolders,
27 | transformer: {
28 | getTransformOptions: async () => ({
29 | transform: {
30 | experimentalImportSupport: false,
31 | inlineRequires: true,
32 | },
33 | }),
34 | },
35 | };
36 |
37 | module.exports = mergeConfig(getDefaultConfig(__dirname), config);
--------------------------------------------------------------------------------
/Example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.iaphub.example",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android",
7 | "ios": "react-native run-ios",
8 | "lint": "eslint .",
9 | "start": "react-native start",
10 | "test": "jest"
11 | },
12 | "dependencies": {
13 | "@react-native-async-storage/async-storage": "^2.1.0",
14 | "mobx": "^6.13.5",
15 | "mobx-react": "^7.6.0",
16 | "react": "18.3.1",
17 | "react-native": "0.76.5",
18 | "react-native-iaphub": "../"
19 | },
20 | "devDependencies": {
21 | "@babel/core": "^7.25.2",
22 | "@babel/preset-env": "^7.25.3",
23 | "@babel/runtime": "^7.25.0",
24 | "@react-native-community/cli": "15.0.1",
25 | "@react-native-community/cli-platform-android": "15.0.1",
26 | "@react-native-community/cli-platform-ios": "15.0.1",
27 | "@react-native/babel-preset": "0.76.5",
28 | "@react-native/eslint-config": "0.76.5",
29 | "@react-native/metro-config": "0.76.5",
30 | "@react-native/typescript-config": "0.76.5",
31 | "@types/react": "^18.2.6",
32 | "@types/react-test-renderer": "^18.0.0",
33 | "babel-jest": "^29.6.3",
34 | "eslint": "^8.19.0",
35 | "jest": "^29.6.3",
36 | "prettier": "2.8.8",
37 | "react-test-renderer": "18.3.1",
38 | "typescript": "5.0.4"
39 | },
40 | "engines": {
41 | "node": ">=18"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {View, StyleSheet} from 'react-native';
3 | import {Observer} from 'mobx-react';
4 | import IAP from './pages/iap';
5 | import Login from './pages/login';
6 | import app from './stores/app';
7 |
8 | export default class App extends React.Component {
9 |
10 | renderContent = () => {
11 | var {isLogged} = app;
12 |
13 | return (
14 |
15 | {isLogged && }
16 | {!isLogged && }
17 |
18 | )
19 | }
20 |
21 | render() {
22 | return (
23 |
24 | {this.renderContent}
25 |
26 | )
27 | }
28 |
29 | }
30 |
31 | const styles = StyleSheet.create({
32 | root: {
33 | flex: 1,
34 | backgroundColor: 'white'
35 | }
36 | });
--------------------------------------------------------------------------------
/Example/src/pages/iap.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, View, ScrollView, Text, Button, TouchableOpacity, ActivityIndicator} from 'react-native';
3 | import {Observer} from 'mobx-react';
4 | import app from '../stores/app';
5 | import iap from '../stores/iap';
6 |
7 | export default class IAPPage extends React.Component {
8 |
9 | renderPrice = (product) => {
10 | // Convert iso duration to human
11 | var isoToHuman = (isoDuration) => {
12 | if (!isoDuration) return;
13 | if (isoDuration == "P7D") isoDuration = "P1W";
14 | var number = isoDuration.match(/\d+/g);
15 | var periods = {"D": "day", "W": "week", "M": "month", "Y": "year"};
16 | var period = isoDuration[isoDuration.length - 1];
17 |
18 | return `${number > 1 ? `${number} ` : ''}${periods[period]}${number > 1 ? 's' : ''}`;
19 | };
20 |
21 | // Renewable subscriptions
22 | if (product.type == "renewable_subscription") {
23 | // Trial
24 | if (product.subscriptionPeriodType == "trial") {
25 | return `Enjoy a free trial during ${isoToHuman(product.subscriptionTrialDuration)} (then ${product.price} every ${isoToHuman(product.subscriptionDuration)})`;
26 | }
27 | // As you go introductory offer
28 | else if (product.subscriptionPeriodType == "intro" && product.subscriptionIntroPayment == "as_you_go") {
29 | return `Enjoy an introductory offer of ${product.subscriptionIntroPrice} every ${isoToHuman(product.subscriptionIntroDuration)} (then ${product.price} every ${isoToHuman(product.subscriptionDuration)})`;
30 | }
31 | // Upfront introductory offer
32 | else if (product.subscriptionPeriodType == "intro" && product.subscriptionIntroPayment == "upfront") {
33 | return `Enjoy an introductory offer of ${product.subscriptionIntroPrice} for ${isoToHuman(product.subscriptionIntroDuration)} (then ${product.price} every ${isoToHuman(product.subscriptionDuration)})`;
34 | }
35 | else {
36 | return `${product.localizedPrice} / ${isoToHuman(product.subscriptionDuration)}`;
37 | }
38 | }
39 | // Non-renewable subscriptions
40 | if (product.type == "subscription") {
41 | return `${product.localizedPrice} / ${isoToHuman(product.subscriptionDuration)}`;
42 | }
43 | // Everything else
44 | else {
45 | return product.localizedPrice || 'Price not available';
46 | }
47 | }
48 |
49 | renderProduct = (product, onPress) => {
50 | var {skuProcessing} = iap;
51 |
52 | return (
53 |
54 |
55 |
56 | {product.localizedTitle || 'Title not available'}
57 | {this.renderPrice(product)}
58 |
59 | {(product.sku && skuProcessing == product.sku) && }
60 |
61 |
62 | );
63 | }
64 |
65 | renderEmpty = (label) => {
66 | return (
67 |
68 | {label}
69 |
70 | )
71 | }
72 |
73 | renderBillingError = (error) => {
74 | var description = "Billing not available, please try again later";
75 |
76 | if (error.subcode == "play_store_outdated") {
77 | description = "Billing not available, you must update your Play Store App";
78 | }
79 |
80 | return this.renderEmpty(description);
81 | }
82 |
83 | renderProductsForSale = () => {
84 | var {productsForSale, billingStatus} = iap;
85 | var groups = {};
86 |
87 | if (productsForSale) {
88 | productsForSale.forEach((product) => {
89 | var groupName = product.groupName || "default";
90 |
91 | if (!groups[groupName]) {
92 | groups[groupName] = [];
93 | }
94 | groups[groupName].push(product);
95 | });
96 | }
97 | return (
98 |
99 | Products for sale
100 | {!productsForSale &&
101 |
102 |
103 |
104 | }
105 | {productsForSale && !productsForSale.length && (!billingStatus || !billingStatus.error) && this.renderEmpty("No products for sale")}
106 | {productsForSale && !productsForSale.length && billingStatus && billingStatus.error && this.renderBillingError(billingStatus.error)}
107 | {Object.keys(groups).map((groupName) => (
108 |
109 | {groupName}
110 | {groups[groupName].map((product) => this.renderProduct(product, () => iap.buy(product.sku)))}
111 |
112 | ))}
113 |
114 | );
115 | }
116 |
117 | renderActiveProducts = () => {
118 | var {activeProducts} = iap;
119 |
120 | return (
121 |
122 | Active products
123 | {!activeProducts &&
124 |
125 |
126 |
127 | }
128 | {activeProducts && !activeProducts.length && this.renderEmpty("No active products (Subscriptions, non-consumables)")}
129 | {activeProducts && activeProducts.map((product) => this.renderProduct(product, () => iap.buy(product.sku)))}
130 |
131 | );
132 | }
133 |
134 | renderRestore = () => {
135 | return (
136 |
137 | Restore
138 |
139 |
140 | );
141 | }
142 |
143 | renderShowManageSubscriptions = () => {
144 | return (
145 |
146 | Manage subscriptions
147 |
148 |
149 | );
150 | }
151 |
152 | renderPromoCode = () => {
153 | return (
154 |
155 | Promo code
156 |
157 |
158 | );
159 | }
160 |
161 | renderLogout = () => {
162 | return (
163 |
164 | Logout
165 |
166 |
167 | );
168 | }
169 |
170 | renderContent = () => {
171 | var {isInitialized, isProcessing} = iap;
172 |
173 | if (!isInitialized || isProcessing) {
174 | return (
175 |
176 |
177 |
178 | )
179 | }
180 | return (
181 |
182 |
183 | {this.renderProductsForSale()}
184 | {this.renderActiveProducts()}
185 | {this.renderRestore()}
186 | {Platform.OS == 'ios' && this.renderPromoCode()}
187 | {this.renderShowManageSubscriptions()}
188 | {this.renderLogout()}
189 |
190 |
191 | );
192 | }
193 |
194 | render() {
195 | return (
196 |
197 | {this.renderContent}
198 |
199 | )
200 | }
201 |
202 | }
203 |
204 | const styles = StyleSheet.create({
205 | root: {
206 | flex: 1,
207 | paddingTop: 40,
208 | paddingBottom: 40,
209 | paddingLeft: 20,
210 | paddingRight: 20
211 | },
212 | title: {
213 | color: '#111566',
214 | fontWeight: 'bold',
215 | fontSize: 24,
216 | marginTop: 20,
217 | marginBottom: 20
218 | },
219 | // Loader
220 | loader: {
221 | flex: 1,
222 | alignContent: 'center',
223 | justifyContent: 'center'
224 | },
225 | // Empty
226 | empty: {
227 | textAlign: 'center',
228 | color: 'black'
229 | },
230 | // Group
231 | groupTitle: {
232 | color: '#111566',
233 | fontWeight: 'bold',
234 | fontSize: 16,
235 | marginTop: 20,
236 | marginBottom: 10
237 | },
238 | // Product
239 | product: {
240 | flexDirection: 'row',
241 | padding: 10,
242 | backgroundColor: 'white',
243 | ...Platform.select({
244 | ios: {
245 | shadowOpacity: 1,
246 | shadowRadius: 2,
247 | shadowOffset: {height: 1, width: 1},
248 | shadowColor: '#979797'
249 | },
250 | android: {
251 | elevation: 2
252 | }
253 | })
254 | },
255 | productDetails: {
256 | flex: 1
257 | },
258 | productTitle: {
259 | fontWeight: 'bold',
260 | fontSize: 16,
261 | color: 'black'
262 | },
263 | productPrice: {
264 | fontSize: 14,
265 | color: '#818181'
266 | }
267 | });
--------------------------------------------------------------------------------
/Example/src/pages/login.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {StyleSheet, View, Button} from 'react-native';
3 | import app from '../stores/app';
4 |
5 | export default class LoginPage extends React.Component {
6 |
7 | render() {
8 | return (
9 |
10 |
11 |
12 |
13 | )
14 | }
15 |
16 | }
17 |
18 | const styles = StyleSheet.create({
19 | root: {
20 | flex: 1,
21 | justifyContent: 'center',
22 | alignItems: 'center'
23 | }
24 | });
--------------------------------------------------------------------------------
/Example/src/stores/app.js:
--------------------------------------------------------------------------------
1 | import {makeAutoObservable} from "mobx"
2 | import AsyncStorage from '@react-native-async-storage/async-storage';
3 | import iap from './iap';
4 |
5 | class AppStore {
6 |
7 | isLogged = false;
8 |
9 | constructor() {
10 | makeAutoObservable(this)
11 | this.start()
12 | }
13 |
14 | start = async () => {
15 | await iap.init();
16 | this.isLogged = (await AsyncStorage.getItem('isLogged')) == "true" ? true : false;
17 | if (this.isLogged) {
18 | await this.login();
19 | }
20 | }
21 |
22 | login = async () => {
23 | await iap.login("42");
24 | await iap.refreshProducts();
25 | await AsyncStorage.setItem('isLogged', "true");
26 | this.isLogged = true;
27 | }
28 |
29 | loginAnonymously = async () => {
30 | await iap.refreshProducts();
31 | await AsyncStorage.setItem('isLogged', "false");
32 | this.isLogged = true;
33 | }
34 |
35 | logout = async () => {
36 | await iap.logout();
37 | await AsyncStorage.setItem('isLogged', "false");
38 | this.isLogged = false;
39 | }
40 |
41 | }
42 |
43 | export default new AppStore();
--------------------------------------------------------------------------------
/Example/src/stores/iap.js:
--------------------------------------------------------------------------------
1 | import {makeAutoObservable, configure} from "mobx"
2 | import {Alert} from 'react-native';
3 | import Iaphub from 'react-native-iaphub';
4 | import pkg from '../../package.json';
5 |
6 | configure({enforceActions: "never"});
7 |
8 | class IAPStore {
9 |
10 | isInitialized = false;
11 | skuProcessing = null;
12 | productsForSale = null;
13 | activeProducts = null;
14 | billingStatus = null;
15 |
16 | constructor() {
17 | makeAutoObservable(this)
18 | }
19 |
20 | // Init IAPHUB
21 | async init() {
22 | // Init iaphub2
23 | await Iaphub.start({
24 | // The app id is available on the settings page of your app
25 | appId: "5e4890f6c61fc971cf46db4d",
26 | // The (client) api key is available on the settings page of your app
27 | apiKey: "SDp7aY220RtzZrsvRpp4BGFm6qZqNkNf",
28 | // Allow anonymous purchase
29 | allowAnonymousPurchase: true,
30 | // Enable StoreKit V2 if supported by the phone (iOS 15+)
31 | enableStorekitV2: true,
32 | // Add lang
33 | lang: "en"
34 | });
35 | // Add device params
36 | await Iaphub.setDeviceParams({appVersion: pkg.version});
37 | // Iaphub is now initialized and ready to use
38 | this.isInitialized = true;
39 | // Listen to user updates and refresh productsForSale/activeProducts
40 | Iaphub.addEventListener('onUserUpdate', async () => {
41 | console.log("-> Got user update");
42 | await this.refreshProducts();
43 | });
44 | Iaphub.addEventListener('onDeferredPurchase', async (transaction) => {
45 | console.log("-> Got deferred purchase: ", transaction);
46 | });
47 | Iaphub.addEventListener('onError', async (err) => {
48 | console.log("-> Got err: ", err);
49 | });
50 | Iaphub.addEventListener('onReceipt', async (receipt) => {
51 | //console.log("-> Got receipt: ", receipt);
52 | });
53 | Iaphub.addEventListener('onBuyRequest', async (opts) => {
54 | console.log("-> Got buy request: ", opts);
55 | });
56 | }
57 |
58 | // Login
59 | async login(userId) {
60 | await Iaphub.login(userId);
61 | }
62 |
63 | // Logout
64 | async logout() {
65 | await Iaphub.logout();
66 | }
67 |
68 | // Refresh products
69 | async refreshProducts() {
70 | try {
71 | // Refresh products
72 | var products = await Iaphub.getProducts();
73 | this.activeProducts = products.activeProducts;
74 | this.productsForSale = products.productsForSale;
75 | console.log('activeProducts: ', products.activeProducts);
76 | console.log('productsForSale: ', products.productsForSale);
77 | // Resfresh billing status
78 | this.billingStatus = await Iaphub.getBillingStatus();
79 | }
80 | catch (err) {
81 | console.error(err);
82 | }
83 | }
84 |
85 | // Call this method when an user click on one of your products
86 | async buy(productSku) {
87 | try {
88 | this.skuProcessing = productSku;
89 | var transaction = await Iaphub.buy(productSku);
90 | console.log('purchase: ', transaction);
91 | this.skuProcessing = null;
92 | // The webhook could not been sent to my server (Only needed if you're relying on webhooks)
93 | if (transaction.webhookStatus == "failed") {
94 | Alert.alert(
95 | "Purchase delayed",
96 | "Your purchase was successful but we need some more time to validate it, should arrive soon! Otherwise contact the support (support@myapp.com)"
97 | );
98 | }
99 | // Everything was successful! Yay!
100 | else {
101 | Alert.alert(
102 | "Purchase successful",
103 | "Your purchase has been processed successfully!"
104 | );
105 | }
106 | } catch (err) {
107 | // Stop loading
108 | this.skuProcessing = null;
109 | // Display error
110 | var errors = {
111 | // Couldn't buy product because it has been bought in the past but hasn't been consumed (restore needed)
112 | "product_already_owned": "Product already owned, please restore your purchases in order to fix that issue",
113 | // The payment has been deferred (its final status is pending external action such as 'Ask to Buy')
114 | "deferred_payment": "Purchase awaiting approval, your purchase has been processed but is awaiting approval",
115 | // The billing is unavailable (An iPhone can be restricted from accessing the Apple App Store)
116 | "billing_unavailable": "In-app purchase not allowed",
117 | // The remote server couldn't be reached properly
118 | "network_error": "Network error, please try to restore your purchases later (Button in the settings) or contact the support (support@myapp.com)",
119 | // The receipt has been processed on IAPHUB but something went wrong
120 | "receipt_failed": "We're having trouble validating your transaction, give us some time, we'll retry to validate your transaction ASAP",
121 | // The user has already an active subscription on a different platform (android or ios)
122 | "cross_platform_conflict": "It seems like you already have a subscription on a different platform, please use the same platform to change your subscription or wait for your current subscription to expire",
123 | // The transaction is successful but the product belongs to a different user
124 | "user_conflict": "Product owned by a different user, please use the account with which you originally bought the product or restore your purchases",
125 | // Unknown
126 | "unexpected": "We were not able to process your purchase, please try again later or contact the support (support@myapp.com)"
127 | };
128 | var errorsToIgnore = ["user_cancelled", "product_already_purchased"];
129 | var errorMessage = errors[err.code];
130 |
131 | if (!errorMessage && errorsToIgnore.indexOf(err.code) == -1) {
132 | errorMessage = errors["unexpected"];
133 | }
134 | if (errorMessage) {
135 | Alert.alert("Error", errorMessage);
136 | }
137 | }
138 | }
139 |
140 | // Call this method to restore the user purchases (you should have a button, it is usually displayed on the settings page)
141 | async restore() {
142 | var response = await Iaphub.restore();
143 | console.log("Response response: ", response);
144 | Alert.alert("Restore", "Purchases restored");
145 | }
146 |
147 | // Call this method to show the subscriptions page
148 | async showManageSubscriptions() {
149 | await Iaphub.showManageSubscriptions();
150 | }
151 |
152 | // Call this method to present the ios code redemption sheet
153 | async presentCodeRedemptionSheet() {
154 | await Iaphub.presentCodeRedemptionSheet();
155 | }
156 |
157 | }
158 |
159 | export default new IAPStore();
--------------------------------------------------------------------------------
/Example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@react-native/typescript-config/tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 IAPHUB
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 
8 | 
9 |
10 |
11 |
12 |
13 | We've simplified in-app purchases for you. Say goodbye to wasting time and resources reinventing the wheel.
14 | At [IAPHUB](https://www.iaphub.com), we've developed all the necessary tools for selling in-app purchases and subscriptions, managing your customers, and boosting your revenue. 🚀
15 |
16 |
17 | ## Getting Started
18 |
19 | Please refer to our [Quickstart Guide](https://www.iaphub.com/docs/getting-started/?sdk=react_native) for step-by-step instructions on integrating IAPHUB into your React Native app.
20 |
21 | You can also learn all about our SDK methods in our comprehensive [SDK Reference](https://www.iaphub.com/docs/sdk-reference/?sdk=react_native).
22 |
23 | ## UI
24 |
25 | The package provides all the methods of the IAPHUB SDK for easy implementation of in-app purchases, but it does not include any user interface (UI).
26 | If you prefer a plug-and-play component with built-in UI, consider exploring [react-native-iaphub-ui](https://github.com/iaphub/react-native-iaphub-ui).
27 |
28 | ## Upgrades
29 |
30 | If you're upgrading to a new major version of the plugin, please consult our migration guides:
31 | - For transitioning from v7.X.X to v8.X.X, refer to [this guide](https://github.com/iaphub/react-native-iaphub/tree/master/guides/migrate-v7-to-v8.md).
32 | - For transitioning from v6.X.X to v7.X.X, refer to [this guide](https://github.com/iaphub/react-native-iaphub/tree/master/guides/migrate-v6-to-v7.md).
33 |
34 | ## Example
35 |
36 | You can check our [Example app](https://github.com/iaphub/react-native-iaphub/tree/master/Example).
37 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | // Buildscript is evaluated before everything else so we can't use getExtOrDefault
3 | def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['Iaphub_kotlinVersion']
4 |
5 | repositories {
6 | google()
7 | mavenCentral()
8 | jcenter()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:3.2.1'
13 | // noinspection DifferentKotlinGradleVersion
14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
15 | }
16 | }
17 |
18 | apply plugin: 'com.android.library'
19 | apply plugin: 'kotlin-android'
20 |
21 | def getExtOrDefault(name) {
22 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['Iaphub_' + name]
23 | }
24 |
25 | def getExtOrIntegerDefault(name) {
26 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['Iaphub_' + name]).toInteger()
27 | }
28 |
29 | android {
30 | compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
31 | defaultConfig {
32 | minSdkVersion getExtOrIntegerDefault('minSdkVersion')
33 | targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
34 | versionCode 1
35 | versionName "8.5.0"
36 | }
37 |
38 | buildTypes {
39 | release {
40 | minifyEnabled false
41 | }
42 | }
43 | lintOptions {
44 | disable 'GradleCompatible'
45 | }
46 | compileOptions {
47 | sourceCompatibility JavaVersion.VERSION_1_8
48 | targetCompatibility JavaVersion.VERSION_1_8
49 | }
50 | }
51 |
52 | repositories {
53 | mavenCentral()
54 | jcenter()
55 | google()
56 |
57 | def found = false
58 | def defaultDir = null
59 | def androidSourcesName = 'React Native sources'
60 |
61 | if (rootProject.ext.has('reactNativeAndroidRoot')) {
62 | defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
63 | } else {
64 | defaultDir = new File(
65 | projectDir,
66 | '/../../../node_modules/react-native/android'
67 | )
68 | }
69 |
70 | if (defaultDir.exists()) {
71 | maven {
72 | url defaultDir.toString()
73 | name androidSourcesName
74 | }
75 |
76 | logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
77 | found = true
78 | } else {
79 | def parentDir = rootProject.projectDir
80 |
81 | 1.upto(5, {
82 | if (found) return true
83 | parentDir = parentDir.parentFile
84 |
85 | def androidSourcesDir = new File(
86 | parentDir,
87 | 'node_modules/react-native'
88 | )
89 |
90 | def androidPrebuiltBinaryDir = new File(
91 | parentDir,
92 | 'node_modules/react-native/android'
93 | )
94 |
95 | if (androidPrebuiltBinaryDir.exists()) {
96 | maven {
97 | url androidPrebuiltBinaryDir.toString()
98 | name androidSourcesName
99 | }
100 |
101 | logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
102 | found = true
103 | } else if (androidSourcesDir.exists()) {
104 | maven {
105 | url androidSourcesDir.toString()
106 | name androidSourcesName
107 | }
108 |
109 | logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
110 | found = true
111 | }
112 | })
113 | }
114 |
115 | if (!found) {
116 | throw new FileNotFoundException(
117 | "${project.name}: unable to locate React Native android sources. " +
118 | "Ensure you have you installed React Native as a dependency in your project and try again."
119 | )
120 | }
121 | }
122 |
123 | def kotlin_version = getExtOrDefault('kotlinVersion')
124 |
125 | dependencies {
126 | // noinspection GradleDynamicVersion
127 | api 'com.facebook.react:react-native:+'
128 | implementation 'com.iaphub:iaphub-android-sdk:4.6.6'
129 | implementation 'com.google.code.gson:gson:2.8.9'
130 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
131 | }
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | Iaphub_kotlinVersion=1.6.0
2 | Iaphub_compileSdkVersion=29
3 | Iaphub_targetSdkVersion=29
4 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/java/com/iaphub/RNIaphub/RNIaphubModule.kt:
--------------------------------------------------------------------------------
1 | package com.iaphub.RNIaphub
2 |
3 | import com.google.gson.Gson
4 | import com.facebook.react.bridge.*
5 | import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
6 |
7 | import com.iaphub.Iaphub
8 | import com.iaphub.IaphubError
9 | import com.facebook.react.bridge.ReactMethod
10 | import java.math.BigDecimal
11 |
12 | class RNIaphubModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
13 |
14 | override fun getName(): String {
15 | return "RNIaphub"
16 | }
17 |
18 | /**
19 | * Required for the EventEmitter to work properly without a warning
20 | */
21 | @ReactMethod
22 | @Suppress("UNUSED_PARAMETER")
23 | fun addListener(eventName: String?) {
24 |
25 | }
26 |
27 | /**
28 | * Required for the EventEmitter to work properly without a warning
29 | */
30 | @ReactMethod
31 | @Suppress("UNUSED_PARAMETER")
32 | fun removeListeners(count: Int?) {
33 |
34 | }
35 |
36 | /**
37 | * Start IAPHUB
38 | */
39 | @ReactMethod
40 | fun start(options: ReadableMap, promise: Promise) {
41 | val appId = this.getString(options, "appId", "")
42 | val apiKey = this.getString(options, "apiKey", "")
43 | val userId = this.getStringOrNull(options, "userId")
44 | val allowAnonymousPurchase = this.getBoolean(options, "allowAnonymousPurchase", false)
45 | val enableDeferredPurchaseListener = this.getBoolean(options, "enableDeferredPurchaseListener", true)
46 | val environment = this.getString(options, "environment", "production")
47 | val lang = this.getString(options, "lang", "")
48 | val sdkVersion = this.getString(options, "sdkVersion", "")
49 | val extraSdk = this.getStringOrNull(options, "sdk")
50 | var sdk = "react_native"
51 |
52 | if (extraSdk != null) {
53 | sdk = "${sdk}/${extraSdk}"
54 | }
55 | // Start SDK
56 | Iaphub.start(
57 | context=this.reactApplicationContext,
58 | appId=appId,
59 | apiKey=apiKey,
60 | userId=userId,
61 | allowAnonymousPurchase=allowAnonymousPurchase,
62 | enableDeferredPurchaseListener=enableDeferredPurchaseListener,
63 | environment=environment,
64 | lang=lang,
65 | sdk=sdk,
66 | sdkVersion=sdkVersion
67 | )
68 | // Register listeners
69 | Iaphub.setOnUserUpdateListener { ->
70 | this.reactApplicationContext.getJSModule(RCTDeviceEventEmitter::class.java).emit("onUserUpdate", null)
71 | }
72 | Iaphub.setOnDeferredPurchaseListener { transaction ->
73 | this.reactApplicationContext.getJSModule(RCTDeviceEventEmitter::class.java).emit("onDeferredPurchase", this.writableMapOf(transaction.getData()))
74 | }
75 | Iaphub.setOnErrorListener { err ->
76 | this.reactApplicationContext.getJSModule(RCTDeviceEventEmitter::class.java).emit("onError", this.writableMapOf(err.getData()))
77 | }
78 | Iaphub.setOnReceiptListener { err, receipt ->
79 | this.reactApplicationContext.getJSModule(RCTDeviceEventEmitter::class.java).emit("onReceipt", this.writableMapOf(mapOf(
80 | "err" to if (err != null) this.writableMapOf(err.getData()) else null,
81 | "receipt" to if (receipt != null) receipt.getData() else null
82 | )))
83 | }
84 | // Resolve promise
85 | promise.resolve(null)
86 | }
87 |
88 | /**
89 | * Stop IAPHUB
90 | */
91 | @ReactMethod
92 | fun stop(promise: Promise) {
93 | // Stop IAPHUB
94 | Iaphub.stop()
95 | // Resolve promise
96 | promise.resolve(null)
97 | }
98 |
99 | /**
100 | * Get SDK version
101 | */
102 | @ReactMethod
103 | fun getSDKVersion(promise: Promise) {
104 | val version = Iaphub.getSDKVersion()
105 | promise.resolve(version)
106 | }
107 |
108 | /**
109 | * Set lang
110 | */
111 | @ReactMethod
112 | fun setLang(lang: String, promise: Promise) {
113 | val result = Iaphub.setLang(lang)
114 | promise.resolve(result)
115 | }
116 |
117 | /**
118 | * Login
119 | */
120 | @ReactMethod
121 | fun login(userId: String, promise: Promise) {
122 | Iaphub.login(userId) { err ->
123 | if (err != null) {
124 | this.rejectWithError(err, promise)
125 | }
126 | else {
127 | promise.resolve(null)
128 | }
129 | }
130 | }
131 |
132 | /**
133 | * Get user id
134 | */
135 | @ReactMethod
136 | fun getUserId(promise: Promise) {
137 | val userId = Iaphub.getUserId()
138 |
139 | if (userId == null) {
140 | this.rejectWithUnexpectedError("start_missing", "iaphub not started", promise)
141 | }
142 | else {
143 | promise.resolve(userId)
144 | }
145 | }
146 |
147 | /**
148 | * Logout
149 | */
150 | @ReactMethod
151 | fun logout(promise: Promise) {
152 | // Logout
153 | Iaphub.logout()
154 | // Resolve promise
155 | promise.resolve(null)
156 | }
157 |
158 | /**
159 | * Set device params
160 | */
161 | @ReactMethod
162 | fun setDeviceParams(params: ReadableMap, promise: Promise) {
163 | // Set device params
164 | Iaphub.setDeviceParams(this.mapOf(params))
165 | // Resolve promise
166 | promise.resolve(null)
167 | }
168 |
169 | /**
170 | * Set user tags
171 | */
172 | @ReactMethod
173 | fun setUserTags(tags: ReadableMap, promise: Promise) {
174 | Iaphub.setUserTags(this.mapOf(tags)) { err ->
175 | if (err != null) {
176 | this.rejectWithError(err, promise)
177 | }
178 | else {
179 | promise.resolve(null)
180 | }
181 | }
182 | }
183 |
184 | /**
185 | * Buy
186 | */
187 | @ReactMethod
188 | fun buy(sku: String, options: ReadableMap, promise: Promise) {
189 | val activity = this.currentActivity
190 | val prorationMode = this.getStringOrNull(options, "prorationMode")
191 | val crossPlatformConflict = this.getBoolean(options, "crossPlatformConflict", true)
192 |
193 | if (activity == null) {
194 | this.rejectWithUnexpectedError("activity_null", "activity not found", promise)
195 | return
196 | }
197 | Iaphub.buy(activity=activity, sku=sku, prorationMode=prorationMode, crossPlatformConflict=crossPlatformConflict, completion={ err, transaction ->
198 | if (err != null) {
199 | this.rejectWithError(err, promise)
200 | }
201 | else if (transaction == null) {
202 | this.rejectWithUnexpectedError("unexpected_parameter", "transaction returned by buy is null", promise)
203 | }
204 | else {
205 | promise.resolve(this.writableMapOf(transaction.getData()))
206 | }
207 | })
208 | }
209 |
210 | /**
211 | * Restore
212 | */
213 | @ReactMethod
214 | fun restore(promise: Promise) {
215 | Iaphub.restore { err, response ->
216 | if (err != null) {
217 | this.rejectWithError(err, promise)
218 | }
219 | else if (response == null) {
220 | this.rejectWithUnexpectedError("unexpected_parameter", "response returned by restore is null", promise)
221 | }
222 | else {
223 | promise.resolve(this.writableMapOf(response.getData()))
224 | }
225 | }
226 | }
227 |
228 | /**
229 | * Get active products
230 | */
231 | @ReactMethod
232 | fun getActiveProducts(options: ReadableMap, promise: Promise) {
233 | val includeSubscriptionStatesValue = options.getArray("includeSubscriptionStates")
234 | var includeSubscriptionStates: List = listOf()
235 |
236 | if (includeSubscriptionStatesValue != null) {
237 | includeSubscriptionStates = this.listOf(includeSubscriptionStatesValue)
238 | }
239 | Iaphub.getActiveProducts(includeSubscriptionStates) { err, products ->
240 | if (err != null) {
241 | this.rejectWithError(err, promise)
242 | }
243 | else if (products == null) {
244 | this.rejectWithUnexpectedError("unexpected_parameter", "products returned by getActiveProducts is null", promise)
245 | }
246 | else {
247 | promise.resolve(this.writableArrayOf(products.map { product -> product.getData() }))
248 | }
249 | }
250 | }
251 |
252 | /**
253 | * Get products for sale
254 | */
255 | @ReactMethod
256 | fun getProductsForSale(promise: Promise) {
257 | Iaphub.getProductsForSale { err, products ->
258 | if (err != null) {
259 | this.rejectWithError(err, promise)
260 | }
261 | else if (products == null) {
262 | this.rejectWithUnexpectedError("unexpected_parameter", "products returned by getProductsForSale is null", promise)
263 | }
264 | else {
265 | promise.resolve(this.writableArrayOf(products.map { product -> product.getData() }))
266 | }
267 | }
268 | }
269 |
270 | /**
271 | * Get products
272 | */
273 | @ReactMethod
274 | fun getProducts(options: ReadableMap, promise: Promise) {
275 | val includeSubscriptionStatesValue = options.getArray("includeSubscriptionStates")
276 | var includeSubscriptionStates: List = listOf()
277 |
278 | if (includeSubscriptionStatesValue != null) {
279 | includeSubscriptionStates = this.listOf(includeSubscriptionStatesValue)
280 | }
281 | Iaphub.getProducts(includeSubscriptionStates) { err, products ->
282 | if (err != null) {
283 | this.rejectWithError(err, promise)
284 | }
285 | else if (products == null) {
286 | this.rejectWithUnexpectedError("unexpected_parameter", "products returned by getProducts is null", promise)
287 | }
288 | else {
289 | promise.resolve(this.writableMapOf(products.getData()))
290 | }
291 | }
292 | }
293 |
294 | /**
295 | * Get billing status
296 | */
297 | @ReactMethod
298 | fun getBillingStatus(promise: Promise) {
299 | val status = Iaphub.getBillingStatus()
300 |
301 | promise.resolve(this.writableMapOf(status.getData()))
302 | }
303 |
304 | /**
305 | * Show manage subscriptions
306 | */
307 | @ReactMethod
308 | fun showManageSubscriptions(options: ReadableMap, promise: Promise) {
309 | val sku = this.getStringOrNull(options, "sku")
310 |
311 | Iaphub.showManageSubscriptions(sku) { err ->
312 | if (err != null) {
313 | this.rejectWithError(err, promise)
314 | }
315 | else {
316 | promise.resolve(null)
317 | }
318 | }
319 | }
320 |
321 | /**
322 | * Get readable map boolean
323 | */
324 | private fun getBoolean(options: ReadableMap, key: String, default: Boolean = false): Boolean {
325 | return if (options.hasKey(key)) options.getBoolean(key) else default
326 | }
327 |
328 | /**
329 | * Get readable map string
330 | */
331 | private fun getString(options: ReadableMap, key: String, default: String): String {
332 | return if (options.hasKey(key)) options.getString(key) ?: default else default
333 | }
334 |
335 | /**
336 | * Get readable map string or null
337 | */
338 | private fun getStringOrNull(options: ReadableMap, key: String): String? {
339 | return if (options.hasKey(key)) options.getString(key) else null
340 | }
341 |
342 | /**
343 | * Reject promise with error
344 | */
345 | private fun rejectWithError(err: IaphubError, promise: Promise) {
346 | val json = Gson().toJson(err.getData())
347 |
348 | promise.reject("iaphub_error", json)
349 | }
350 |
351 | /**
352 | * Create unexpected error
353 | */
354 | private fun rejectWithUnexpectedError(subcode: String, message: String, promise: Promise) {
355 | val json = Gson().toJson(mapOf(
356 | "code" to "unexpected",
357 | "subcode" to subcode,
358 | "message" to message
359 | ))
360 |
361 | promise.reject("iaphub_error", json)
362 | }
363 |
364 | /**
365 | * Method to create a writable map
366 | */
367 | @Suppress("UNCHECKED_CAST")
368 | private fun writableMapOf(data: Map?): WritableMap {
369 | val map = WritableNativeMap()
370 |
371 | if (data == null) {
372 | return map
373 | }
374 |
375 | for ((key, value) in data) {
376 | when (value) {
377 | null -> map.putNull(key)
378 | is Boolean -> map.putBoolean(key, value)
379 | is Double -> map.putDouble(key, value)
380 | is BigDecimal -> map.putDouble(key, value.toDouble())
381 | is Int -> map.putInt(key, value)
382 | is String -> map.putString(key, value)
383 | is Map<*, *> -> map.putMap(key, this.writableMapOf(value as? Map))
384 | is Array<*> -> map.putArray(key, this.writableArrayOf(value as? List))
385 | is List<*> -> map.putArray(key, this.writableArrayOf(value.toTypedArray().toList()))
386 | }
387 | }
388 | return map
389 | }
390 |
391 | /**
392 | * Method to create a writable array
393 | */
394 | @Suppress("UNCHECKED_CAST")
395 | private fun writableArrayOf(data: List?): WritableArray {
396 | val arr = WritableNativeArray()
397 |
398 | if (data == null) {
399 | return arr
400 | }
401 | for (item in data) {
402 | when (item) {
403 | null -> arr.pushNull()
404 | is Boolean -> arr.pushBoolean(item)
405 | is Int -> arr.pushInt(item)
406 | is Double -> arr.pushDouble(item)
407 | is String -> arr.pushString(item)
408 | is Map<*, *> -> arr.pushMap(this.writableMapOf(item as? Map))
409 | is Array<*> -> arr.pushArray(this.writableArrayOf(item as? List))
410 | is List<*> -> arr.pushArray(this.writableArrayOf(item.toTypedArray().toList()))
411 | }
412 | }
413 | return arr
414 | }
415 |
416 | /**
417 | * Method to convert a readable map to a map
418 | */
419 | private fun mapOf(data: ReadableMap): Map {
420 | val map: MutableMap = mutableMapOf()
421 | val iterator = data.keySetIterator()
422 |
423 | while (iterator.hasNextKey()) {
424 | val key = iterator.nextKey()
425 |
426 | when (data.getType(key)) {
427 | ReadableType.Null -> map.put(key, "")
428 | ReadableType.String -> map.put(key, data.getString(key) ?: "")
429 | else -> throw IllegalArgumentException("Unsupported value type for key [$key]")
430 | }
431 | }
432 |
433 | return map.toMap()
434 | }
435 |
436 | /**
437 | * Method to convert a readable array to a list
438 | */
439 | private fun listOf(data: ReadableArray): List {
440 | val list: MutableList = mutableListOf()
441 |
442 | for (index in 0 until data.size()) {
443 | when (data.getType(index)) {
444 | ReadableType.String -> list.add(data.getString(index) ?: "")
445 | else -> throw IllegalArgumentException("Unsupported value, must be a string")
446 | }
447 | }
448 |
449 | return list.toList()
450 | }
451 |
452 | }
453 |
--------------------------------------------------------------------------------
/android/src/main/java/com/iaphub/RNIaphub/RNIaphubPackage.kt:
--------------------------------------------------------------------------------
1 | package com.iaphub.RNIaphub
2 |
3 | import com.facebook.react.ReactPackage
4 | import com.facebook.react.bridge.NativeModule
5 | import com.facebook.react.bridge.ReactApplicationContext
6 | import com.facebook.react.uimanager.ViewManager
7 |
8 |
9 | class RNIaphubPackage : ReactPackage {
10 | override fun createNativeModules(reactContext: ReactApplicationContext): List {
11 | return listOf(RNIaphubModule(reactContext))
12 | }
13 |
14 | override fun createViewManagers(reactContext: ReactApplicationContext): List> {
15 | return emptyList()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/guides/migrate-v6-to-v7.md:
--------------------------------------------------------------------------------
1 | ## Migrate react-native-iaphub 6.X.X to 7.X.X
2 |
3 | The version 7 of react-native-iaphub is a major update, the library is now using the latest version of our [iOS](https://github.com/iaphub/iaphub-ios-sdk) and [Android](https://github.com/iaphub/iaphub-android-sdk) SDKs.
4 |
5 | ```js
6 | // Install react-native-iaphub
7 | npm install react-native-iaphub@latest --save
8 | // Update dependency on xcode (in the ios folder)
9 | pod install
10 | ```
11 |
12 | Here is what it'll change for you:
13 |
14 | ### The react-native-iap dependency isn't necessary anymore
15 | The library is now using our [iOS](https://github.com/iaphub/iaphub-ios-sdk) and [Android](https://github.com/iaphub/iaphub-android-sdk) SDKs, you can uninstall the dependency.
16 |
17 | ### New methods
18 |
19 | - The `init` method has been renamed to `start`
20 | - The `setUserId` method has been renamed to `login`
21 | - The `logout` method has been created
22 |
23 | ### New properties
24 |
25 | - The property `price` has been renamed to `localizedPrice`
26 | - The property `priceCurrency` has been renamed to `currency`
27 | - The property `priceAmount` has been renamed to `price`
28 | - The property `title` has been renamed to `localizedTitle`
29 | - The property `description` has been renamed to `localizedDescription`
30 | - The property `subscriptionIntroPrice` has been renamed to `subscriptionIntroLocalizedPrice`
31 | - The property `subscriptionIntroPrice` is now returning the introductory price amount (number)
32 |
33 | ### Errors enhanced
34 |
35 | The error sytem has been enhanced.
36 | An error still has the property `code` but we added added an extra property `subcode` in order to get more details about the error.
37 | Some errors have been renamed, you can check the updated [code example to buy a product](https://github.com/iaphub/react-native-iaphub#buy-a-product).
38 |
39 | ### New authentication system
40 |
41 | **In the version 6** of the library it wasn't possible to display the products or purchase a product without authenticating the user.
42 | The apps that do not have an authentication system had to call the `setUserId` method using the device ID coming from the react-native-device-info plugin.
43 |
44 | **The new version** is now supporting anonymous users by default, you'll be able to display the products or purchase a product without authenticating the user using the `login` method. IAPHUB will automatically generate a anonymous user id (prefixed with 'a:').
45 |
46 | We also implemented a security to prevent anonymous purchases by default (an error 'anonymous_purchase_not_allowed' will be returned), you'll just have to enable the option `allowAnonymousPurchase` (in the options of the start method).
47 |
48 | Another amazing feature is that the products bought by an anonymous user (not logged in) will be automatically transferred to his new user id when he logs in!
49 |
50 | You can also easily log out the user using our new `logout` method.
51 |
52 | ### ⚠ For users that were already using the device ID in production
53 |
54 | If you are currently using the device ID to authenticate your users in production, the active subscriptions you currently have are linked to the device IDs of your users.
55 | Which means the users that will use your brand new app update using react-native-iaphub 7.X.X won't be able to access their active subscriptions since they'll now have a different user id.
56 |
57 | In order to prevent that, you can call the `login` method and provide the device ID of the user to the method.
58 | You won't be able to use our new 'anonymous users' feature but it'll allow you to have a smooth migration.
59 |
60 | If you would like to completely migrate to our new system you'll have to ask the users to restore their in-app purchases (restore() method).
61 | It'll automatically transfer the purchases owned by the device ID to the anonymous user id.
62 |
63 | ### Need help?
64 |
65 | If you have any questions you can of course contact us at `support@iaphub.com`.
--------------------------------------------------------------------------------
/guides/migrate-v7-to-v8.md:
--------------------------------------------------------------------------------
1 | ## Migrate react-native-iaphub 7.X.X to 8.X.X
2 |
3 | The version 8 of react-native-iaphub implements the Google Play Billing Library 5.0!
4 | It is an important update, especially now that Google has completely updated the Google Play Console with the possibility to create multiple offers and intro phases.
5 |
6 | You must update the library to v8 in order to detect the intro phases correctly so we recommend to update the library as soon as you can.
7 |
8 | To update the library:
9 | ```js
10 | // Install react-native-iaphub
11 | npm install react-native-iaphub@latest --save
12 | // Update dependency on xcode (in the ios folder)
13 | pod install
14 | ```
15 |
16 | The only major change is that the following properties are removed:
17 | - subscriptionIntroPrice
18 | - subscriptionIntroLocalizedPrice
19 | - subscriptionIntroPayment
20 | - subscriptionIntroDuration
21 | - subscriptionIntroCycles
22 | - subscriptionTrialDuration
23 |
24 | Instead you'll have a property `subscriptionIntroPhases` that is an ordered list containing the intro phases the user is eligible to.
25 | So for instance, if you create 2 intro phases: a free trial of 1 month followed by an introductory price of $4.99 for 3 months, the `subscriptionIntroPhases` property will contain the following:
26 |
27 | ```js
28 | [
29 | {
30 | type: "trial",
31 | price: 0,
32 | currency: "USD",
33 | localizedPrice: "FREE",
34 | cycleDuration: "P1M",
35 | cycleCount: 1,
36 | payment: "upfront"
37 | },
38 | {
39 | type: "intro",
40 | price: 4.99,
41 | currency: "USD",
42 | localizedPrice: "$4.99",
43 | cycleDuration: "P1M",
44 | cycleCount: 3,
45 | payment: "as_you_go"
46 | }
47 | ]
48 | ```
49 |
50 | If you have multiple Android offers, the oldest (first one you've created) will be used by default.
51 | We do not support the Android multiple offers system in order to have a common system with iOS. To have a different offer simply create a new product, you can do pretty much everything with [smart listings](https://www.iaphub.com/docs/resources/smart-listing).
52 |
53 | ### Need help?
54 |
55 | If you have any questions you can of course contact us at `support@iaphub.com`.
--------------------------------------------------------------------------------
/ios/RNIaphub-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "React/RCTBridgeModule.h"
2 | #import "React/RCTEventEmitter.h"
3 |
--------------------------------------------------------------------------------
/ios/RNIaphub.m:
--------------------------------------------------------------------------------
1 | #import "React/RCTBridgeModule.h"
2 | #import "React/RCTEventEmitter.h"
3 |
4 | @interface RCT_EXTERN_MODULE(RNIaphub, RCTEventEmitter)
5 |
6 | RCT_EXTERN_METHOD(
7 | start: (NSDictionary)options
8 | resolver: (RCTPromiseResolveBlock)resolve
9 | rejecter: (RCTPromiseRejectBlock)reject
10 | )
11 |
12 | RCT_EXTERN_METHOD(
13 | stop: (RCTPromiseResolveBlock)resolve
14 | rejecter: (RCTPromiseRejectBlock)reject
15 | )
16 |
17 | RCT_EXTERN_METHOD(
18 | getSDKVersion: (RCTPromiseResolveBlock)resolve
19 | rejecter: (RCTPromiseRejectBlock)reject
20 | )
21 |
22 | RCT_EXTERN_METHOD(
23 | setLang: (NSString)lang
24 | resolver: (RCTPromiseResolveBlock)resolve
25 | rejecter: (RCTPromiseRejectBlock)reject
26 | )
27 |
28 | RCT_EXTERN_METHOD(
29 | login: (NSString)userId
30 | resolver: (RCTPromiseResolveBlock)resolve
31 | rejecter: (RCTPromiseRejectBlock)reject
32 | )
33 |
34 | RCT_EXTERN_METHOD(
35 | getUserId: (RCTPromiseResolveBlock)resolve
36 | rejecter: (RCTPromiseRejectBlock)reject
37 | )
38 |
39 | RCT_EXTERN_METHOD(
40 | logout: (RCTPromiseResolveBlock)resolve
41 | rejecter: (RCTPromiseRejectBlock)reject
42 | )
43 |
44 | RCT_EXTERN_METHOD(
45 | setDeviceParams: (NSDictionary)params
46 | resolver: (RCTPromiseResolveBlock)resolve
47 | rejecter: (RCTPromiseRejectBlock)reject
48 | )
49 |
50 | RCT_EXTERN_METHOD(
51 | setUserTags: (NSDictionary)tags
52 | resolver: (RCTPromiseResolveBlock)resolve
53 | rejecter: (RCTPromiseRejectBlock)reject
54 | )
55 |
56 | RCT_EXTERN_METHOD(
57 | buy: (NSString)sku
58 | options: (NSDictionary)options
59 | resolver: (RCTPromiseResolveBlock)resolve
60 | rejecter: (RCTPromiseRejectBlock)reject
61 | )
62 |
63 | RCT_EXTERN_METHOD(
64 | restore: (RCTPromiseResolveBlock)resolve
65 | rejecter: (RCTPromiseRejectBlock)reject
66 | )
67 |
68 | RCT_EXTERN_METHOD(
69 | getActiveProducts: (NSDictionary)options
70 | resolver: (RCTPromiseResolveBlock)resolve
71 | rejecter: (RCTPromiseRejectBlock)reject
72 | )
73 |
74 | RCT_EXTERN_METHOD(
75 | getProductsForSale: (RCTPromiseResolveBlock)resolve
76 | rejecter: (RCTPromiseRejectBlock)reject
77 | )
78 |
79 | RCT_EXTERN_METHOD(
80 | getActiveProducts: (NSDictionary)options
81 | resolver: (RCTPromiseResolveBlock)resolve
82 | rejecter: (RCTPromiseRejectBlock)reject
83 | )
84 |
85 | RCT_EXTERN_METHOD(
86 | getProducts: (NSDictionary)options
87 | resolver: (RCTPromiseResolveBlock)resolve
88 | rejecter: (RCTPromiseRejectBlock)reject
89 | )
90 |
91 | RCT_EXTERN_METHOD(
92 | getBillingStatus: (RCTPromiseResolveBlock)resolve
93 | rejecter: (RCTPromiseRejectBlock)reject
94 | )
95 |
96 | RCT_EXTERN_METHOD(
97 | presentCodeRedemptionSheet: (RCTPromiseResolveBlock)resolve
98 | rejecter: (RCTPromiseRejectBlock)reject
99 | )
100 |
101 | RCT_EXTERN_METHOD(
102 | showManageSubscriptions: (NSDictionary)options
103 | resolver: (RCTPromiseResolveBlock)resolve
104 | rejecter: (RCTPromiseRejectBlock)reject
105 | )
106 |
107 | @end
108 |
--------------------------------------------------------------------------------
/ios/RNIaphub.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Iaphub
3 |
4 | @objc(RNIaphub)
5 | class RNIaphub: RCTEventEmitter, IaphubDelegate {
6 |
7 | private var hasListeners = false
8 |
9 |
10 | override static func requiresMainQueueSetup() -> Bool {
11 | return true
12 | }
13 |
14 | override func startObserving() {
15 | hasListeners = true
16 | }
17 |
18 | override func stopObserving() {
19 | hasListeners = false
20 | }
21 |
22 | override func supportedEvents() -> [String]! {
23 | return ["onUserUpdate", "onDeferredPurchase", "onError", "onBuyRequest", "onReceipt"]
24 | }
25 |
26 | /***************************** EVENTS ******************************/
27 |
28 | /**
29 | Listen for user update event
30 | */
31 | func didReceiveUserUpdate() {
32 | if (!self.hasListeners) {
33 | return
34 | }
35 | self.sendEvent(withName: "onUserUpdate", body: nil)
36 | }
37 |
38 | /**
39 | Listen for a deferred purchase event
40 | */
41 | func didReceiveDeferredPurchase(transaction: IHReceiptTransaction) {
42 | if (!self.hasListeners) {
43 | return
44 | }
45 | self.sendEvent(withName: "onDeferredPurchase", body: transaction.getDictionary())
46 | }
47 |
48 | /**
49 | Listen for error event
50 | */
51 | func didReceiveError(err: IHError) {
52 | if (!self.hasListeners) {
53 | return
54 | }
55 | self.sendEvent(withName: "onError", body: err.getDictionary())
56 | }
57 |
58 | /**
59 | Listen for buy request event
60 | */
61 | func didReceiveBuyRequest(sku: String) {
62 | if (!self.hasListeners) {
63 | return
64 | }
65 | self.sendEvent(withName: "onBuyRequest", body: ["sku": sku])
66 | }
67 |
68 | /**
69 | Listen for receipt event
70 | */
71 | func didProcessReceipt(err: IHError?, receipt: IHReceipt?) {
72 | if (!self.hasListeners) {
73 | return
74 | }
75 | self.sendEvent(withName: "onReceipt", body: [
76 | "err": err?.getDictionary(),
77 | "receipt": receipt?.getDictionary()
78 | ])
79 | }
80 |
81 | /***************************** PUBLIC ******************************/
82 |
83 | /**
84 | Start IAPHUB
85 | */
86 | @objc
87 | func start(_ options: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
88 | let appId = options.value(forKey: "appId") as? String ?? ""
89 | let apiKey = options.value(forKey: "apiKey") as? String ?? ""
90 | let userId = options.value(forKey: "userId") as? String
91 | let allowAnonymousPurchase = options.value(forKey: "allowAnonymousPurchase") as? Bool ?? false
92 | let enableDeferredPurchaseListener = options.value(forKey: "enableDeferredPurchaseListener") as? Bool ?? true
93 | let enableStorekitV2 = options.value(forKey: "enableStorekitV2") as? Bool ?? false
94 | let environment = options.value(forKey: "environment") as? String ?? "production"
95 | let lang = options.value(forKey: "lang") as? String ?? ""
96 | let sdkVersion = options.value(forKey: "sdkVersion") as? String ?? ""
97 | var sdk = "react_native"
98 |
99 | if let extraSdk = options.value(forKey: "sdk") as? String {
100 | sdk = "\(sdk)/\(extraSdk)"
101 | }
102 | Iaphub.delegate = self
103 | Iaphub.start(
104 | appId: appId,
105 | apiKey: apiKey,
106 | userId: userId,
107 | allowAnonymousPurchase: allowAnonymousPurchase,
108 | enableDeferredPurchaseListener: enableDeferredPurchaseListener,
109 | enableStorekitV2: enableStorekitV2,
110 | environment: environment,
111 | lang: lang,
112 | sdk: sdk,
113 | sdkVersion: sdkVersion
114 | )
115 | resolve(nil)
116 | }
117 |
118 | /**
119 | Stop IAPHUB
120 | */
121 | @objc
122 | func stop(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
123 | Iaphub.stop()
124 | resolve(nil)
125 | }
126 |
127 | /**
128 | Get SDK version
129 | */
130 | @objc
131 | func getSDKVersion(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
132 | let version = Iaphub.getSDKVersion();
133 | resolve(version)
134 | }
135 |
136 | /**
137 | Set lang
138 | */
139 | @objc
140 | func setLang(_ lang: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
141 | let result = Iaphub.setLang(lang);
142 | resolve(result);
143 | }
144 |
145 | /**
146 | Login
147 | */
148 | @objc
149 | func login(_ userId: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
150 | Iaphub.login(userId: userId, { (err) in
151 | if let err = err {
152 | return reject("iaphub_error", self.createError(err), nil)
153 | }
154 | resolve(nil)
155 | })
156 | }
157 |
158 | /**
159 | Get user id
160 | */
161 | @objc
162 | func getUserId(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
163 | let userId = Iaphub.getUserId()
164 |
165 | if (userId == nil) {
166 | reject("iaphub_error", self.createError(code: "unexpected", subcode: "start_missing", message: "iaphub not started"), nil)
167 | }
168 | else {
169 | resolve(userId)
170 | }
171 | }
172 |
173 | /**
174 | Logout
175 | */
176 | @objc
177 | func logout(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
178 | Iaphub.logout()
179 | resolve(nil)
180 | }
181 |
182 | /**
183 | Set device params
184 | */
185 | @objc
186 | func setDeviceParams(_ params: Dictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
187 | Iaphub.setDeviceParams(params: params)
188 | resolve(nil)
189 | }
190 |
191 | /**
192 | Set user tags
193 | */
194 | @objc
195 | func setUserTags(_ tags: Dictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
196 | Iaphub.setUserTags(tags: tags, { (err) in
197 | if let err = err {
198 | return reject("iaphub_error", self.createError(err), nil)
199 | }
200 | resolve(nil)
201 | })
202 | }
203 |
204 | /**
205 | Buy
206 | */
207 | @objc
208 | func buy(_ sku: String, options: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
209 | let crossPlatformConflict = options.value(forKey: "crossPlatformConflict") as? Bool ?? true
210 |
211 | Iaphub.buy(sku: sku, crossPlatformConflict: crossPlatformConflict, { (err, transaction) in
212 | if let err = err {
213 | return reject("iaphub_error", self.createError(err), nil)
214 | }
215 | resolve(transaction?.getDictionary())
216 | })
217 | }
218 |
219 | /**
220 | Restore
221 | */
222 | @objc
223 | func restore(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
224 | Iaphub.restore({ (err, response) in
225 | if let err = err {
226 | return reject("iaphub_error", self.createError(err), nil)
227 | }
228 | resolve(response?.getDictionary())
229 | })
230 | }
231 |
232 | /**
233 | Get active products
234 | */
235 | @objc
236 | func getActiveProducts(_ options: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
237 | let includeSubscriptionStates = options.value(forKey: "includeSubscriptionStates") as? [String] ?? []
238 |
239 | Iaphub.getActiveProducts(includeSubscriptionStates: includeSubscriptionStates, { (err, products) in
240 | if let err = err {
241 | return reject("iaphub_error", self.createError(err), nil)
242 | }
243 | resolve(products?.map({ (product) in product.getDictionary()}))
244 | })
245 | }
246 |
247 | /**
248 | Get products for sale
249 | */
250 | @objc
251 | func getProductsForSale(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
252 | Iaphub.getProductsForSale({ (err, products) in
253 | if let err = err {
254 | return reject("iaphub_error", self.createError(err), nil)
255 | }
256 | resolve(products?.map({ (product) in product.getDictionary()}))
257 | })
258 | }
259 |
260 | /**
261 | Get products (active and for sale)
262 | */
263 | @objc
264 | func getProducts(_ options: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
265 | let includeSubscriptionStates = options.value(forKey: "includeSubscriptionStates") as? [String] ?? []
266 |
267 | Iaphub.getProducts(includeSubscriptionStates: includeSubscriptionStates, { (err, products) in
268 | if let err = err {
269 | return reject("iaphub_error", self.createError(err), nil)
270 | }
271 | resolve(products?.getDictionary())
272 | })
273 | }
274 |
275 | /**
276 | Get billing status
277 | */
278 | @objc
279 | func getBillingStatus(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
280 | let status = Iaphub.getBillingStatus()
281 |
282 | resolve(status.getDictionary())
283 | }
284 |
285 | /**
286 | Present code redemption
287 | */
288 | @objc
289 | func presentCodeRedemptionSheet(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
290 | Iaphub.presentCodeRedemptionSheet({ (err) in
291 | if let err = err {
292 | return reject("iaphub_error", self.createError(err), nil)
293 | }
294 | resolve(nil)
295 | })
296 | }
297 |
298 | /**
299 | Show manage subscriptions page
300 | */
301 | @objc
302 | func showManageSubscriptions(_ options: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
303 | Iaphub.showManageSubscriptions({ (err) in
304 | if let err = err {
305 | return reject("iaphub_error", self.createError(err), nil)
306 | }
307 | resolve(nil)
308 | })
309 | }
310 |
311 | /***************************** PRIVATE ******************************/
312 |
313 | /**
314 | Create error
315 | */
316 | func createError(code: String, subcode: String, message: String, params: Dictionary = [:]) -> String? {
317 | if let json = try? JSONSerialization.data(withJSONObject: ["code": code, "subcode": subcode, "message": message, "params": params]) {
318 | return String(data: json, encoding: .utf8)
319 | }
320 | return nil
321 | }
322 |
323 | /**
324 | Create error
325 | */
326 | func createError(_ err: IHError) -> String? {
327 | if let json = try? JSONSerialization.data(withJSONObject: err.getDictionary()) {
328 | return String(data: json, encoding: .utf8)
329 | }
330 | return nil
331 | }
332 | }
333 |
--------------------------------------------------------------------------------
/ios/RNIaphub.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 960FD6C327F7FF9A006A36AF /* RNIaphub.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNIaphub.m */; };
11 | F4FF95D7245B92E800C19C63 /* RNIaphub.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* RNIaphub.swift */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXCopyFilesBuildPhase section */
15 | 58B511D91A9E6C8500147676 /* CopyFiles */ = {
16 | isa = PBXCopyFilesBuildPhase;
17 | buildActionMask = 2147483647;
18 | dstPath = "include/$(PRODUCT_NAME)";
19 | dstSubfolderSpec = 16;
20 | files = (
21 | );
22 | runOnlyForDeploymentPostprocessing = 0;
23 | };
24 | /* End PBXCopyFilesBuildPhase section */
25 |
26 | /* Begin PBXFileReference section */
27 | 134814201AA4EA6300B7C361 /* libRNIaphub.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNIaphub.a; sourceTree = BUILT_PRODUCTS_DIR; };
28 | B3E7B5891CC2AC0600A0062D /* RNIaphub.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNIaphub.m; sourceTree = ""; };
29 | F4FF95D5245B92E700C19C63 /* RNIaphub-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNIaphub-Bridging-Header.h"; sourceTree = ""; };
30 | F4FF95D6245B92E800C19C63 /* RNIaphub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNIaphub.swift; sourceTree = ""; };
31 | /* End PBXFileReference section */
32 |
33 | /* Begin PBXFrameworksBuildPhase section */
34 | 58B511D81A9E6C8500147676 /* Frameworks */ = {
35 | isa = PBXFrameworksBuildPhase;
36 | buildActionMask = 2147483647;
37 | files = (
38 | );
39 | runOnlyForDeploymentPostprocessing = 0;
40 | };
41 | /* End PBXFrameworksBuildPhase section */
42 |
43 | /* Begin PBXGroup section */
44 | 134814211AA4EA7D00B7C361 /* Products */ = {
45 | isa = PBXGroup;
46 | children = (
47 | 134814201AA4EA6300B7C361 /* libRNIaphub.a */,
48 | );
49 | name = Products;
50 | sourceTree = "";
51 | };
52 | 58B511D21A9E6C8500147676 = {
53 | isa = PBXGroup;
54 | children = (
55 | F4FF95D6245B92E800C19C63 /* RNIaphub.swift */,
56 | B3E7B5891CC2AC0600A0062D /* RNIaphub.m */,
57 | F4FF95D5245B92E700C19C63 /* RNIaphub-Bridging-Header.h */,
58 | 134814211AA4EA7D00B7C361 /* Products */,
59 | );
60 | sourceTree = "";
61 | };
62 | /* End PBXGroup section */
63 |
64 | /* Begin PBXNativeTarget section */
65 | 58B511DA1A9E6C8500147676 /* RNIaphub */ = {
66 | isa = PBXNativeTarget;
67 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNIaphub" */;
68 | buildPhases = (
69 | 58B511D71A9E6C8500147676 /* Sources */,
70 | 58B511D81A9E6C8500147676 /* Frameworks */,
71 | 58B511D91A9E6C8500147676 /* CopyFiles */,
72 | );
73 | buildRules = (
74 | );
75 | dependencies = (
76 | );
77 | name = RNIaphub;
78 | productName = RCTDataManager;
79 | productReference = 134814201AA4EA6300B7C361 /* libRNIaphub.a */;
80 | productType = "com.apple.product-type.library.static";
81 | };
82 | /* End PBXNativeTarget section */
83 |
84 | /* Begin PBXProject section */
85 | 58B511D31A9E6C8500147676 /* Project object */ = {
86 | isa = PBXProject;
87 | attributes = {
88 | LastUpgradeCheck = 0920;
89 | ORGANIZATIONNAME = Facebook;
90 | TargetAttributes = {
91 | 58B511DA1A9E6C8500147676 = {
92 | CreatedOnToolsVersion = 6.1.1;
93 | };
94 | };
95 | };
96 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNIaphub" */;
97 | compatibilityVersion = "Xcode 3.2";
98 | developmentRegion = English;
99 | hasScannedForEncodings = 0;
100 | knownRegions = (
101 | English,
102 | en,
103 | );
104 | mainGroup = 58B511D21A9E6C8500147676;
105 | productRefGroup = 58B511D21A9E6C8500147676;
106 | projectDirPath = "";
107 | projectRoot = "";
108 | targets = (
109 | 58B511DA1A9E6C8500147676 /* RNIaphub */,
110 | );
111 | };
112 | /* End PBXProject section */
113 |
114 | /* Begin PBXSourcesBuildPhase section */
115 | 58B511D71A9E6C8500147676 /* Sources */ = {
116 | isa = PBXSourcesBuildPhase;
117 | buildActionMask = 2147483647;
118 | files = (
119 | 960FD6C327F7FF9A006A36AF /* RNIaphub.m in Sources */,
120 | F4FF95D7245B92E800C19C63 /* RNIaphub.swift in Sources */,
121 | );
122 | runOnlyForDeploymentPostprocessing = 0;
123 | };
124 | /* End PBXSourcesBuildPhase section */
125 |
126 | /* Begin XCBuildConfiguration section */
127 | 58B511ED1A9E6C8500147676 /* Debug */ = {
128 | isa = XCBuildConfiguration;
129 | buildSettings = {
130 | ALWAYS_SEARCH_USER_PATHS = NO;
131 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
132 | CLANG_CXX_LIBRARY = "libc++";
133 | CLANG_ENABLE_MODULES = YES;
134 | CLANG_ENABLE_OBJC_ARC = YES;
135 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
136 | CLANG_WARN_BOOL_CONVERSION = YES;
137 | CLANG_WARN_COMMA = YES;
138 | CLANG_WARN_CONSTANT_CONVERSION = YES;
139 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
140 | CLANG_WARN_EMPTY_BODY = YES;
141 | CLANG_WARN_ENUM_CONVERSION = YES;
142 | CLANG_WARN_INFINITE_RECURSION = YES;
143 | CLANG_WARN_INT_CONVERSION = YES;
144 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
145 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
146 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
147 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
148 | CLANG_WARN_STRICT_PROTOTYPES = YES;
149 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
150 | CLANG_WARN_UNREACHABLE_CODE = YES;
151 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
152 | COPY_PHASE_STRIP = NO;
153 | ENABLE_STRICT_OBJC_MSGSEND = YES;
154 | ENABLE_TESTABILITY = YES;
155 | GCC_C_LANGUAGE_STANDARD = gnu99;
156 | GCC_DYNAMIC_NO_PIC = NO;
157 | GCC_NO_COMMON_BLOCKS = YES;
158 | GCC_OPTIMIZATION_LEVEL = 0;
159 | GCC_PREPROCESSOR_DEFINITIONS = (
160 | "DEBUG=1",
161 | "$(inherited)",
162 | );
163 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
164 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
165 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
166 | GCC_WARN_UNDECLARED_SELECTOR = YES;
167 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
168 | GCC_WARN_UNUSED_FUNCTION = YES;
169 | GCC_WARN_UNUSED_VARIABLE = YES;
170 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
171 | MTL_ENABLE_DEBUG_INFO = YES;
172 | ONLY_ACTIVE_ARCH = YES;
173 | SDKROOT = iphoneos;
174 | };
175 | name = Debug;
176 | };
177 | 58B511EE1A9E6C8500147676 /* Release */ = {
178 | isa = XCBuildConfiguration;
179 | buildSettings = {
180 | ALWAYS_SEARCH_USER_PATHS = NO;
181 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
182 | CLANG_CXX_LIBRARY = "libc++";
183 | CLANG_ENABLE_MODULES = YES;
184 | CLANG_ENABLE_OBJC_ARC = YES;
185 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
186 | CLANG_WARN_BOOL_CONVERSION = YES;
187 | CLANG_WARN_COMMA = YES;
188 | CLANG_WARN_CONSTANT_CONVERSION = YES;
189 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
190 | CLANG_WARN_EMPTY_BODY = YES;
191 | CLANG_WARN_ENUM_CONVERSION = YES;
192 | CLANG_WARN_INFINITE_RECURSION = YES;
193 | CLANG_WARN_INT_CONVERSION = YES;
194 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
195 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
197 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
198 | CLANG_WARN_STRICT_PROTOTYPES = YES;
199 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
200 | CLANG_WARN_UNREACHABLE_CODE = YES;
201 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
202 | COPY_PHASE_STRIP = YES;
203 | ENABLE_NS_ASSERTIONS = NO;
204 | ENABLE_STRICT_OBJC_MSGSEND = YES;
205 | GCC_C_LANGUAGE_STANDARD = gnu99;
206 | GCC_NO_COMMON_BLOCKS = YES;
207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
209 | GCC_WARN_UNDECLARED_SELECTOR = YES;
210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
211 | GCC_WARN_UNUSED_FUNCTION = YES;
212 | GCC_WARN_UNUSED_VARIABLE = YES;
213 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
214 | MTL_ENABLE_DEBUG_INFO = NO;
215 | SDKROOT = iphoneos;
216 | VALIDATE_PRODUCT = YES;
217 | };
218 | name = Release;
219 | };
220 | 58B511F01A9E6C8500147676 /* Debug */ = {
221 | isa = XCBuildConfiguration;
222 | buildSettings = {
223 | HEADER_SEARCH_PATHS = (
224 | "$(inherited)",
225 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
226 | "$(SRCROOT)/../../../React/**",
227 | "$(SRCROOT)/../../react-native/React/**",
228 | );
229 | LIBRARY_SEARCH_PATHS = "$(inherited)";
230 | OTHER_LDFLAGS = "-ObjC";
231 | PRODUCT_NAME = RNIaphub;
232 | SKIP_INSTALL = YES;
233 | SWIFT_OBJC_BRIDGING_HEADER = "RNIaphub-Bridging-Header.h";
234 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
235 | SWIFT_VERSION = 5.0;
236 | };
237 | name = Debug;
238 | };
239 | 58B511F11A9E6C8500147676 /* Release */ = {
240 | isa = XCBuildConfiguration;
241 | buildSettings = {
242 | HEADER_SEARCH_PATHS = (
243 | "$(inherited)",
244 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
245 | "$(SRCROOT)/../../../React/**",
246 | "$(SRCROOT)/../../react-native/React/**",
247 | );
248 | LIBRARY_SEARCH_PATHS = "$(inherited)";
249 | OTHER_LDFLAGS = "-ObjC";
250 | PRODUCT_NAME = RNIaphub;
251 | SKIP_INSTALL = YES;
252 | SWIFT_OBJC_BRIDGING_HEADER = "RNIaphub-Bridging-Header.h";
253 | SWIFT_VERSION = 5.0;
254 | };
255 | name = Release;
256 | };
257 | /* End XCBuildConfiguration section */
258 |
259 | /* Begin XCConfigurationList section */
260 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNIaphub" */ = {
261 | isa = XCConfigurationList;
262 | buildConfigurations = (
263 | 58B511ED1A9E6C8500147676 /* Debug */,
264 | 58B511EE1A9E6C8500147676 /* Release */,
265 | );
266 | defaultConfigurationIsVisible = 0;
267 | defaultConfigurationName = Release;
268 | };
269 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNIaphub" */ = {
270 | isa = XCConfigurationList;
271 | buildConfigurations = (
272 | 58B511F01A9E6C8500147676 /* Debug */,
273 | 58B511F11A9E6C8500147676 /* Release */,
274 | );
275 | defaultConfigurationIsVisible = 0;
276 | defaultConfigurationName = Release;
277 | };
278 | /* End XCConfigurationList section */
279 | };
280 | rootObject = 58B511D31A9E6C8500147676 /* Project object */;
281 | }
282 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-iaphub",
3 | "version": "8.10.4",
4 | "description": "The easiest way to implement IAP (In-app purchase) in your React Native app",
5 | "main": "lib/commonjs/index",
6 | "module": "lib/module/index",
7 | "types": "lib/typescript/index.d.ts",
8 | "react-native": "src/index",
9 | "source": "src/index",
10 | "files": [
11 | "src",
12 | "lib",
13 | "android",
14 | "ios",
15 | "cpp",
16 | "react-native-iaphub.podspec",
17 | "!lib/typescript/example",
18 | "!android/build",
19 | "!ios/build",
20 | "!**/__tests__",
21 | "!**/__fixtures__",
22 | "!**/__mocks__"
23 | ],
24 | "scripts": {
25 | "test": "jest",
26 | "typescript": "tsc --noEmit",
27 | "lint": "eslint \"**/*.{js,ts,tsx}\"",
28 | "prepare": "bob build",
29 | "release": "release-it",
30 | "example": "yarn --cwd example",
31 | "pods": "cd example && pod-install --quiet",
32 | "bootstrap": "yarn example && yarn && yarn pods"
33 | },
34 | "keywords": [
35 | "react-native",
36 | "ios",
37 | "android"
38 | ],
39 | "repository": "https://github.com/iaphub/react-native-iaphub",
40 | "author": "iaphub (https://www.iaphub.com)",
41 | "license": "MIT",
42 | "bugs": {
43 | "url": "https://github.com/iaphub/react-native-iaphub/issues"
44 | },
45 | "homepage": "https://github.com/iaphub/react-native-iaphub#readme",
46 | "publishConfig": {
47 | "registry": "https://registry.npmjs.org/"
48 | },
49 | "devDependencies": {
50 | "@commitlint/config-conventional": "^11.0.0",
51 | "@react-native-community/eslint-config": "^2.0.0",
52 | "@release-it/conventional-changelog": "^2.0.0",
53 | "@types/jest": "^26.0.0",
54 | "@types/react": "^16.9.19",
55 | "@types/react-native": "0.62.13",
56 | "commitlint": "^11.0.0",
57 | "eslint": "^7.2.0",
58 | "eslint-config-prettier": "^7.0.0",
59 | "eslint-plugin-prettier": "^3.1.3",
60 | "husky": "^6.0.0",
61 | "jest": "^26.0.1",
62 | "pod-install": "^0.1.0",
63 | "prettier": "^2.0.5",
64 | "react": "16.13.1",
65 | "react-native": "0.63.4",
66 | "react-native-builder-bob": "^0.18.3",
67 | "release-it": "^14.2.2",
68 | "typescript": "^4.1.3"
69 | },
70 | "peerDependencies": {
71 | "react": "*",
72 | "react-native": "*"
73 | },
74 | "jest": {
75 | "preset": "react-native",
76 | "modulePathIgnorePatterns": [
77 | "/example/node_modules",
78 | "/lib/"
79 | ]
80 | },
81 | "commitlint": {
82 | "extends": [
83 | "@commitlint/config-conventional"
84 | ]
85 | },
86 | "release-it": {
87 | "git": {
88 | "commitMessage": "chore: release ${version}",
89 | "tagName": "v${version}"
90 | },
91 | "npm": {
92 | "publish": true
93 | },
94 | "github": {
95 | "release": true
96 | },
97 | "plugins": {
98 | "@release-it/conventional-changelog": {
99 | "preset": "angular"
100 | }
101 | }
102 | },
103 | "eslintConfig": {
104 | "root": true,
105 | "extends": [
106 | "@react-native-community",
107 | "prettier"
108 | ],
109 | "rules": {
110 | "prettier/prettier": [
111 | "error",
112 | {
113 | "quoteProps": "consistent",
114 | "singleQuote": true,
115 | "tabWidth": 2,
116 | "trailingComma": "es5",
117 | "useTabs": false
118 | }
119 | ]
120 | }
121 | },
122 | "eslintIgnore": [
123 | "node_modules/",
124 | "lib/"
125 | ],
126 | "prettier": {
127 | "quoteProps": "consistent",
128 | "singleQuote": true,
129 | "tabWidth": 2,
130 | "trailingComma": "es5",
131 | "useTabs": false
132 | },
133 | "react-native-builder-bob": {
134 | "source": "src",
135 | "output": "lib",
136 | "targets": [
137 | "commonjs",
138 | "module",
139 | [
140 | "typescript",
141 | {
142 | "project": "tsconfig.build.json"
143 | }
144 | ]
145 | ]
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/react-native-iaphub.podspec:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4 |
5 | Pod::Spec.new do |s|
6 | s.name = "react-native-iaphub"
7 | s.version = package["version"]
8 | s.summary = package["description"]
9 | s.homepage = package["homepage"]
10 | s.license = package["license"]
11 | s.authors = package["author"]
12 |
13 | s.platforms = { :ios => "9.0" }
14 | s.source = { :git => "https://github.com/iaphub/react-native-iaphub.git", :tag => "#{s.version}" }
15 |
16 | s.source_files = "ios/**/*.{h,m,mm,swift}"
17 |
18 | s.dependency "Iaphub", "4.6.5"
19 | s.dependency "React-Core"
20 | end
--------------------------------------------------------------------------------
/src/config.tsx:
--------------------------------------------------------------------------------
1 | export default {
2 | version: "8.10.4",
3 | iosSDKVersion: "4.6.5",
4 | androidSDKVersion: "4.6.6"
5 | };
--------------------------------------------------------------------------------
/src/iaphub.tsx:
--------------------------------------------------------------------------------
1 | import {Platform, NativeModules, NativeEventEmitter, EmitterSubscription} from 'react-native';
2 |
3 | import config from './config';
4 | import type Transaction from './models/transaction';
5 | import type Product from './models/product';
6 | import type ActiveProduct from './models/active-product';
7 | import type BillingStatus from './models/billing-status';
8 | import IaphubError from './models/iaphub-error';
9 |
10 | const LINKING_ERROR =
11 | `The package 'react-native-iaphub' doesn't seem to be linked. Make sure: \n\n` +
12 | Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
13 | '- You rebuilt the app after installing the package\n' +
14 | '- You are not using Expo managed workflow\n';
15 |
16 | const RNIaphub = NativeModules.RNIaphub
17 | ? NativeModules.RNIaphub
18 | : new Proxy(
19 | {},
20 | {
21 | get() {
22 | throw new Error(LINKING_ERROR);
23 | },
24 | }
25 | );
26 |
27 | type Products = {productsForSale: Product[]; activeProducts: ActiveProduct[]};
28 | type RestoreResponse = {newPurchases: Transaction[]; transferredActiveProducts: ActiveProduct[]};
29 | type EventName = 'onUserUpdate' | 'onDeferredPurchase' | 'onError' | 'onBuyRequest' | 'onReceipt';
30 |
31 | interface StartOptions {
32 | appId: string,
33 | apiKey: string,
34 | userId?: string,
35 | allowAnonymousPurchase?: boolean,
36 | enableDeferredPurchaseListener?: boolean,
37 | enableStorekitV2?: boolean,
38 | lang?: string
39 | environment?: string
40 | };
41 |
42 | interface BuyOptions {
43 | crossPlatformConflict: boolean,
44 | prorationMode?: string
45 | }
46 |
47 | interface GetProductsOptions {
48 | includeSubscriptionStates: string[]
49 | }
50 |
51 | interface ShowManageSubscriptionsOptions {
52 | sku?: string
53 | }
54 |
55 | export default class Iaphub {
56 |
57 | nativeEventEmitter;
58 | errorListener?: EmitterSubscription;
59 | listeners: EmitterSubscription[] = [];
60 |
61 | constructor() {
62 | this.nativeEventEmitter = new NativeEventEmitter(NativeModules.RNIaphub);
63 | this.listeners = [];
64 | }
65 |
66 | /**
67 | * Add event listener
68 | */
69 | public addEventListener(name: EventName, callback: (data: any) => void): EmitterSubscription {
70 | const subscription = this.nativeEventEmitter.addListener(name, (data: any) => {
71 | if (name == "onError") {
72 | data = new IaphubError(data);
73 | }
74 | callback(data);
75 | });
76 |
77 | this.listeners.push(subscription);
78 | return subscription;
79 | }
80 |
81 | /**
82 | * Remove event listener
83 | */
84 | public removeEventListener(listener: EmitterSubscription): boolean {
85 | var index = this.listeners.indexOf(listener);
86 |
87 | if (index === -1) return false;
88 | this.listeners[index].remove();
89 | this.listeners.splice(index, 1);
90 |
91 | return true;
92 | }
93 |
94 | /**
95 | * Remove all listeners
96 | */
97 | public removeAllListeners(): void {
98 | this.listeners.forEach((listener) => listener.remove());
99 | this.listeners = [];
100 | }
101 |
102 | /**
103 | * Start Iaphub
104 | * @param {String} appId App id that can be found on the IAPHUB dashboard
105 | * @param {String} apiKey Api key that can be found on the IAPHUB dashboard
106 | * @param {Boolean} allowAnonymousPurchase Option to allow purchases without being logged in
107 | * @param {Boolean} enableDeferredPurchaseListener Option to enable the onDeferredPurchase event (true by default)
108 | * @param {Boolean} enableStorekitV2 Enable StoreKit V2 if supported by the phone (iOS 15+) (false by default)
109 | * @param {String} environment Option to specify a different environment than production
110 | * @returns {Promise}
111 | */
112 | public async start(opts: StartOptions): Promise {
113 | try {
114 | // Build sdk version (we dot not define it in StartOptions on purpose, it is private)
115 | var sdkVersion = config.version;
116 | if (opts["sdkVersion"]) {
117 | sdkVersion += "/" + opts["sdkVersion"];
118 | }
119 | // Clear listeners
120 | this.removeAllListeners();
121 | // Start IAPHUB
122 | await RNIaphub.start(Object.assign(opts, {sdkVersion: sdkVersion}));
123 | // Display product missing error
124 | this.errorListener = this.nativeEventEmitter.addListener("onError", (err) => {
125 | if (err.code == "unexpected" && err.subcode == "product_missing_from_store") {
126 | console.error(err.message);
127 | }
128 | });
129 | // Check SDK version
130 | var nativeSDKVersion = RNIaphub.getSDKVersion ? await RNIaphub.getSDKVersion() : null;
131 | if (Platform.OS == "ios" && nativeSDKVersion != config.iosSDKVersion) {
132 | console.error(`The "react-native-iaphub" plugin requires the native IAPHUB iOS SDK version ${config.iosSDKVersion}.\n\nTo fix this issue:\nRun \`pod update Iaphub\` in the ios folder of your project to update the IAPHUB iOS SDK to the required version.`);
133 | }
134 | else if (Platform.OS == "android" && nativeSDKVersion != config.androidSDKVersion) {
135 | console.error(`The "react-native-iaphub" plugin requires the native IAPHUB Android SDK version ${config.androidSDKVersion}.\n\nTo fix this issue:\nRebuild your Android project to update the dependencies with the correct SDK version.`);
136 | }
137 | }
138 | catch (err) {
139 | throw IaphubError.parse(err);
140 | }
141 | }
142 |
143 | /**
144 | * Stop Iaphub
145 | * @returns {Promise}
146 | */
147 | public async stop(): Promise {
148 | try {
149 | // Clear listeners
150 | this.removeAllListeners();
151 | // Stop IAPHUB
152 | await RNIaphub.stop();
153 | // Remove error listener
154 | if (this.errorListener) {
155 | this.errorListener.remove();
156 | }
157 | }
158 | catch (err) {
159 | throw IaphubError.parse(err);
160 | }
161 | }
162 |
163 | /**
164 | * Set lang
165 | * @param {String} lang Language
166 | * @returns {Promise}
167 | */
168 | public async setLang(lang: string): Promise {
169 | try {
170 | var result = await RNIaphub.setLang(lang);
171 | return result;
172 | }
173 | catch (err) {
174 | throw IaphubError.parse(err);
175 | }
176 | }
177 |
178 | /**
179 | * Log in user
180 | * @param {String} userId User id
181 | * @returns {Promise}
182 | */
183 | public async login(userId: string): Promise {
184 | try {
185 | await RNIaphub.login(userId);
186 | }
187 | catch (err) {
188 | throw IaphubError.parse(err);
189 | }
190 | }
191 |
192 | /**
193 | * Ger user id
194 | * @returns {Promise}
195 | */
196 | public async getUserId(): Promise {
197 | try {
198 | var userId = await RNIaphub.getUserId();
199 | return userId;
200 | }
201 | catch (err) {
202 | throw IaphubError.parse(err);
203 | }
204 | }
205 |
206 | /**
207 | * Log out user
208 | * @returns {Promise}
209 | */
210 | public async logout(): Promise {
211 | try {
212 | await RNIaphub.logout();
213 | }
214 | catch (err) {
215 | throw IaphubError.parse(err);
216 | }
217 | }
218 |
219 | /**
220 | * Set device params
221 | * @param {Dict} params Device params
222 | * @returns {Promise}
223 | */
224 | public async setDeviceParams(params: { [key: string]: any }): Promise {
225 | try {
226 | await RNIaphub.setDeviceParams(params);
227 | }
228 | catch (err) {
229 | throw IaphubError.parse(err);
230 | }
231 | }
232 |
233 | /**
234 | * Set user tags
235 | * @param {Dict} tags User tags
236 | * @returns {Promise}
237 | */
238 | public async setUserTags(tags: { [key: string]: any }): Promise {
239 | try {
240 | await RNIaphub.setUserTags(tags);
241 | }
242 | catch (err) {
243 | throw IaphubError.parse(err);
244 | }
245 | }
246 |
247 | /**
248 | * Buy product
249 | * @param {String} sku Product sku
250 | * @param {Boolean} [crossPlatformConflict=true] Throws an error if the user has already a subscription on a different platform
251 | * @returns {Promise}
252 | */
253 | public async buy(sku: string, opts: BuyOptions = {crossPlatformConflict: true}): Promise {
254 | try {
255 | var transaction = await RNIaphub.buy(sku, opts);
256 | return transaction
257 | }
258 | catch (err) {
259 | throw IaphubError.parse(err);
260 | }
261 | }
262 |
263 | /**
264 | * Restore purchases
265 | * @returns {Promise}
266 | */
267 | public async restore(): Promise {
268 | try {
269 | var response = await RNIaphub.restore();
270 | return response;
271 | }
272 | catch (err) {
273 | throw IaphubError.parse(err);
274 | }
275 | }
276 |
277 | /**
278 | * Get active products
279 | * @param {String[]} [includeSubscriptionStates=[]] Include subscription states (only 'active' and 'grace_period' states are returned by default)
280 | * @returns {Promise}
281 | */
282 | public async getActiveProducts(opts: GetProductsOptions = {includeSubscriptionStates: []}): Promise {
283 | try {
284 | var products = await RNIaphub.getActiveProducts(opts);
285 | return products;
286 | }
287 | catch (err) {
288 | throw IaphubError.parse(err);
289 | }
290 | }
291 |
292 | /**
293 | * Get products for sale
294 | * @returns {Promise}
295 | */
296 | public async getProductsForSale(): Promise {
297 | try {
298 | var products = await RNIaphub.getProductsForSale();
299 | return products;
300 | }
301 | catch (err) {
302 | throw IaphubError.parse(err);
303 | }
304 | }
305 |
306 | /**
307 | * Get products (active and for sale)
308 | * @returns {Promise}
309 | */
310 | public async getProducts(opts: GetProductsOptions = {includeSubscriptionStates: []}): Promise {
311 | try {
312 | var products = await RNIaphub.getProducts(opts);
313 | return products;
314 | }
315 | catch (err) {
316 | throw IaphubError.parse(err);
317 | }
318 | }
319 |
320 | /**
321 | * Get billing status
322 | * @returns {Promise}
323 | */
324 | public async getBillingStatus(): Promise {
325 | try {
326 | var status = await RNIaphub.getBillingStatus();
327 | return status;
328 | }
329 | catch (err) {
330 | throw IaphubError.parse(err);
331 | }
332 | }
333 |
334 | /**
335 | * Present code redemption (iOS only)
336 | * @returns {Promise}
337 | */
338 | public async presentCodeRedemptionSheet(): Promise {
339 | try {
340 | await RNIaphub.presentCodeRedemptionSheet();
341 | }
342 | catch (err) {
343 | throw IaphubError.parse(err);
344 | }
345 | }
346 |
347 | /**
348 | * Show manage subscriptions page
349 | * @returns {Promise}
350 | */
351 | public async showManageSubscriptions(opts: ShowManageSubscriptionsOptions = {sku: undefined}): Promise {
352 | try {
353 | await RNIaphub.showManageSubscriptions(opts);
354 | }
355 | catch (err) {
356 | throw IaphubError.parse(err);
357 | }
358 | }
359 |
360 | }
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import Iaphub from './iaphub';
2 |
3 | export default new Iaphub();
--------------------------------------------------------------------------------
/src/models/active-product.tsx:
--------------------------------------------------------------------------------
1 | import type Product from './product';
2 |
3 | export default interface ActiveProduct extends Product {
4 |
5 | /**
6 | * Purchase id
7 | */
8 | readonly purchase: string | null;
9 | /**
10 | * Purchase date (ISO format)
11 | */
12 | readonly purchaseDate: string | null;
13 | /**
14 | * Platform of the purchase
15 | */
16 | readonly platform: string | null;
17 | /**
18 | * If it is a sandbox transaction
19 | */
20 | readonly isSandbox: boolean | false;
21 | /**
22 | * If it has been purchased using a promo code
23 | */
24 | readonly isPromo: boolean | false;
25 | /**
26 | * Promo code used for the purchase
27 | * (Android: only available for subscriptions vanity codes, not available for one time codes) (iOS: the value is the offer reference name)
28 | */
29 | readonly promoCode: string | null;
30 | /**
31 | * Subscription original purchase id
32 | */
33 | readonly originalPurchase: string | null;
34 | /**
35 | * Subscription expiration date
36 | */
37 | readonly expirationDate: string | null;
38 | /**
39 | * Returns if the subscription will auto renew
40 | */
41 | readonly isSubscriptionRenewable: boolean | false;
42 | /**
43 | * True if the subscription is shared by a family member (iOS subscriptions only)
44 | */
45 | readonly isFamilyShare: boolean | false;
46 | /**
47 | * Subscription product of the next renewal (only defined if different than the current product)
48 | */
49 | readonly subscriptionRenewalProduct: string | null;
50 | /**
51 | * SubscriptionRenewalProduct sku
52 | */
53 | readonly subscriptionRenewalProductSku: string | null;
54 | /**
55 | * Subscription state ("active", "retry_period", "grace_period", "paused")
56 | */
57 | readonly subscriptionState: string | null;
58 | /**
59 | * Subscription period type ("normal", "trial", "intro")
60 | */
61 | readonly subscriptionPeriodType: string | null;
62 |
63 | }
--------------------------------------------------------------------------------
/src/models/billing-status.tsx:
--------------------------------------------------------------------------------
1 | import type IaphubError from './iaphub-error';
2 |
3 | export default interface BillingStatus {
4 |
5 | /**
6 | * Error
7 | */
8 | readonly error: IaphubError | null;
9 | /**
10 | * Filtered products ids
11 | */
12 | readonly filteredProductIds: string[]
13 |
14 | }
--------------------------------------------------------------------------------
/src/models/iaphub-error.tsx:
--------------------------------------------------------------------------------
1 | export default class IaphubError {
2 |
3 | public code: string;
4 | public message: string;
5 | public subcode?: string;
6 | public params?: { [key: string]: any };
7 |
8 | constructor({code, subcode, message, params}: {code: string, subcode?: string, message: string, params?: { [key: string]: any }}) {
9 | this.code = code;
10 | this.subcode = subcode;
11 | this.message = message;
12 | this.params = params;
13 | }
14 |
15 | static parse(err: any) {
16 | if (typeof err != 'object' || !err.message) {
17 | return new IaphubError({code: "unexpected", message: `${err}`})
18 | }
19 |
20 | var json = null;
21 | try {
22 | json = JSON.parse(err.message);
23 | }
24 | catch (err) {
25 |
26 | }
27 |
28 | if (json && json.code) {
29 | return new IaphubError({
30 | code: json.code,
31 | subcode: json.subcode,
32 | message: json.message,
33 | params: json.params
34 | })
35 | }
36 |
37 | return new IaphubError({code: "unexpected", message: `${err}`})
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/src/models/product.tsx:
--------------------------------------------------------------------------------
1 | import type SubscriptionIntroPhase from './subscription-intro-phase';
2 |
3 | export default interface Product {
4 |
5 | /**
6 | * Product id
7 | */
8 | readonly id: string;
9 | /**
10 | * Product type
11 | */
12 | readonly type: string;
13 | /**
14 | * Product sku
15 | */
16 | readonly sku: string;
17 | /**
18 | * Product price
19 | */
20 | readonly price: number;
21 | /**
22 | * Product price currency
23 | */
24 | readonly currency: string | null;
25 | /**
26 | * Product localized price
27 | */
28 | readonly localizedPrice: string | null;
29 | /**
30 | * Product localized title
31 | */
32 | readonly localizedTitle: string | null;
33 | /**
34 | * Product localized description
35 | */
36 | readonly localizedDescription: string | null;
37 | /**
38 | * Product alias
39 | */
40 | readonly alias: string | null;
41 | /**
42 | * Product group id
43 | */
44 | readonly group: string | null;
45 | /**
46 | * Product group name
47 | */
48 | readonly groupName: string | null;
49 | /**
50 | * Duration of the subscription cycle specified in the ISO 8601 format
51 | */
52 | readonly subscriptionDuration: string | null;
53 | /**
54 | * Subscription intro phases
55 | */
56 | readonly subscriptionIntroPhases: [SubscriptionIntroPhase] | null;
57 | /**
58 | * Metadata for the product as key/value pairs
59 | */
60 | readonly metadata: { [key: string]: string };
61 | }
--------------------------------------------------------------------------------
/src/models/subscription-intro-phase.tsx:
--------------------------------------------------------------------------------
1 | export default interface SubscriptionIntroPhase {
2 |
3 | /**
4 | * Phase type
5 | */
6 | readonly type: string;
7 | /**
8 | * Phase price
9 | */
10 | readonly price: number;
11 | /**
12 | * Phase currency
13 | */
14 | readonly currency: string;
15 | /**
16 | * Phase localized price
17 | */
18 | readonly localizedPrice: string;
19 | /**
20 | * Phase duration cycle specified in the ISO 8601 format
21 | */
22 | readonly cycleDuration: string;
23 | /**
24 | * Phase cycle count
25 | */
26 | readonly cycleCount: number;
27 | /**
28 | * Phase payment type (Possible values: 'as_you_go', 'upfront')
29 | */
30 | readonly payment: string;
31 | }
--------------------------------------------------------------------------------
/src/models/transaction.tsx:
--------------------------------------------------------------------------------
1 | import type ActiveProduct from './active-product';
2 |
3 | export default interface Transaction extends ActiveProduct {
4 |
5 | /**
6 | * Webhook status of the transction
7 | */
8 | readonly webhookStatus: string | null;
9 | /**
10 | * Internal IAPHUB user id
11 | */
12 | readonly user: string | null;
13 |
14 | }
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "./tsconfig",
4 | "exclude": ["example"]
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "react-native-iaphub": ["./src/index"]
6 | },
7 | "allowUnreachableCode": false,
8 | "allowUnusedLabels": false,
9 | "esModuleInterop": true,
10 | "importsNotUsedAsValues": "error",
11 | "forceConsistentCasingInFileNames": true,
12 | "jsx": "react",
13 | "lib": ["esnext"],
14 | "module": "esnext",
15 | "moduleResolution": "node",
16 | "noFallthroughCasesInSwitch": true,
17 | "noImplicitReturns": true,
18 | "noImplicitUseStrict": false,
19 | "noStrictGenericChecks": false,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "resolveJsonModule": true,
23 | "skipLibCheck": true,
24 | "strict": true,
25 | "target": "esnext",
26 | "suppressImplicitAnyIndexErrors": true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------