├── .circleci └── config.yml ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .husky ├── .npmignore ├── commit-msg └── pre-commit ├── .yarnrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── android ├── CMakeLists.txt ├── build.gradle ├── cpp-adapter.cpp └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── reactnativeobjcruntime │ ├── ObjcRuntimeModule.java │ └── ObjcRuntimePackage.java ├── babel.config.js ├── example ├── .eslintrc.json ├── android │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── reactnativeobjcruntime │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── reactnativeobjcruntime │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── 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.tsx ├── ios │ ├── File.swift │ ├── ObjcRuntimeExample-Bridging-Header.h │ ├── ObjcRuntimeExample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── ObjcRuntimeExample.xcscheme │ ├── ObjcRuntimeExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── ObjcRuntimeExample │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ ├── main.m │ │ └── objc-constants │ │ │ └── Foundation.json │ ├── Podfile │ └── Podfile.lock ├── metro.config.js ├── package.json ├── src │ ├── App.tsx │ └── objc-types.d.ts └── yarn.lock ├── ios ├── HostObjectClass.h ├── HostObjectClass.mm ├── HostObjectClassInstance.h ├── HostObjectClassInstance.mm ├── HostObjectObjc.h ├── HostObjectObjc.mm ├── HostObjectProtocol.h ├── HostObjectProtocol.mm ├── HostObjectSelector.h ├── HostObjectSelector.mm ├── HostObjectUtils.h ├── HostObjectUtils.mm ├── JSIUtils.h ├── JSIUtils.mm ├── ObjcRuntime.h ├── ObjcRuntime.mm ├── ObjcRuntime.xcodeproj │ └── project.pbxproj ├── react-native-objc-runtime.h └── react-native-objc-runtime.mm ├── package.json ├── react-native-objc-runtime.podspec ├── scripts └── bootstrap.js ├── src ├── __tests__ │ └── index.test.tsx └── index.tsx ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | default: 5 | docker: 6 | - image: circleci/node:10 7 | working_directory: ~/project 8 | 9 | commands: 10 | attach_project: 11 | steps: 12 | - attach_workspace: 13 | at: ~/project 14 | 15 | jobs: 16 | install-dependencies: 17 | executor: default 18 | steps: 19 | - checkout 20 | - attach_project 21 | - restore_cache: 22 | keys: 23 | - dependencies-{{ checksum "package.json" }} 24 | - dependencies- 25 | - restore_cache: 26 | keys: 27 | - dependencies-example-{{ checksum "example/package.json" }} 28 | - dependencies-example- 29 | - run: 30 | name: Install dependencies 31 | command: | 32 | yarn install --cwd example --frozen-lockfile 33 | yarn install --frozen-lockfile 34 | - save_cache: 35 | key: dependencies-{{ checksum "package.json" }} 36 | paths: node_modules 37 | - save_cache: 38 | key: dependencies-example-{{ checksum "example/package.json" }} 39 | paths: example/node_modules 40 | - persist_to_workspace: 41 | root: . 42 | paths: . 43 | 44 | lint: 45 | executor: default 46 | steps: 47 | - attach_project 48 | - run: 49 | name: Lint files 50 | command: | 51 | yarn lint 52 | 53 | typescript: 54 | executor: default 55 | steps: 56 | - attach_project 57 | - run: 58 | name: Typecheck files 59 | command: | 60 | yarn typescript 61 | 62 | unit-tests: 63 | executor: default 64 | steps: 65 | - attach_project 66 | - run: 67 | name: Run unit tests 68 | command: | 69 | yarn test --coverage 70 | - store_artifacts: 71 | path: coverage 72 | destination: coverage 73 | 74 | build-package: 75 | executor: default 76 | steps: 77 | - attach_project 78 | - run: 79 | name: Build package 80 | command: | 81 | yarn prepare 82 | 83 | workflows: 84 | build-and-test: 85 | jobs: 86 | - install-dependencies 87 | - lint: 88 | requires: 89 | - install-dependencies 90 | - typescript: 91 | requires: 92 | - install-dependencies 93 | - unit-tests: 94 | requires: 95 | - install-dependencies 96 | - build-package: 97 | requires: 98 | - install-dependencies 99 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.husky/.npmignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint -E HUSKY_GIT_PARAMS 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint && yarn typescript 5 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Override Yarn command so we can automatically setup the repo on running `yarn` 2 | 3 | yarn-path "scripts/bootstrap.js" 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. 4 | 5 | ## Development workflow 6 | 7 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: 8 | 9 | ```sh 10 | yarn 11 | ``` 12 | 13 | > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development. 14 | 15 | While developing, you can run the [example app](/example/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app. 16 | 17 | To start the packager: 18 | 19 | ```sh 20 | yarn example start 21 | ``` 22 | 23 | To run the example app on Android: 24 | 25 | ```sh 26 | yarn example android 27 | ``` 28 | 29 | To run the example app on iOS: 30 | 31 | ```sh 32 | yarn example ios 33 | ``` 34 | 35 | Make sure your code passes TypeScript and ESLint. Run the following to verify: 36 | 37 | ```sh 38 | yarn typescript 39 | yarn lint 40 | ``` 41 | 42 | To fix formatting errors, run the following: 43 | 44 | ```sh 45 | yarn lint --fix 46 | ``` 47 | 48 | Remember to add tests for your change if possible. Run the unit tests by: 49 | 50 | ```sh 51 | yarn test 52 | ``` 53 | 54 | To edit the Objective-C files, open `example/ios/ObjcRuntimeExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-objc-runtime`. 55 | 56 | To edit the Kotlin files, open `example/android` in Android studio and find the source files at `reactnativeobjcruntime` under `Android`. 57 | 58 | ### Commit message convention 59 | 60 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 61 | 62 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 63 | - `feat`: new features, e.g. add new method to the module. 64 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 65 | - `docs`: changes into documentation, e.g. add usage example for the module.. 66 | - `test`: adding or updating tests, e.g. add integration tests using detox. 67 | - `chore`: tooling changes, e.g. change CI config. 68 | 69 | Our pre-commit hooks verify that your commit message matches this format when committing. 70 | 71 | ### Linting and tests 72 | 73 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 74 | 75 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. 76 | 77 | Our pre-commit hooks verify that the linter and tests pass when committing. 78 | 79 | ### Publishing to npm 80 | 81 | We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc. 82 | 83 | To publish new versions, run the following: 84 | 85 | ```sh 86 | yarn release 87 | ``` 88 | 89 | ### Scripts 90 | 91 | The `package.json` file contains various scripts for common tasks: 92 | 93 | - `yarn bootstrap`: setup project by installing all dependencies and pods. 94 | - `yarn typescript`: type-check files with TypeScript. 95 | - `yarn lint`: lint files with ESLint. 96 | - `yarn test`: run unit tests with Jest. 97 | - `yarn example start`: start the Metro server for the example app. 98 | - `yarn example android`: run the example app on Android. 99 | - `yarn example ios`: run the example app on iOS. 100 | 101 | ### Sending a pull request 102 | 103 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 104 | 105 | When you're sending a pull request: 106 | 107 | - Prefer small pull requests focused on one change. 108 | - Verify that linters and tests are passing. 109 | - Review the documentation to make sure it looks good. 110 | - Follow the pull request template when opening a pull request. 111 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 112 | 113 | ## Code of Conduct 114 | 115 | ### Our Pledge 116 | 117 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 118 | 119 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 120 | 121 | ### Our Standards 122 | 123 | Examples of behavior that contributes to a positive environment for our community include: 124 | 125 | - Demonstrating empathy and kindness toward other people 126 | - Being respectful of differing opinions, viewpoints, and experiences 127 | - Giving and gracefully accepting constructive feedback 128 | - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 129 | - Focusing on what is best not just for us as individuals, but for the overall community 130 | 131 | Examples of unacceptable behavior include: 132 | 133 | - The use of sexualized language or imagery, and sexual attention or 134 | advances of any kind 135 | - Trolling, insulting or derogatory comments, and personal or political attacks 136 | - Public or private harassment 137 | - Publishing others' private information, such as a physical or email 138 | address, without their explicit permission 139 | - Other conduct which could reasonably be considered inappropriate in a 140 | professional setting 141 | 142 | ### Enforcement Responsibilities 143 | 144 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 145 | 146 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 147 | 148 | ### Scope 149 | 150 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 151 | 152 | ### Enforcement 153 | 154 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly. 155 | 156 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 157 | 158 | ### Enforcement Guidelines 159 | 160 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 161 | 162 | #### 1. Correction 163 | 164 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 165 | 166 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 167 | 168 | #### 2. Warning 169 | 170 | **Community Impact**: A violation through a single incident or series of actions. 171 | 172 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 173 | 174 | #### 3. Temporary Ban 175 | 176 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 177 | 178 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 179 | 180 | #### 4. Permanent Ban 181 | 182 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 183 | 184 | **Consequence**: A permanent ban from any sort of public interaction within the community. 185 | 186 | ### Attribution 187 | 188 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, 189 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 190 | 191 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 192 | 193 | [homepage]: https://www.contributor-covenant.org 194 | 195 | For answers to common questions about this code of conduct, see the FAQ at 196 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 197 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ===== react-nativescript-native-runtime ===== 2 | 3 | MIT License 4 | 5 | Copyright (c) 2021 Jamie Birch 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | ===== react-native-vision-camera (used in cpp/JSIUtils.mm and cpp/JSIUtils.h; forked from react-native) ===== 26 | 27 | Copyright 2021 Marc Rousavy 28 | 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 30 | 31 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 32 | 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | ===== react-native ===== 36 | 37 | MIT License 38 | 39 | Copyright (c) Facebook, Inc. and its affiliates. 40 | 41 | Permission is hereby granted, free of charge, to any person obtaining a copy 42 | of this software and associated documentation files (the "Software"), to deal 43 | in the Software without restriction, including without limitation the rights 44 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 45 | copies of the Software, and to permit persons to whom the Software is 46 | furnished to do so, subject to the following conditions: 47 | 48 | The above copyright notice and this permission notice shall be included in all 49 | copies or substantial portions of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 52 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 53 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 54 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 55 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 56 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 57 | SOFTWARE. 58 | 59 | 60 | ====== @nativescript/ios/types ===== 61 | 62 | Apache License 63 | Version 2.0, January 2004 64 | http://www.apache.org/licenses/ 65 | 66 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 67 | 68 | 1. Definitions. 69 | 70 | "License" shall mean the terms and conditions for use, reproduction, 71 | and distribution as defined by Sections 1 through 9 of this document. 72 | 73 | "Licensor" shall mean the copyright owner or entity authorized by 74 | the copyright owner that is granting the License. 75 | 76 | "Legal Entity" shall mean the union of the acting entity and all 77 | other entities that control, are controlled by, or are under common 78 | control with that entity. For the purposes of this definition, 79 | "control" means (i) the power, direct or indirect, to cause the 80 | direction or management of such entity, whether by contract or 81 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 82 | outstanding shares, or (iii) beneficial ownership of such entity. 83 | 84 | "You" (or "Your") shall mean an individual or Legal Entity 85 | exercising permissions granted by this License. 86 | 87 | "Source" form shall mean the preferred form for making modifications, 88 | including but not limited to software source code, documentation 89 | source, and configuration files. 90 | 91 | "Object" form shall mean any form resulting from mechanical 92 | transformation or translation of a Source form, including but 93 | not limited to compiled object code, generated documentation, 94 | and conversions to other media types. 95 | 96 | "Work" shall mean the work of authorship, whether in Source or 97 | Object form, made available under the License, as indicated by a 98 | copyright notice that is included in or attached to the work 99 | (an example is provided in the Appendix below). 100 | 101 | "Derivative Works" shall mean any work, whether in Source or Object 102 | form, that is based on (or derived from) the Work and for which the 103 | editorial revisions, annotations, elaborations, or other modifications 104 | represent, as a whole, an original work of authorship. For the purposes 105 | of this License, Derivative Works shall not include works that remain 106 | separable from, or merely link (or bind by name) to the interfaces of, 107 | the Work and Derivative Works thereof. 108 | 109 | "Contribution" shall mean any work of authorship, including 110 | the original version of the Work and any modifications or additions 111 | to that Work or Derivative Works thereof, that is intentionally 112 | submitted to Licensor for inclusion in the Work by the copyright owner 113 | or by an individual or Legal Entity authorized to submit on behalf of 114 | the copyright owner. For the purposes of this definition, "submitted" 115 | means any form of electronic, verbal, or written communication sent 116 | to the Licensor or its representatives, including but not limited to 117 | communication on electronic mailing lists, source code control systems, 118 | and issue tracking systems that are managed by, or on behalf of, the 119 | Licensor for the purpose of discussing and improving the Work, but 120 | excluding communication that is conspicuously marked or otherwise 121 | designated in writing by the copyright owner as "Not a Contribution." 122 | 123 | "Contributor" shall mean Licensor and any individual or Legal Entity 124 | on behalf of whom a Contribution has been received by Licensor and 125 | subsequently incorporated within the Work. 126 | 127 | 2. Grant of Copyright License. Subject to the terms and conditions of 128 | this License, each Contributor hereby grants to You a perpetual, 129 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 130 | copyright license to reproduce, prepare Derivative Works of, 131 | publicly display, publicly perform, sublicense, and distribute the 132 | Work and such Derivative Works in Source or Object form. 133 | 134 | 3. Grant of Patent License. Subject to the terms and conditions of 135 | this License, each Contributor hereby grants to You a perpetual, 136 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 137 | (except as stated in this section) patent license to make, have made, 138 | use, offer to sell, sell, import, and otherwise transfer the Work, 139 | where such license applies only to those patent claims licensable 140 | by such Contributor that are necessarily infringed by their 141 | Contribution(s) alone or by combination of their Contribution(s) 142 | with the Work to which such Contribution(s) was submitted. If You 143 | institute patent litigation against any entity (including a 144 | cross-claim or counterclaim in a lawsuit) alleging that the Work 145 | or a Contribution incorporated within the Work constitutes direct 146 | or contributory patent infringement, then any patent licenses 147 | granted to You under this License for that Work shall terminate 148 | as of the date such litigation is filed. 149 | 150 | 4. Redistribution. You may reproduce and distribute copies of the 151 | Work or Derivative Works thereof in any medium, with or without 152 | modifications, and in Source or Object form, provided that You 153 | meet the following conditions: 154 | 155 | (a) You must give any other recipients of the Work or 156 | Derivative Works a copy of this License; and 157 | 158 | (b) You must cause any modified files to carry prominent notices 159 | stating that You changed the files; and 160 | 161 | (c) You must retain, in the Source form of any Derivative Works 162 | that You distribute, all copyright, patent, trademark, and 163 | attribution notices from the Source form of the Work, 164 | excluding those notices that do not pertain to any part of 165 | the Derivative Works; and 166 | 167 | (d) If the Work includes a "NOTICE" text file as part of its 168 | distribution, then any Derivative Works that You distribute must 169 | include a readable copy of the attribution notices contained 170 | within such NOTICE file, excluding those notices that do not 171 | pertain to any part of the Derivative Works, in at least one 172 | of the following places: within a NOTICE text file distributed 173 | as part of the Derivative Works; within the Source form or 174 | documentation, if provided along with the Derivative Works; or, 175 | within a display generated by the Derivative Works, if and 176 | wherever such third-party notices normally appear. The contents 177 | of the NOTICE file are for informational purposes only and 178 | do not modify the License. You may add Your own attribution 179 | notices within Derivative Works that You distribute, alongside 180 | or as an addendum to the NOTICE text from the Work, provided 181 | that such additional attribution notices cannot be construed 182 | as modifying the License. 183 | 184 | You may add Your own copyright statement to Your modifications and 185 | may provide additional or different license terms and conditions 186 | for use, reproduction, or distribution of Your modifications, or 187 | for any such Derivative Works as a whole, provided Your use, 188 | reproduction, and distribution of the Work otherwise complies with 189 | the conditions stated in this License. 190 | 191 | 5. Submission of Contributions. Unless You explicitly state otherwise, 192 | any Contribution intentionally submitted for inclusion in the Work 193 | by You to the Licensor shall be under the terms and conditions of 194 | this License, without any additional terms or conditions. 195 | Notwithstanding the above, nothing herein shall supersede or modify 196 | the terms of any separate license agreement you may have executed 197 | with Licensor regarding such Contributions. 198 | 199 | 6. Trademarks. This License does not grant permission to use the trade 200 | names, trademarks, service marks, or product names of the Licensor, 201 | except as required for reasonable and customary use in describing the 202 | origin of the Work and reproducing the content of the NOTICE file. 203 | 204 | 7. Disclaimer of Warranty. Unless required by applicable law or 205 | agreed to in writing, Licensor provides the Work (and each 206 | Contributor provides its Contributions) on an "AS IS" BASIS, 207 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 208 | implied, including, without limitation, any warranties or conditions 209 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 210 | PARTICULAR PURPOSE. You are solely responsible for determining the 211 | appropriateness of using or redistributing the Work and assume any 212 | risks associated with Your exercise of permissions under this License. 213 | 214 | 8. Limitation of Liability. In no event and under no legal theory, 215 | whether in tort (including negligence), contract, or otherwise, 216 | unless required by applicable law (such as deliberate and grossly 217 | negligent acts) or agreed to in writing, shall any Contributor be 218 | liable to You for damages, including any direct, indirect, special, 219 | incidental, or consequential damages of any character arising as a 220 | result of this License or out of the use or inability to use the 221 | Work (including but not limited to damages for loss of goodwill, 222 | work stoppage, computer failure or malfunction, or any and all 223 | other commercial damages or losses), even if such Contributor 224 | has been advised of the possibility of such damages. 225 | 226 | 9. Accepting Warranty or Additional Liability. While redistributing 227 | the Work or Derivative Works thereof, You may choose to offer, 228 | and charge a fee for, acceptance of support, warranty, indemnity, 229 | or other liability obligations and/or rights consistent with this 230 | License. However, in accepting such obligations, You may act only 231 | on Your own behalf and on Your sole responsibility, not on behalf 232 | of any other Contributor, and only if You agree to indemnify, 233 | defend, and hold each Contributor harmless for any liability 234 | incurred by, or claims asserted against, such Contributor by reason 235 | of your accepting any such warranty or additional liability. 236 | 237 | END OF TERMS AND CONDITIONS 238 | 239 | APPENDIX: How to apply the Apache License to your work. 240 | 241 | To apply the Apache License to your work, attach the following 242 | boilerplate notice, with the fields enclosed by brackets "{}" 243 | replaced with your own identifying information. (Don't include 244 | the brackets!) The text should be enclosed in the appropriate 245 | comment syntax for the file format. We also recommend that a 246 | file or class name and description of purpose be included on the 247 | same "printed page" as the copyright notice for easier 248 | identification within third-party archives. 249 | 250 | Copyright (c) 2020, nStudio LLC 251 | 252 | Licensed under the Apache License, Version 2.0 (the "License"); 253 | you may not use this file except in compliance with the License. 254 | You may obtain a copy of the License at 255 | 256 | http://www.apache.org/licenses/LICENSE-2.0 257 | 258 | Unless required by applicable law or agreed to in writing, software 259 | distributed under the License is distributed on an "AS IS" BASIS, 260 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 261 | See the License for the specific language governing permissions and 262 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-native-runtime 2 | 3 |

4 | npm version 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 |

18 | 19 | Access the native APIs directly from the React Native JS context! 20 | 21 | For now, we support just Objective-C (for iOS/macOS/tvOS devices, but I'll refer to iOS for brevity). Adding support for Java (for Android devices) is on my wishlist for the future. 22 | 23 | ## Installation 24 | 25 | Please get in contact if these instructions don't work for you! 26 | 27 | ```sh 28 | # WARNING: only tested with react-native@^0.64.2. 29 | # Will not work on lower versions; *may* work on newer versions. 30 | 31 | # Install the npm package 32 | npm install --save react-native-native-runtime 33 | 34 | # Install the Cocoapod 35 | cd ios && pod install && cd .. 36 | ``` 37 | 38 | ## Usage 39 | 40 | For now, as I haven't installed the package yet, just clone the repo and play around with [example/src/App.tsx](example/src/App.tsx). 41 | 42 | ### The Java runtime 43 | 44 | 🚧 I haven't yet started building this, and it's further away from my expertise (I'm more of an iOS developer). Get in contact if you'd like to get involved! 45 | 46 | ### The Obj-C runtime 47 | 48 | All Obj-C APIs are exposed through a proxy object called `objc`, which is injected into the JS context's global scope early at run time (provided you have remembered to install the Cocoapod and rebuilt your iOS app since then). Technically it occurs when React Native finds the `setBridge` method in `ios/ObjcRuntime.mm` just like for any other iOS native module and then calls the `ObjcRuntimeJsi::install()` method within. 49 | 50 | #### TypeScript typings? 51 | 52 | ⚠️ First, a warning: We only have very minimal hand-written TypeScript typings at the moment. Get used to `any` type until we make a more lasting solution, most likely based on the NativeScript metadata/typings generator. For now, copy [example/src/objc-types.d.ts](example/src/objc-types.d.ts) to get started. 53 | 54 | #### The `objc` proxy object 55 | 56 | As mentioned, this is available in the global scope on any Apple app. 57 | 58 | Generally, you'll use the `objc` proxy object to look up some native data type. If a match is found for the native data type, we wrap it in a C++ HostObject class instance that is shared across to the JS context. 59 | 60 | ```typescript 61 | // Class lookup: 62 | // Returns a JS HostObject wrapping a native class. 63 | // This works via the Obj-C runtime helper NSClassFromString, 64 | // so it has O(1) complexity. 65 | objc.NSString; 66 | 67 | // Protocol lookup: 68 | // Returns a JS HostObject wrapping a native Protocol (if a class 69 | // with the same name wasn't found first). 70 | // This works via the Obj-C runtime helper NSProtocolFromString, 71 | // so it has O(1) complexity. 72 | objc.NSSecureCoding; 73 | 74 | // Constant/variable lookup: 75 | // Returns a JS HostObject wrapping any native global symbol (the 76 | // most common ones being constants and variables). 77 | // This works by looking up the symbol from the executable (a 78 | // fallback undertaken when neither NSClassFromString nor 79 | // NSProtocolFromString return a match for the given string). 80 | // This works via dlsym, so I believe it has O(N) complexity, but 81 | // probably isn't too slow anyway. 82 | objc.NSStringTransformLatinToHiragana; 83 | 84 | // Selector lookup: 85 | // Returns a JS HostObject wrapping a native Selector. 86 | // This works via the Obj-C runtime helper NSSelectorFromString, so 87 | // it has O(1) complexity. 88 | // I can't think of a good example for this, but it's possible. 89 | objc.getSelector("NoGoodExample:soWhoKnows:"); 90 | 91 | // Will return an array of all Obj-C classes and all convenience 92 | // methods, but that's all. 93 | // Does not, for example, list out all constants/variables/protocols 94 | // available. Those have to be looked up individually. 95 | Object.keys(objc); 96 | 97 | // Will return the string: 98 | // [object HostObjectObjc] 99 | objc.toString(); 100 | 101 | // Will cause an infinite loop and crash! Need some advice from JSI 102 | // experts on this. 103 | // It involves the following getters being called in sequence: 104 | // $$typeof -> Symbol.toStringTag -> toJSON -> Symbol.toStringTag -> 105 | // Symbol.toStringTag -> Symbol.toStringTag -> toString 106 | console.log(objc); 107 | ``` 108 | 109 | #### Host Objects 110 | 111 | Again, this is a C++ HostObject class instance that is shared across to the JS context. Don't ask me how the memory-management works! That's one for a JSI expert (and I'd love some code review on my approach). 112 | 113 | The `objc` proxy object is one such HostObject. I've made some others: 114 | 115 | * HostObjectClass (wraps a class) 116 | * HostObjectClassInstance (wraps a class instance) 117 | * HostObjectProtocol (wraps a protocol) 118 | * HostObjectSelector (wraps a selector) 119 | 120 | *TODO: I'll likely make these expand a common abstract class. For now, they all directly extend `facebook::jsi::HostObject`.* 121 | 122 | These may expand in future, but the former two cover a huge API surface on their own. I'll focus on documenting those, as the latter two are largely empty skeletons. 123 | 124 | 125 | ##### HostObjectClass 126 | 127 | You can obtain a HostObjectClass by looking up a class on the `objc` proxy object: 128 | 129 | ```typescript 130 | const nSStringClass: objc.NSString = objc.NSString; 131 | ``` 132 | 133 | You can also call class methods (AKA static methods, in other languages) on it: 134 | 135 | ```typescript 136 | const voice: objc.AVSpeechSynthesisVoice = 137 | objc.AVSpeechSynthesisVoice['voiceWithLanguage:']('en-GB'); 138 | ``` 139 | 140 | We'll cover what you can do with a class instance in the next section. 141 | 142 | ##### HostObjectClassInstance 143 | 144 | Once you have a class instance, you can call instance methods. The method names mirror the Obj-C selector, hence you'll be seeing a lot of colons. The JS invocation takes as many arguments as the Obj-C selector suggests (each colon indicates one param). 145 | 146 | ```typescript 147 | // Initialise an NSString 148 | const hello: objc.NSString = 149 | objc.NSString.alloc()['initWithString:']('Hello'); 150 | 151 | // Return a new NSString by concatenating it 152 | const helloWorld: objc.NSString = 153 | hello['stringByAppendingString:'](', world!'); 154 | ``` 155 | 156 | You will have noticed that we're passing JS primitive types in as parameters. All JS primitive types are marshalled into equivalent Obj-C types: 157 | 158 | * string -> NSString 159 | * number -> NSNumber 160 | * boolean -> NSBoolean 161 | * Array -> NSArray 162 | * object -> NSDictionary 163 | * undefined -> nil 164 | * null -> nil 165 | 166 | Conversely, you can also marshal the following types from Obj-C to JS: 167 | 168 | * NSString -> string 169 | * NSNumber -> number 170 | * NSBoolean -> boolean 171 | * NSArray -> Array (provided each of the constituent values are marshal-friendly) 172 | * NSDictionary -> object (provided each of the constituent values are marshal-friendly) 173 | * kCFNull -> null 174 | * nil -> undefined 175 | 176 | Do so using the `toJS()` method on a HostObjectClassInstance: 177 | 178 | ```typescript 179 | // Marshal the NSString to a JS primitive string 180 | console.log(helloWorld.toJS()); 181 | ``` 182 | 183 | Beyond that, you can get the keys on the class instance: 184 | 185 | ```typescript 186 | // Will return a list of all instance variables, properties, and 187 | // native methods, and some added methods like toString(). 188 | // TODO: list out all the *inherited* instance variables, 189 | // properties, and methods as well. 190 | Object.keys(helloWorld); 191 | ``` 192 | 193 | You can also use getters: 194 | 195 | ```typescript 196 | // Allocate a native class instance 197 | const utterance: objc.AVSpeechUtterance = 198 | objc.AVSpeechUtterance.alloc()['initWithString:']('Hello, world!'); 199 | 200 | // Get the property 201 | utterance.voice; 202 | ``` 203 | 204 | ... and call setters: 205 | 206 | ```typescript 207 | // Allocate a native class instance 208 | const utterance: objc.AVSpeechUtterance = 209 | objc.AVSpeechUtterance.alloc()['initWithString:']('Hello, world!'); 210 | 211 | // Set properties on it 212 | utterance.voice = 213 | objc.AVSpeechSynthesisVoice['voiceWithLanguage:']('ja-JP'); 214 | ``` 215 | 216 | ... but both getters and setters are currently *very* experimental and I need some help from an Obj-C expert to get them right. 217 | 218 | ## Is it complete? 219 | 220 | The Java runtime isn't even started yet, for one thing. 221 | 222 | The Obj-C runtime lets you do a lot of things already, but it is far from complete. There are some glaring things like lack of `console.log()` support and incomplete getter/setter support that are high priorities to solve, and peer review from JSI experts is also needed. 223 | 224 | ## Is it production-ready? 225 | 226 | No, but you can help that by contributing! 227 | 228 | Seriously, expect errors to be thrown if you don't know what you're doing (particularly as there are no TypeScript typings yet). 229 | 230 | ## Contributing 231 | 232 | Get in touch on the `#objc-runtime` channel of the [React Native JSI Discord](https://discord.com/invite/QDMxYqXw), or [send me a message](https://twitter.com/LinguaBrowse) on Twitter! 233 | 234 | I'll start putting some issues together to indicate tasks that need help. 235 | 236 | ## Acknowledgements 237 | 238 | JSI is an API that is not yet publicly documented, so I've stood on the shoulders of a few giants to get here. 239 | 240 | I couldn't have done this without [Marc Rousavy](https://github.com/mrousavy)'s [react-native-vision-camera](https://github.com/mrousavy/react-native-vision-camera) project to refer to (I even make direct use of his JSIUtils helper methods in this codebase). He has done an exceptional amount of work digging into JSI by asking around in GitHub issues and other channels, paving the way for the rest of us. 241 | 242 | Thanks also to [Oscar Franco](https://github.com/ospfranco) for his JSI guides (e.g. [this one](https://ospfranco.com/post/2021/02/24/how-to-create-a-javascript-jsi-module/)) and [starter template](https://github.com/ospfranco/react-native-jsi-template). 243 | 244 | I also got a lot of mileage out of Ammar Ahmed's [JSI guide](https://blog.notesnook.com/getting-started-react-native-jsi/), too – well worth a read. 245 | 246 | Thank you to [Takuya Matsuyama](https://github.com/craftzdog) too, for his very minimal [react-native-quick-base64](https://github.com/craftzdog/react-native-quick-base64) JSI module, and for being one of the first community members to jump into the ring to wrestle with JSI. 247 | 248 | ## License 249 | 250 | MIT 251 | -------------------------------------------------------------------------------- /android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | set (CMAKE_VERBOSE_MAKEFILE ON) 4 | set (CMAKE_CXX_STANDARD 11) 5 | 6 | add_library(cpp 7 | SHARED 8 | ../cpp/example.cpp 9 | cpp-adapter.cpp 10 | ) 11 | 12 | # Specifies a path to native header files. 13 | include_directories( 14 | ../cpp 15 | ) 16 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | if (project == rootProject) { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | jcenter() 7 | } 8 | 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.5.3' 11 | } 12 | } 13 | } 14 | 15 | apply plugin: 'com.android.library' 16 | 17 | def safeExtGet(prop, fallback) { 18 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 19 | } 20 | 21 | android { 22 | compileSdkVersion safeExtGet('ObjcRuntime_compileSdkVersion', 29) 23 | defaultConfig { 24 | minSdkVersion safeExtGet('ObjcRuntime_minSdkVersion', 16) 25 | targetSdkVersion safeExtGet('ObjcRuntime_targetSdkVersion', 29) 26 | versionCode 1 27 | versionName "1.0" 28 | 29 | externalNativeBuild { 30 | cmake { 31 | cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all" 32 | abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' 33 | } 34 | } 35 | 36 | } 37 | 38 | externalNativeBuild { 39 | cmake { 40 | path "CMakeLists.txt" 41 | } 42 | } 43 | 44 | buildTypes { 45 | release { 46 | minifyEnabled false 47 | } 48 | } 49 | lintOptions { 50 | disable 'GradleCompatible' 51 | } 52 | compileOptions { 53 | sourceCompatibility JavaVersion.VERSION_1_8 54 | targetCompatibility JavaVersion.VERSION_1_8 55 | } 56 | } 57 | 58 | repositories { 59 | mavenLocal() 60 | maven { 61 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 62 | url("$rootDir/../node_modules/react-native/android") 63 | } 64 | google() 65 | mavenCentral() 66 | jcenter() 67 | } 68 | 69 | dependencies { 70 | //noinspection GradleDynamicVersion 71 | implementation "com.facebook.react:react-native:+" // From node_modules 72 | } 73 | -------------------------------------------------------------------------------- /android/cpp-adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "example.h" 3 | 4 | extern "C" 5 | JNIEXPORT jint JNICALL 6 | Java_com_reactnativeobjcruntime_ObjcRuntimeModule_nativeMultiply(JNIEnv *env, jclass type, jint a, jint b) { 7 | return example::multiply(a, b); 8 | } 9 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativeobjcruntime/ObjcRuntimeModule.java: -------------------------------------------------------------------------------- 1 | package com.reactnativeobjcruntime; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.bridge.Promise; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 8 | import com.facebook.react.bridge.ReactMethod; 9 | import com.facebook.react.module.annotations.ReactModule; 10 | 11 | @ReactModule(name = ObjcRuntimeModule.NAME) 12 | public class ObjcRuntimeModule extends ReactContextBaseJavaModule { 13 | public static final String NAME = "ObjcRuntime"; 14 | 15 | public ObjcRuntimeModule(ReactApplicationContext reactContext) { 16 | super(reactContext); 17 | } 18 | 19 | @Override 20 | @NonNull 21 | public String getName() { 22 | return NAME; 23 | } 24 | 25 | static { 26 | try { 27 | // Used to load the 'native-lib' library on application startup. 28 | System.loadLibrary("cpp"); 29 | } catch (Exception ignored) { 30 | } 31 | } 32 | 33 | // Example method 34 | // See https://reactnative.dev/docs/native-modules-android 35 | @ReactMethod 36 | public void multiply(int a, int b, Promise promise) { 37 | promise.resolve(nativeMultiply(a, b)); 38 | } 39 | 40 | public static native int nativeMultiply(int a, int b); 41 | } 42 | -------------------------------------------------------------------------------- /android/src/main/java/com/reactnativeobjcruntime/ObjcRuntimePackage.java: -------------------------------------------------------------------------------- 1 | package com.reactnativeobjcruntime; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.facebook.react.ReactPackage; 6 | import com.facebook.react.bridge.NativeModule; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.uimanager.ViewManager; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class ObjcRuntimePackage implements ReactPackage { 15 | @NonNull 16 | @Override 17 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) { 18 | List modules = new ArrayList<>(); 19 | modules.add(new ObjcRuntimeModule(reactContext)); 20 | return modules; 21 | } 22 | 23 | @NonNull 24 | @Override 25 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) { 26 | return Collections.emptyList(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": ["*.ts", "*.tsx"], 5 | "rules": { 6 | "no-undef": "off" 7 | } 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // https://reactnative.dev/docs/performance#enable-the-ram-format 22 | * bundleCommand: "ram-bundle", 23 | * 24 | * // whether to bundle JS and assets in debug mode 25 | * bundleInDebug: false, 26 | * 27 | * // whether to bundle JS and assets in release mode 28 | * bundleInRelease: true, 29 | * 30 | * // whether to bundle JS and assets in another build variant (if configured). 31 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 32 | * // The configuration property can be in the following formats 33 | * // 'bundleIn${productFlavor}${buildType}' 34 | * // 'bundleIn${buildType}' 35 | * // bundleInFreeDebug: true, 36 | * // bundleInPaidRelease: true, 37 | * // bundleInBeta: true, 38 | * 39 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 40 | * // for ObjcRuntimeExample: to disable dev mode in the staging build type (if configured) 41 | * devDisabledInStaging: true, 42 | * // The configuration property can be in the following formats 43 | * // 'devDisabledIn${productFlavor}${buildType}' 44 | * // 'devDisabledIn${buildType}' 45 | * 46 | * // the root of your project, i.e. where "package.json" lives 47 | * root: "../../", 48 | * 49 | * // where to put the JS bundle asset in debug mode 50 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 51 | * 52 | * // where to put the JS bundle asset in release mode 53 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 54 | * 55 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 56 | * // require('./image.png')), in debug mode 57 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 58 | * 59 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 60 | * // require('./image.png')), in release mode 61 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 62 | * 63 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 64 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 65 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 66 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 67 | * // for ObjcRuntimeExample, you might want to remove it from here. 68 | * inputExcludes: ["android/**", "ios/**"], 69 | * 70 | * // override which node gets called and with what additional arguments 71 | * nodeExecutableAndArgs: ["node"], 72 | * 73 | * // supply additional arguments to the packager 74 | * extraPackagerArgs: [] 75 | * ] 76 | */ 77 | 78 | project.ext.react = [ 79 | enableHermes: false, // clean and rebuild if changing 80 | entryFile: "index.tsx", 81 | ] 82 | 83 | apply from: "../../node_modules/react-native/react.gradle" 84 | 85 | /** 86 | * Set this to true to create two separate APKs instead of one: 87 | * - An APK that only works on ARM devices 88 | * - An APK that only works on x86 devices 89 | * The advantage is the size of the APK is reduced by about 4MB. 90 | * Upload all the APKs to the Play Store and people will download 91 | * the correct one based on the CPU architecture of their device. 92 | */ 93 | def enableSeparateBuildPerCPUArchitecture = false 94 | 95 | /** 96 | * Run Proguard to shrink the Java bytecode in release builds. 97 | */ 98 | def enableProguardInReleaseBuilds = false 99 | 100 | /** 101 | * The preferred build flavor of JavaScriptCore. 102 | * 103 | * For ObjcRuntimeExample, to use the international variant, you can use: 104 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 105 | * 106 | * The international variant includes ICU i18n library and necessary data 107 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 108 | * give correct results when using with locales other than en-US. Note that 109 | * this variant is about 6MiB larger per architecture than default. 110 | */ 111 | def jscFlavor = 'org.webkit:android-jsc:+' 112 | 113 | /** 114 | * Whether to enable the Hermes VM. 115 | * 116 | * This should be set on project.ext.react and mirrored here. If it is not set 117 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode 118 | * and the benefits of using Hermes will therefore be sharply reduced. 119 | */ 120 | def enableHermes = project.ext.react.get("enableHermes", false); 121 | 122 | android { 123 | compileSdkVersion rootProject.ext.compileSdkVersion 124 | 125 | compileOptions { 126 | sourceCompatibility JavaVersion.VERSION_1_8 127 | targetCompatibility JavaVersion.VERSION_1_8 128 | } 129 | 130 | defaultConfig { 131 | applicationId "com.example.reactnativeobjcruntime" 132 | minSdkVersion rootProject.ext.minSdkVersion 133 | targetSdkVersion rootProject.ext.targetSdkVersion 134 | versionCode 1 135 | versionName "1.0" 136 | } 137 | splits { 138 | abi { 139 | reset() 140 | enable enableSeparateBuildPerCPUArchitecture 141 | universalApk false // If true, also generate a universal APK 142 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 143 | } 144 | } 145 | signingConfigs { 146 | debug { 147 | storeFile file('debug.keystore') 148 | storePassword 'android' 149 | keyAlias 'androiddebugkey' 150 | keyPassword 'android' 151 | } 152 | } 153 | buildTypes { 154 | debug { 155 | signingConfig signingConfigs.debug 156 | } 157 | release { 158 | // Caution! In production, you need to generate your own keystore file. 159 | // see https://reactnative.dev/docs/signed-apk-android. 160 | signingConfig signingConfigs.debug 161 | minifyEnabled enableProguardInReleaseBuilds 162 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 163 | } 164 | } 165 | // applicationVariants are e.g. debug, release 166 | applicationVariants.all { variant -> 167 | variant.outputs.each { output -> 168 | // For each separate APK per architecture, set a unique version code as described here: 169 | // https://developer.android.com/studio/build/configure-apk-splits.html 170 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 171 | def abi = output.getFilter(OutputFile.ABI) 172 | if (abi != null) { // null for the universal-debug, universal-release variants 173 | output.versionCodeOverride = 174 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 175 | } 176 | 177 | } 178 | } 179 | } 180 | 181 | dependencies { 182 | implementation fileTree(dir: "libs", include: ["*.jar"]) 183 | //noinspection GradleDynamicVersion 184 | implementation "com.facebook.react:react-native:+" // From node_modules 185 | 186 | 187 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" 188 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { 189 | exclude group:'com.facebook.fbjni' 190 | } 191 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 192 | exclude group:'com.facebook.flipper' 193 | exclude group:'com.squareup.okhttp3', module:'okhttp' 194 | } 195 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { 196 | exclude group:'com.facebook.flipper' 197 | } 198 | 199 | if (enableHermes) { 200 | def hermesPath = "../../node_modules/hermes-engine/android/"; 201 | debugImplementation files(hermesPath + "hermes-debug.aar") 202 | releaseImplementation files(hermesPath + "hermes-release.aar") 203 | } else { 204 | implementation jscFlavor 205 | } 206 | 207 | implementation project(':reactnativeobjcruntime') 208 | } 209 | 210 | // Run this once to be able to run the application with BUCK 211 | // puts all compile dependencies into folder libs for BUCK to use 212 | task copyDownloadableDepsToLibs(type: Copy) { 213 | from configurations.compile 214 | into 'libs' 215 | } 216 | 217 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 218 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/com/example/reactnativeobjcruntime/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.example.reactnativeobjcruntime; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 32 | client.addPlugin(new ReactFlipperPlugin()); 33 | client.addPlugin(new DatabasesFlipperPlugin(context)); 34 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 35 | client.addPlugin(CrashReporterPlugin.getInstance()); 36 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 37 | NetworkingModule.setCustomClientBuilder( 38 | new NetworkingModule.CustomClientBuilder() { 39 | @Override 40 | public void apply(OkHttpClient.Builder builder) { 41 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 42 | } 43 | }); 44 | client.addPlugin(networkFlipperPlugin); 45 | client.start(); 46 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 47 | // Hence we run if after all native modules have been initialized 48 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 49 | if (reactContext == null) { 50 | reactInstanceManager.addReactInstanceEventListener( 51 | new ReactInstanceManager.ReactInstanceEventListener() { 52 | @Override 53 | public void onReactContextInitialized(ReactContext reactContext) { 54 | reactInstanceManager.removeReactInstanceEventListener(this); 55 | reactContext.runOnNativeModulesQueueThread( 56 | new Runnable() { 57 | @Override 58 | public void run() { 59 | client.addPlugin(new FrescoFlipperPlugin()); 60 | } 61 | }); 62 | } 63 | }); 64 | } else { 65 | client.addPlugin(new FrescoFlipperPlugin()); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/reactnativeobjcruntime/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.reactnativeobjcruntime; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "ObjcRuntimeExample"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/reactnativeobjcruntime/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.reactnativeobjcruntime; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.ReactInstanceManager; 10 | import com.facebook.soloader.SoLoader; 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.util.List; 13 | import com.reactnativeobjcruntime.ObjcRuntimePackage; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = 18 | new ReactNativeHost(this) { 19 | @Override 20 | public boolean getUseDeveloperSupport() { 21 | return BuildConfig.DEBUG; 22 | } 23 | 24 | @Override 25 | protected List getPackages() { 26 | @SuppressWarnings("UnnecessaryLocalVariable") 27 | List packages = new PackageList(this).getPackages(); 28 | // Packages that cannot be autolinked yet can be added manually here, for ObjcRuntimeExample: 29 | // packages.add(new MyReactNativePackage()); 30 | packages.add(new ObjcRuntimePackage()); 31 | return packages; 32 | } 33 | 34 | @Override 35 | protected String getJSMainModuleName() { 36 | return "index"; 37 | } 38 | }; 39 | 40 | @Override 41 | public ReactNativeHost getReactNativeHost() { 42 | return mReactNativeHost; 43 | } 44 | 45 | @Override 46 | public void onCreate() { 47 | super.onCreate(); 48 | SoLoader.init(this, /* native exopackage */ false); 49 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); // Remove this line if you don't want Flipper enabled 50 | } 51 | 52 | /** 53 | * Loads Flipper in React Native templates. 54 | * 55 | * @param context 56 | */ 57 | private static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 58 | if (BuildConfig.DEBUG) { 59 | try { 60 | /* 61 | We use reflection here to pick up the class that initializes Flipper, 62 | since Flipper library is not available in release mode 63 | */ 64 | Class aClass = Class.forName("com.example.reactnativeobjcruntime.ReactNativeFlipper"); 65 | aClass 66 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 67 | .invoke(null, context, reactInstanceManager); 68 | } catch (ClassNotFoundException e) { 69 | e.printStackTrace(); 70 | } catch (NoSuchMethodException e) { 71 | e.printStackTrace(); 72 | } catch (IllegalAccessException e) { 73 | e.printStackTrace(); 74 | } catch (InvocationTargetException e) { 75 | e.printStackTrace(); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ObjcRuntime Example 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | minSdkVersion = 16 6 | compileSdkVersion = 29 7 | targetSdkVersion = 29 8 | } 9 | repositories { 10 | google() 11 | mavenCentral() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:3.5.3") 16 | 17 | // NOTE: Do not place your application dependencies here; they belong 18 | // in the individual module build.gradle files 19 | } 20 | } 21 | 22 | allprojects { 23 | repositories { 24 | mavenLocal() 25 | maven { 26 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 27 | url("$rootDir/../node_modules/react-native/android") 28 | } 29 | maven { 30 | // Android JSC is installed from npm 31 | url("$rootDir/../node_modules/jsc-android/dist") 32 | } 33 | 34 | google() 35 | mavenCentral() 36 | jcenter() 37 | maven { url 'https://www.jitpack.io' } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /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: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 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 | android.useAndroidX=true 21 | android.enableJetifier=true 22 | FLIPPER_VERSION=0.54.0 23 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shirakaba/react-native-native-runtime/2d1d04ef32dd23643594a3cb4179e59897fc1725/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-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or 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 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /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 http://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 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'ObjcRuntimeExample' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | 5 | include ':reactnativeobjcruntime' 6 | project(':reactnativeobjcruntime').projectDir = new File(rootProject.projectDir, '../../android') 7 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ObjcRuntimeExample", 3 | "displayName": "ObjcRuntime Example" 4 | } 5 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = { 5 | presets: ['module:metro-react-native-babel-preset'], 6 | plugins: [ 7 | [ 8 | 'module-resolver', 9 | { 10 | extensions: ['.tsx', '.ts', '.js', '.json'], 11 | alias: { 12 | [pak.name]: path.join(__dirname, '..', pak.source), 13 | }, 14 | }, 15 | ], 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /example/index.tsx: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './src/App'; 3 | import { name as appName } from './app.json'; 4 | 5 | AppRegistry.registerComponent(appName, () => App); 6 | -------------------------------------------------------------------------------- /example/ios/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // ObjcRuntimeExample 4 | // 5 | 6 | import Foundation 7 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample.xcodeproj/xcshareddata/xcschemes/ObjcRuntimeExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | 66 | 68 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | #import 11 | #import 12 | #import 13 | 14 | #ifdef FB_SONARKIT_ENABLED 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | static void InitializeFlipper(UIApplication *application) { 22 | FlipperClient *client = [FlipperClient sharedClient]; 23 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 24 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 25 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 26 | [client addPlugin:[FlipperKitReactPlugin new]]; 27 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 28 | [client start]; 29 | } 30 | #endif 31 | 32 | @implementation AppDelegate 33 | 34 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 35 | { 36 | #ifdef FB_SONARKIT_ENABLED 37 | InitializeFlipper(application); 38 | #endif 39 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 40 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 41 | moduleName:@"ObjcRuntimeExample" 42 | initialProperties:nil]; 43 | if (@available(iOS 13.0, *)) { 44 | rootView.backgroundColor = [UIColor systemBackgroundColor]; 45 | } else { 46 | rootView.backgroundColor = [UIColor redColor]; 47 | } 48 | 49 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 50 | UIViewController *rootViewController = [UIViewController new]; 51 | rootViewController.view = rootView; 52 | self.window.rootViewController = rootViewController; 53 | [self.window makeKeyAndVisible]; 54 | return YES; 55 | } 56 | 57 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 58 | { 59 | #if DEBUG 60 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 61 | #else 62 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 63 | #endif 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ObjcRuntime 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 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSLocationWhenInUseUsageDescription 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UIViewControllerBasedStatusBarAppearance 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /example/ios/ObjcRuntimeExample/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #import 9 | 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '10.0' 5 | 6 | target 'ObjcRuntimeExample' do 7 | config = use_native_modules! 8 | 9 | use_react_native!( 10 | :path => config[:reactNativePath], 11 | # to enable hermes on iOS, change `false` to `true` and then install pods 12 | :hermes_enabled => false 13 | ) 14 | 15 | pod 'react-native-objc-runtime', :path => '../..' 16 | 17 | # Enables Flipper. 18 | # 19 | # Note that if you have use_frameworks! enabled, Flipper will not work and 20 | # you should disable these next few lines. 21 | # use_flipper!() 22 | # post_install do |installer| 23 | # react_native_post_install(installer) 24 | # end 25 | end 26 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - boost-for-react-native (1.63.0) 3 | - DoubleConversion (1.1.6) 4 | - FBLazyVector (0.64.2) 5 | - FBReactNativeSpec (0.64.2): 6 | - RCT-Folly (= 2020.01.13.00) 7 | - RCTRequired (= 0.64.2) 8 | - RCTTypeSafety (= 0.64.2) 9 | - React-Core (= 0.64.2) 10 | - React-jsi (= 0.64.2) 11 | - ReactCommon/turbomodule/core (= 0.64.2) 12 | - glog (0.3.5) 13 | - RCT-Folly (2020.01.13.00): 14 | - boost-for-react-native 15 | - DoubleConversion 16 | - glog 17 | - RCT-Folly/Default (= 2020.01.13.00) 18 | - RCT-Folly/Default (2020.01.13.00): 19 | - boost-for-react-native 20 | - DoubleConversion 21 | - glog 22 | - RCTRequired (0.64.2) 23 | - RCTTypeSafety (0.64.2): 24 | - FBLazyVector (= 0.64.2) 25 | - RCT-Folly (= 2020.01.13.00) 26 | - RCTRequired (= 0.64.2) 27 | - React-Core (= 0.64.2) 28 | - React (0.64.2): 29 | - React-Core (= 0.64.2) 30 | - React-Core/DevSupport (= 0.64.2) 31 | - React-Core/RCTWebSocket (= 0.64.2) 32 | - React-RCTActionSheet (= 0.64.2) 33 | - React-RCTAnimation (= 0.64.2) 34 | - React-RCTBlob (= 0.64.2) 35 | - React-RCTImage (= 0.64.2) 36 | - React-RCTLinking (= 0.64.2) 37 | - React-RCTNetwork (= 0.64.2) 38 | - React-RCTSettings (= 0.64.2) 39 | - React-RCTText (= 0.64.2) 40 | - React-RCTVibration (= 0.64.2) 41 | - React-callinvoker (0.64.2) 42 | - React-Core (0.64.2): 43 | - glog 44 | - RCT-Folly (= 2020.01.13.00) 45 | - React-Core/Default (= 0.64.2) 46 | - React-cxxreact (= 0.64.2) 47 | - React-jsi (= 0.64.2) 48 | - React-jsiexecutor (= 0.64.2) 49 | - React-perflogger (= 0.64.2) 50 | - Yoga 51 | - React-Core/CoreModulesHeaders (0.64.2): 52 | - glog 53 | - RCT-Folly (= 2020.01.13.00) 54 | - React-Core/Default 55 | - React-cxxreact (= 0.64.2) 56 | - React-jsi (= 0.64.2) 57 | - React-jsiexecutor (= 0.64.2) 58 | - React-perflogger (= 0.64.2) 59 | - Yoga 60 | - React-Core/Default (0.64.2): 61 | - glog 62 | - RCT-Folly (= 2020.01.13.00) 63 | - React-cxxreact (= 0.64.2) 64 | - React-jsi (= 0.64.2) 65 | - React-jsiexecutor (= 0.64.2) 66 | - React-perflogger (= 0.64.2) 67 | - Yoga 68 | - React-Core/DevSupport (0.64.2): 69 | - glog 70 | - RCT-Folly (= 2020.01.13.00) 71 | - React-Core/Default (= 0.64.2) 72 | - React-Core/RCTWebSocket (= 0.64.2) 73 | - React-cxxreact (= 0.64.2) 74 | - React-jsi (= 0.64.2) 75 | - React-jsiexecutor (= 0.64.2) 76 | - React-jsinspector (= 0.64.2) 77 | - React-perflogger (= 0.64.2) 78 | - Yoga 79 | - React-Core/RCTActionSheetHeaders (0.64.2): 80 | - glog 81 | - RCT-Folly (= 2020.01.13.00) 82 | - React-Core/Default 83 | - React-cxxreact (= 0.64.2) 84 | - React-jsi (= 0.64.2) 85 | - React-jsiexecutor (= 0.64.2) 86 | - React-perflogger (= 0.64.2) 87 | - Yoga 88 | - React-Core/RCTAnimationHeaders (0.64.2): 89 | - glog 90 | - RCT-Folly (= 2020.01.13.00) 91 | - React-Core/Default 92 | - React-cxxreact (= 0.64.2) 93 | - React-jsi (= 0.64.2) 94 | - React-jsiexecutor (= 0.64.2) 95 | - React-perflogger (= 0.64.2) 96 | - Yoga 97 | - React-Core/RCTBlobHeaders (0.64.2): 98 | - glog 99 | - RCT-Folly (= 2020.01.13.00) 100 | - React-Core/Default 101 | - React-cxxreact (= 0.64.2) 102 | - React-jsi (= 0.64.2) 103 | - React-jsiexecutor (= 0.64.2) 104 | - React-perflogger (= 0.64.2) 105 | - Yoga 106 | - React-Core/RCTImageHeaders (0.64.2): 107 | - glog 108 | - RCT-Folly (= 2020.01.13.00) 109 | - React-Core/Default 110 | - React-cxxreact (= 0.64.2) 111 | - React-jsi (= 0.64.2) 112 | - React-jsiexecutor (= 0.64.2) 113 | - React-perflogger (= 0.64.2) 114 | - Yoga 115 | - React-Core/RCTLinkingHeaders (0.64.2): 116 | - glog 117 | - RCT-Folly (= 2020.01.13.00) 118 | - React-Core/Default 119 | - React-cxxreact (= 0.64.2) 120 | - React-jsi (= 0.64.2) 121 | - React-jsiexecutor (= 0.64.2) 122 | - React-perflogger (= 0.64.2) 123 | - Yoga 124 | - React-Core/RCTNetworkHeaders (0.64.2): 125 | - glog 126 | - RCT-Folly (= 2020.01.13.00) 127 | - React-Core/Default 128 | - React-cxxreact (= 0.64.2) 129 | - React-jsi (= 0.64.2) 130 | - React-jsiexecutor (= 0.64.2) 131 | - React-perflogger (= 0.64.2) 132 | - Yoga 133 | - React-Core/RCTSettingsHeaders (0.64.2): 134 | - glog 135 | - RCT-Folly (= 2020.01.13.00) 136 | - React-Core/Default 137 | - React-cxxreact (= 0.64.2) 138 | - React-jsi (= 0.64.2) 139 | - React-jsiexecutor (= 0.64.2) 140 | - React-perflogger (= 0.64.2) 141 | - Yoga 142 | - React-Core/RCTTextHeaders (0.64.2): 143 | - glog 144 | - RCT-Folly (= 2020.01.13.00) 145 | - React-Core/Default 146 | - React-cxxreact (= 0.64.2) 147 | - React-jsi (= 0.64.2) 148 | - React-jsiexecutor (= 0.64.2) 149 | - React-perflogger (= 0.64.2) 150 | - Yoga 151 | - React-Core/RCTVibrationHeaders (0.64.2): 152 | - glog 153 | - RCT-Folly (= 2020.01.13.00) 154 | - React-Core/Default 155 | - React-cxxreact (= 0.64.2) 156 | - React-jsi (= 0.64.2) 157 | - React-jsiexecutor (= 0.64.2) 158 | - React-perflogger (= 0.64.2) 159 | - Yoga 160 | - React-Core/RCTWebSocket (0.64.2): 161 | - glog 162 | - RCT-Folly (= 2020.01.13.00) 163 | - React-Core/Default (= 0.64.2) 164 | - React-cxxreact (= 0.64.2) 165 | - React-jsi (= 0.64.2) 166 | - React-jsiexecutor (= 0.64.2) 167 | - React-perflogger (= 0.64.2) 168 | - Yoga 169 | - React-CoreModules (0.64.2): 170 | - FBReactNativeSpec (= 0.64.2) 171 | - RCT-Folly (= 2020.01.13.00) 172 | - RCTTypeSafety (= 0.64.2) 173 | - React-Core/CoreModulesHeaders (= 0.64.2) 174 | - React-jsi (= 0.64.2) 175 | - React-RCTImage (= 0.64.2) 176 | - ReactCommon/turbomodule/core (= 0.64.2) 177 | - React-cxxreact (0.64.2): 178 | - boost-for-react-native (= 1.63.0) 179 | - DoubleConversion 180 | - glog 181 | - RCT-Folly (= 2020.01.13.00) 182 | - React-callinvoker (= 0.64.2) 183 | - React-jsi (= 0.64.2) 184 | - React-jsinspector (= 0.64.2) 185 | - React-perflogger (= 0.64.2) 186 | - React-runtimeexecutor (= 0.64.2) 187 | - React-jsi (0.64.2): 188 | - boost-for-react-native (= 1.63.0) 189 | - DoubleConversion 190 | - glog 191 | - RCT-Folly (= 2020.01.13.00) 192 | - React-jsi/Default (= 0.64.2) 193 | - React-jsi/Default (0.64.2): 194 | - boost-for-react-native (= 1.63.0) 195 | - DoubleConversion 196 | - glog 197 | - RCT-Folly (= 2020.01.13.00) 198 | - React-jsiexecutor (0.64.2): 199 | - DoubleConversion 200 | - glog 201 | - RCT-Folly (= 2020.01.13.00) 202 | - React-cxxreact (= 0.64.2) 203 | - React-jsi (= 0.64.2) 204 | - React-perflogger (= 0.64.2) 205 | - React-jsinspector (0.64.2) 206 | - react-native-objc-runtime (0.2.0): 207 | - React 208 | - React-callinvoker 209 | - React-Core 210 | - React-perflogger (0.64.2) 211 | - React-RCTActionSheet (0.64.2): 212 | - React-Core/RCTActionSheetHeaders (= 0.64.2) 213 | - React-RCTAnimation (0.64.2): 214 | - FBReactNativeSpec (= 0.64.2) 215 | - RCT-Folly (= 2020.01.13.00) 216 | - RCTTypeSafety (= 0.64.2) 217 | - React-Core/RCTAnimationHeaders (= 0.64.2) 218 | - React-jsi (= 0.64.2) 219 | - ReactCommon/turbomodule/core (= 0.64.2) 220 | - React-RCTBlob (0.64.2): 221 | - FBReactNativeSpec (= 0.64.2) 222 | - RCT-Folly (= 2020.01.13.00) 223 | - React-Core/RCTBlobHeaders (= 0.64.2) 224 | - React-Core/RCTWebSocket (= 0.64.2) 225 | - React-jsi (= 0.64.2) 226 | - React-RCTNetwork (= 0.64.2) 227 | - ReactCommon/turbomodule/core (= 0.64.2) 228 | - React-RCTImage (0.64.2): 229 | - FBReactNativeSpec (= 0.64.2) 230 | - RCT-Folly (= 2020.01.13.00) 231 | - RCTTypeSafety (= 0.64.2) 232 | - React-Core/RCTImageHeaders (= 0.64.2) 233 | - React-jsi (= 0.64.2) 234 | - React-RCTNetwork (= 0.64.2) 235 | - ReactCommon/turbomodule/core (= 0.64.2) 236 | - React-RCTLinking (0.64.2): 237 | - FBReactNativeSpec (= 0.64.2) 238 | - React-Core/RCTLinkingHeaders (= 0.64.2) 239 | - React-jsi (= 0.64.2) 240 | - ReactCommon/turbomodule/core (= 0.64.2) 241 | - React-RCTNetwork (0.64.2): 242 | - FBReactNativeSpec (= 0.64.2) 243 | - RCT-Folly (= 2020.01.13.00) 244 | - RCTTypeSafety (= 0.64.2) 245 | - React-Core/RCTNetworkHeaders (= 0.64.2) 246 | - React-jsi (= 0.64.2) 247 | - ReactCommon/turbomodule/core (= 0.64.2) 248 | - React-RCTSettings (0.64.2): 249 | - FBReactNativeSpec (= 0.64.2) 250 | - RCT-Folly (= 2020.01.13.00) 251 | - RCTTypeSafety (= 0.64.2) 252 | - React-Core/RCTSettingsHeaders (= 0.64.2) 253 | - React-jsi (= 0.64.2) 254 | - ReactCommon/turbomodule/core (= 0.64.2) 255 | - React-RCTText (0.64.2): 256 | - React-Core/RCTTextHeaders (= 0.64.2) 257 | - React-RCTVibration (0.64.2): 258 | - FBReactNativeSpec (= 0.64.2) 259 | - RCT-Folly (= 2020.01.13.00) 260 | - React-Core/RCTVibrationHeaders (= 0.64.2) 261 | - React-jsi (= 0.64.2) 262 | - ReactCommon/turbomodule/core (= 0.64.2) 263 | - React-runtimeexecutor (0.64.2): 264 | - React-jsi (= 0.64.2) 265 | - ReactCommon/turbomodule/core (0.64.2): 266 | - DoubleConversion 267 | - glog 268 | - RCT-Folly (= 2020.01.13.00) 269 | - React-callinvoker (= 0.64.2) 270 | - React-Core (= 0.64.2) 271 | - React-cxxreact (= 0.64.2) 272 | - React-jsi (= 0.64.2) 273 | - React-perflogger (= 0.64.2) 274 | - Yoga (1.14.0) 275 | 276 | DEPENDENCIES: 277 | - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) 278 | - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) 279 | - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) 280 | - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) 281 | - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) 282 | - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) 283 | - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) 284 | - React (from `../node_modules/react-native/`) 285 | - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) 286 | - React-Core (from `../node_modules/react-native/`) 287 | - React-Core/DevSupport (from `../node_modules/react-native/`) 288 | - React-Core/RCTWebSocket (from `../node_modules/react-native/`) 289 | - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) 290 | - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) 291 | - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) 292 | - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) 293 | - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) 294 | - react-native-objc-runtime (from `../..`) 295 | - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) 296 | - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) 297 | - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) 298 | - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) 299 | - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) 300 | - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) 301 | - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) 302 | - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) 303 | - React-RCTText (from `../node_modules/react-native/Libraries/Text`) 304 | - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) 305 | - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) 306 | - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) 307 | - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) 308 | 309 | SPEC REPOS: 310 | trunk: 311 | - boost-for-react-native 312 | 313 | EXTERNAL SOURCES: 314 | DoubleConversion: 315 | :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" 316 | FBLazyVector: 317 | :path: "../node_modules/react-native/Libraries/FBLazyVector" 318 | FBReactNativeSpec: 319 | :path: "../node_modules/react-native/React/FBReactNativeSpec" 320 | glog: 321 | :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" 322 | RCT-Folly: 323 | :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" 324 | RCTRequired: 325 | :path: "../node_modules/react-native/Libraries/RCTRequired" 326 | RCTTypeSafety: 327 | :path: "../node_modules/react-native/Libraries/TypeSafety" 328 | React: 329 | :path: "../node_modules/react-native/" 330 | React-callinvoker: 331 | :path: "../node_modules/react-native/ReactCommon/callinvoker" 332 | React-Core: 333 | :path: "../node_modules/react-native/" 334 | React-CoreModules: 335 | :path: "../node_modules/react-native/React/CoreModules" 336 | React-cxxreact: 337 | :path: "../node_modules/react-native/ReactCommon/cxxreact" 338 | React-jsi: 339 | :path: "../node_modules/react-native/ReactCommon/jsi" 340 | React-jsiexecutor: 341 | :path: "../node_modules/react-native/ReactCommon/jsiexecutor" 342 | React-jsinspector: 343 | :path: "../node_modules/react-native/ReactCommon/jsinspector" 344 | react-native-objc-runtime: 345 | :path: "../.." 346 | React-perflogger: 347 | :path: "../node_modules/react-native/ReactCommon/reactperflogger" 348 | React-RCTActionSheet: 349 | :path: "../node_modules/react-native/Libraries/ActionSheetIOS" 350 | React-RCTAnimation: 351 | :path: "../node_modules/react-native/Libraries/NativeAnimation" 352 | React-RCTBlob: 353 | :path: "../node_modules/react-native/Libraries/Blob" 354 | React-RCTImage: 355 | :path: "../node_modules/react-native/Libraries/Image" 356 | React-RCTLinking: 357 | :path: "../node_modules/react-native/Libraries/LinkingIOS" 358 | React-RCTNetwork: 359 | :path: "../node_modules/react-native/Libraries/Network" 360 | React-RCTSettings: 361 | :path: "../node_modules/react-native/Libraries/Settings" 362 | React-RCTText: 363 | :path: "../node_modules/react-native/Libraries/Text" 364 | React-RCTVibration: 365 | :path: "../node_modules/react-native/Libraries/Vibration" 366 | React-runtimeexecutor: 367 | :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" 368 | ReactCommon: 369 | :path: "../node_modules/react-native/ReactCommon" 370 | Yoga: 371 | :path: "../node_modules/react-native/ReactCommon/yoga" 372 | 373 | SPEC CHECKSUMS: 374 | boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c 375 | DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de 376 | FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b 377 | FBReactNativeSpec: 79c8f2e24c41de3fc8ede5fca3d216e7a33a2aa4 378 | glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62 379 | RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c 380 | RCTRequired: 6d3e854f0e7260a648badd0d44fc364bc9da9728 381 | RCTTypeSafety: c1f31d19349c6b53085766359caac425926fafaa 382 | React: bda6b6d7ae912de97d7a61aa5c160db24aa2ad69 383 | React-callinvoker: 9840ea7e8e88ed73d438edb725574820b29b5baa 384 | React-Core: b5e385da7ce5f16a220fc60fd0749eae2c6120f0 385 | React-CoreModules: 17071a4e2c5239b01585f4aa8070141168ab298f 386 | React-cxxreact: 9be7b6340ed9f7c53e53deca7779f07cd66525ba 387 | React-jsi: 67747b9722f6dab2ffe15b011bcf6b3f2c3f1427 388 | React-jsiexecutor: 80c46bd381fd06e418e0d4f53672dc1d1945c4c3 389 | React-jsinspector: cc614ec18a9ca96fd275100c16d74d62ee11f0ae 390 | react-native-objc-runtime: fb386d114554d6f39aa4fcbe9c96ccaccbf0a910 391 | React-perflogger: 25373e382fed75ce768a443822f07098a15ab737 392 | React-RCTActionSheet: af7796ba49ffe4ca92e7277a5d992d37203f7da5 393 | React-RCTAnimation: 6a2e76ab50c6f25b428d81b76a5a45351c4d77aa 394 | React-RCTBlob: 02a2887023e0eed99391b6445b2e23a2a6f9226d 395 | React-RCTImage: ce5bf8e7438f2286d9b646a05d6ab11f38b0323d 396 | React-RCTLinking: ccd20742de14e020cb5f99d5c7e0bf0383aefbd9 397 | React-RCTNetwork: dfb9d089ab0753e5e5f55fc4b1210858f7245647 398 | React-RCTSettings: b14aef2d83699e48b410fb7c3ba5b66cd3291ae2 399 | React-RCTText: 41a2e952dd9adc5caf6fb68ed46b275194d5da5f 400 | React-RCTVibration: 24600e3b1aaa77126989bc58b6747509a1ba14f3 401 | React-runtimeexecutor: a9904c6d0218fb9f8b19d6dd88607225927668f9 402 | ReactCommon: 149906e01aa51142707a10665185db879898e966 403 | Yoga: 575c581c63e0d35c9a83f4b46d01d63abc1100ac 404 | 405 | PODFILE CHECKSUM: 80e0e3cd0c93a9cab7eb4478e314caa531ecd713 406 | 407 | COCOAPODS: 1.11.3 408 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const blacklist = require('metro-config/src/defaults/exclusionList'); 3 | const escape = require('escape-string-regexp'); 4 | const pak = require('../package.json'); 5 | 6 | const root = path.resolve(__dirname, '..'); 7 | 8 | const modules = Object.keys({ 9 | ...pak.peerDependencies, 10 | }); 11 | 12 | module.exports = { 13 | projectRoot: __dirname, 14 | watchFolders: [root], 15 | 16 | // We need to make sure that only one version is loaded for peerDependencies 17 | // So we blacklist them at the root, and alias them to the versions in example's node_modules 18 | resolver: { 19 | blacklistRE: blacklist( 20 | modules.map( 21 | (m) => 22 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 23 | ) 24 | ), 25 | 26 | extraNodeModules: modules.reduce((acc, name) => { 27 | acc[name] = path.join(__dirname, 'node_modules', name); 28 | return acc; 29 | }, {}), 30 | }, 31 | 32 | transformer: { 33 | getTransformOptions: async () => ({ 34 | transform: { 35 | experimentalImportSupport: false, 36 | inlineRequires: true, 37 | }, 38 | }), 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-native-runtime-example", 3 | "description": "Example app for react-native-native-runtime", 4 | "version": "0.0.1", 5 | "private": true, 6 | "scripts": { 7 | "android": "react-native run-android", 8 | "ios": "react-native run-ios", 9 | "start": "react-native start" 10 | }, 11 | "dependencies": { 12 | "react": "^17.0.1", 13 | "react-native": "^0.64.2" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "^7.12.9", 17 | "@babel/runtime": "^7.12.5", 18 | "babel-plugin-module-resolver": "^4.0.0", 19 | "metro-react-native-babel-preset": "^0.64.0", 20 | "webpack": "^4.44.1", 21 | "webpack-cli": "^4.7.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | /// 2 | import * as React from 'react'; 3 | 4 | import { StyleSheet, View, Text } from 'react-native'; 5 | 6 | export default function App() { 7 | React.useEffect(() => { 8 | const hello: objc.NSString = 9 | objc.NSString.alloc()['initWithString:']('Hello'); 10 | const helloWorld: objc.NSString = 11 | hello['stringByAppendingString:'](', world!'); 12 | console.log('Concatenate two NSStrings:', helloWorld.toJS()); 13 | 14 | console.log( 15 | `Marshal UTF-8 text back and forth, given "白樺":`, 16 | objc.NSString.alloc()['initWithString:']('白樺').toJS() 17 | ); 18 | 19 | console.log( 20 | `Get unicode name for each character, given "🐝":`, 21 | objc.NSString.alloc() 22 | ['initWithString:']('🐝') 23 | ['stringByApplyingTransform:reverse:']('Name-Any', false) 24 | .toJS() 25 | ); 26 | 27 | // Fun with Foundation String Transforms! 28 | // @see https://nshipster.com/ios9/ 29 | // @see https://nshipster.com/cfstringtransform/ 30 | // @see https://sites.google.com/site/icuprojectuserguide/transforms/general#TOC-ICU-Transliterators 31 | // @see https://twitter.com/LinguaBrowse/status/1390225265612181505?s=20 32 | console.log( 33 | 'Convert Chinese script from Trad. -> Simp., given "漢字簡化爭論":', 34 | objc.NSString.alloc() 35 | ['initWithString:']('漢字簡化爭論') 36 | ['stringByApplyingTransform:reverse:']('Simplified-Traditional', false) 37 | .toJS() 38 | ); 39 | 40 | console.log( 41 | 'Look up the global variable "NSStringTransformLatinToHiragana" in order to transliterate Japanese Hiragana to Latin, given "しらかば":', 42 | objc.NSString.alloc() 43 | ['initWithString:']('しらかば') 44 | ['stringByApplyingTransform:reverse:']( 45 | (objc as any).NSStringTransformLatinToHiragana, 46 | false 47 | ) 48 | .toJS() 49 | ); 50 | 51 | console.log( 52 | 'Do the same, this time using the equivalent Core Foundation symbol, "kCFStringTransformToLatin":', 53 | objc.NSString.alloc() 54 | ['initWithString:']('しらかば') 55 | ['stringByApplyingTransform:reverse:']( 56 | (objc as any).kCFStringTransformToLatin, 57 | false 58 | ) 59 | .toJS() 60 | ); 61 | 62 | console.log( 63 | 'Transliterate Korean Hangul to Latin, given "안녕하세요":', 64 | objc.NSString.alloc() 65 | ['initWithString:']('안녕하세요') 66 | ['stringByApplyingTransform:reverse:']('Latin-Hangul', false) 67 | .toJS() 68 | ); 69 | 70 | /* Uncomment this if you're happy to hear a voice speak! */ 71 | // const utterance = 72 | // objc.AVSpeechUtterance.alloc()['initWithString:']('Hello, world!'); 73 | // utterance.voice = 74 | // objc.AVSpeechSynthesisVoice['voiceWithLanguage:']('en-GB'); 75 | // objc.AVSpeechSynthesizer.alloc().init()['speakUtterance:'](utterance); 76 | }, []); 77 | 78 | return ( 79 | 80 | Placeholder text 81 | 82 | ); 83 | } 84 | 85 | const styles = StyleSheet.create({ 86 | container: { 87 | flex: 1, 88 | alignItems: 'center', 89 | justifyContent: 'center', 90 | }, 91 | box: { 92 | width: 60, 93 | height: 60, 94 | marginVertical: 20, 95 | }, 96 | }); 97 | -------------------------------------------------------------------------------- /ios/HostObjectClass.h: -------------------------------------------------------------------------------- 1 | // Forked from: https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.h 2 | // See also: https://github.com/facebook/react-native/blob/1465c8f3874cdee8c325ab4a4916fda0b3e43bdb/Libraries/Blob/RCTBlobCollector.h 3 | 4 | #pragma once 5 | 6 | #import 7 | 8 | using namespace facebook; 9 | 10 | class JSI_EXPORT HostObjectClass: public jsi::HostObject { 11 | public: 12 | HostObjectClass(Class clazz); 13 | ~HostObjectClass(); 14 | 15 | public: 16 | jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override; 17 | std::vector getPropertyNames(jsi::Runtime& rt) override; 18 | 19 | Class clazz_; 20 | }; 21 | -------------------------------------------------------------------------------- /ios/HostObjectClass.mm: -------------------------------------------------------------------------------- 1 | // Forked from https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.mm 2 | 3 | #import 4 | #import 5 | #import 6 | #import "JSIUtils.h" 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import "HostObjectClass.h" 13 | #import "HostObjectClassInstance.h" 14 | #import "HostObjectUtils.h" 15 | 16 | HostObjectClass::HostObjectClass(Class clazz) 17 | : clazz_(clazz) {} 18 | 19 | HostObjectClass::~HostObjectClass() {} 20 | 21 | std::vector HostObjectClass::getPropertyNames(jsi::Runtime& rt) { 22 | std::vector result; 23 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString"))); 24 | 25 | // All class methods 26 | // @see https://www.cocoawithlove.com/2008/02/imp-of-current-method.html 27 | // @see https://stackoverflow.com/questions/2094702/get-all-methods-of-an-objective-c-class-or-instance 28 | unsigned int methodCount; 29 | Method *methodList = class_copyMethodList(object_getClass(clazz_), &methodCount); 30 | for (unsigned int i = 0; i < methodCount; i++){ 31 | NSString *selectorNSString = NSStringFromSelector(method_getName(methodList[i])); 32 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string([selectorNSString UTF8String], [selectorNSString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); 33 | } 34 | free(methodList); 35 | 36 | // TODO: copy all methods of any subclasses too. 37 | 38 | // // Copy properties for this instance's class. I think this means instance fields 39 | // unsigned int propertyCount; 40 | // objc_property_t _Nonnull *propertyList = class_copyPropertyList(clazz_, &propertyCount); 41 | // for (unsigned int i = 0; i < propertyCount; i++){ 42 | // NSString *propertyNSString = [NSString stringWithUTF8String:property_getName(propertyList[i])]; 43 | // result.push_back(jsi::PropNameID::forUtf8(rt, std::string([propertyNSString UTF8String], [propertyNSString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); 44 | // } 45 | // free(propertyList); 46 | 47 | return result; 48 | } 49 | 50 | jsi::Value HostObjectClass::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) { 51 | auto name = propName.utf8(runtime); 52 | NSString *nameNSString = [NSString stringWithUTF8String:name.c_str()]; 53 | if([nameNSString isEqualToString:@"Symbol.toStringTag"]){ 54 | // This seems to happen when you execute this JS: 55 | // console.log(`objc.NSString:`, objc.NSString); 56 | NSString *stringification = @"[object HostObjectClass]"; 57 | 58 | return jsi::String::createFromUtf8(runtime, stringification.UTF8String); 59 | } 60 | 61 | // For HostObjectClassInstance, see instancesRespondToSelector, for looking up instance methods. 62 | SEL sel = NSSelectorFromString(nameNSString); 63 | if([clazz_ respondsToSelector:sel]){ 64 | return invokeClassMethod(runtime, name, sel, clazz_); 65 | } 66 | 67 | objc_property_t property = class_getProperty(clazz_, nameNSString.UTF8String); 68 | if(property){ 69 | const char *propertyName = property_getName(property); 70 | if(propertyName){ 71 | NSObject* value = [clazz_ valueForKey:[NSString stringWithUTF8String:propertyName]]; 72 | return convertObjCObjectToJSIValue(runtime, value); 73 | } 74 | } 75 | 76 | // Next, handle things other than class methods. 77 | throw jsi::JSError(runtime, "HostObjectClass::get: We currently only support accesses into class methods and class fields."); 78 | } 79 | -------------------------------------------------------------------------------- /ios/HostObjectClassInstance.h: -------------------------------------------------------------------------------- 1 | // Forked from: https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.h 2 | // See also: https://github.com/facebook/react-native/blob/1465c8f3874cdee8c325ab4a4916fda0b3e43bdb/Libraries/Blob/RCTBlobCollector.h 3 | 4 | #pragma once 5 | 6 | #import 7 | 8 | using namespace facebook; 9 | 10 | class JSI_EXPORT HostObjectClassInstance: public jsi::HostObject { 11 | public: 12 | HostObjectClassInstance(NSObject* instance); 13 | ~HostObjectClassInstance(); 14 | 15 | public: 16 | jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override; 17 | void set(jsi::Runtime& runtime, const jsi::PropNameID& propName, const jsi::Value& value) override; 18 | std::vector getPropertyNames(jsi::Runtime& rt) override; 19 | 20 | NSObject* instance_; 21 | }; 22 | -------------------------------------------------------------------------------- /ios/HostObjectClassInstance.mm: -------------------------------------------------------------------------------- 1 | // Forked from https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.mm 2 | 3 | #import 4 | #import 5 | #import 6 | #import "HostObjectClass.h" 7 | #import "HostObjectClassInstance.h" 8 | #import "JSIUtils.h" 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import "HostObjectUtils.h" 15 | #import 16 | 17 | HostObjectClassInstance::HostObjectClassInstance(NSObject *instance) 18 | : instance_(instance) {} 19 | 20 | HostObjectClassInstance::~HostObjectClassInstance() {} 21 | 22 | std::vector HostObjectClassInstance::getPropertyNames(jsi::Runtime& rt) { 23 | std::vector result; 24 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString"))); 25 | 26 | // Copy instance methods. 27 | // @see https://www.cocoawithlove.com/2008/02/imp-of-current-method.html 28 | // @see https://stackoverflow.com/questions/2094702/get-all-methods-of-an-objective-c-class-or-instance 29 | unsigned int methodCount; 30 | Method *methodList = class_copyMethodList([instance_ class], &methodCount); 31 | for (unsigned int i = 0; i < methodCount; i++){ 32 | NSString *selectorNSString = NSStringFromSelector(method_getName(methodList[i])); 33 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string([selectorNSString UTF8String], [selectorNSString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); 34 | } 35 | free(methodList); 36 | 37 | // Copy properties for this instance's class. 38 | unsigned int propertyCount; 39 | objc_property_t _Nonnull *propertyList = class_copyPropertyList(object_getClass(instance_), &propertyCount); 40 | for (unsigned int i = 0; i < propertyCount; i++){ 41 | property_getName(propertyList[i]); 42 | NSString *propertyNSString = [NSString stringWithUTF8String:property_getName(propertyList[i])]; 43 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string([propertyNSString UTF8String], [propertyNSString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); 44 | } 45 | free(propertyList); 46 | 47 | // Copy ivars for this instance. 48 | unsigned int ivarCount; 49 | Ivar _Nonnull *ivarList = class_copyIvarList([instance_ class], &ivarCount); 50 | for (unsigned int i = 0; i < ivarCount; i++){ 51 | NSString *ivarNSString = [NSString stringWithUTF8String:ivar_getName(ivarList[i])]; 52 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string([ivarNSString UTF8String], [ivarNSString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); 53 | } 54 | free(propertyList); 55 | 56 | // TODO: do the same for subclasses, too. 57 | 58 | return result; 59 | } 60 | 61 | jsi::Value HostObjectClassInstance::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) { 62 | auto name = propName.utf8(runtime); 63 | NSString *nameNSString = [NSString stringWithUTF8String:name.c_str()]; 64 | if([nameNSString isEqualToString:@"Symbol.toStringTag"]){ 65 | // This seems to happen when you execute this JS: 66 | // console.log(`objc.NSString:`, objc.NSString); 67 | NSString *stringification = @"[object HostObjectClassInstance]"; 68 | 69 | return jsi::String::createFromUtf8(runtime, stringification.UTF8String); 70 | } 71 | 72 | if([nameNSString isEqualToString:@"$$typeof"]){ 73 | // This seems to happen when you execute this JS: 74 | // const nsString = objc.NSString.alloc(); 75 | // console.log(`nsString:`, nsString); 76 | // FIXME: My approach here seems to be incorrect, as it puts us into an infinite loop of (approximately): 77 | // $$typeof -> Symbol.toStringTag -> toJSON -> Symbol.toStringTag -> Symbol.toStringTag -> Symbol.toStringTag -> Symbol.toStringTag -> toString 78 | NSString *stringification = NSStringFromClass([instance_ class]); 79 | return jsi::String::createFromUtf8(runtime, stringification.UTF8String); 80 | } 81 | // NSString *stringTag = [NSString stringWithFormat: @"HostObjectClassInstance<%@*>", NSStringFromClass([instance_ class])]; 82 | // NSString *stringTag = [NSString stringWithFormat: @"HostObjectClassInstance"]; 83 | // 84 | // // If you implement this, it'll be used in preference over .toString(). 85 | // if(name == "Symbol.toStringTag"){ 86 | // return jsi::String::createFromUtf8(runtime, stringTag.UTF8String); 87 | // } 88 | // 89 | // if(name == "Symbol.toPrimitive"){ 90 | // return jsi::Function::createFromHostFunction( 91 | // runtime, 92 | // jsi::PropNameID::forAscii(runtime, name), 93 | // 1, 94 | // [this, stringTag] (jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t) -> jsi::Value { 95 | // auto hint = arguments[0].asString(runtime).utf8(runtime); 96 | // if(hint == "number"){ // Handles: console.log(+hostObjectObjc); 97 | // return [instance_ isKindOfClass: [NSNumber class]] ? 98 | // convertNSNumberToJSINumber(runtime, (NSNumber *)instance_) : 99 | // jsi::Value(-1); // I'd prefer to return NaN here, but can't see how..! 100 | // } else if(hint == "string"){ 101 | // if([instance_ isKindOfClass: [NSString class]]){ 102 | // return convertNSStringToJSIString(runtime, (NSString *)instance_); 103 | // } 104 | // } 105 | // return jsi::String::createFromUtf8(runtime, [NSString stringWithFormat: @"[object %@]", stringTag].UTF8String); 106 | // } 107 | // ); 108 | // } 109 | 110 | if (name == "toJS") { 111 | auto toJS = [this] (jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { 112 | return convertObjCObjectToJSIValue(runtime, instance_); 113 | }; 114 | return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toJS"), 0, toJS); 115 | } 116 | 117 | // For HostObjectClassInstance, see instancesRespondToSelector, for looking up instance methods. 118 | SEL sel = NSSelectorFromString(nameNSString); 119 | if([instance_ respondsToSelector:sel]){ 120 | return invokeClassInstanceMethod(runtime, name, sel, instance_); 121 | } 122 | 123 | objc_property_t property = class_getProperty([instance_ class], nameNSString.UTF8String); 124 | if(property){ 125 | const char *propertyName = property_getName(property); 126 | if(propertyName){ 127 | NSObject* value = [instance_ valueForKey:[NSString stringWithUTF8String:propertyName]]; 128 | return convertObjCObjectToJSIValue(runtime, value); 129 | } 130 | } 131 | 132 | // Next, handle things other than class methods. 133 | throw jsi::JSError(runtime, "HostObjectClassInstance::get: We currently only support accesses into class instance methods and class properties."); 134 | } 135 | 136 | void HostObjectClassInstance::set(jsi::Runtime& runtime, const jsi::PropNameID& propName, const jsi::Value& value) { 137 | auto name = propName.utf8(runtime); 138 | NSString *nameNSString = [NSString stringWithUTF8String:name.c_str()]; 139 | 140 | RCTBridge *bridge = [RCTBridge currentBridge]; 141 | auto jsCallInvoker = bridge.jsCallInvoker; 142 | 143 | id marshalled; 144 | if(value.isObject()){ 145 | jsi::Object obj = value.asObject(runtime); 146 | if(obj.isHostObject((runtime))){ 147 | if(HostObjectClass* hostObjectClass = dynamic_cast(obj.asHostObject(runtime).get())){ 148 | marshalled = hostObjectClass->clazz_; 149 | } else if(HostObjectClassInstance* hostObjectClassInstance = dynamic_cast(obj.asHostObject(runtime).get())){ 150 | marshalled = hostObjectClassInstance->instance_; 151 | } else { 152 | throw jsi::JSError(runtime, "invokeClassInstanceMethod: Unwrapping HostObjects other than ClassHostObject not yet supported!"); 153 | } 154 | } else { 155 | marshalled = convertJSIValueToObjCObject(runtime, value, jsCallInvoker); 156 | } 157 | } else { 158 | marshalled = convertJSIValueToObjCObject(runtime, value, jsCallInvoker); 159 | } 160 | 161 | // @see https://stackoverflow.com/questions/29641396/how-to-get-and-set-a-property-value-with-runtime-in-objective-c/29642341 162 | objc_property_t property = class_getProperty([instance_ class], [nameNSString cStringUsingEncoding:NSASCIIStringEncoding]); 163 | 164 | // NSString *setterName; 165 | // const char* setterNameC = property_copyAttributeValue(property, "S"); 166 | // if(setterNameC == NULL){ 167 | // setterName = [NSString stringWithFormat: @"set%@%@:", [nameNSString substringToIndex: 1].uppercaseString, [nameNSString substringFromIndex: 1]]; 168 | // } else { 169 | // setterName = [NSString stringWithCString:setterNameC encoding:NSASCIIStringEncoding]; 170 | // } 171 | // SEL setterSel = sel_registerName(setterName.UTF8String); 172 | 173 | // Some properties are synthesised and thus have a backing variable, and that's what you have to call the method upon. 174 | // I guess if this property returns NULL, then we don't look for a backing variable at all and just use the name as-is? 175 | // @see https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101 176 | // The string starts with a T followed by the @encode type and a comma, and finishes with a V followed by the name of the backing instance variable. Between these, the attributes are specified by the following descriptors, separated by commas: 177 | // property_getAttributes(property) returned the following: 178 | // T@"AVSpeechSynthesisVoice",&,N,V_voice 179 | unsigned int outCount; 180 | objc_property_attribute_t *attributes = property_copyAttributeList(property, &outCount); 181 | objc_property_attribute_t finalAttribute = attributes[outCount - 1]; 182 | const char *finalAttributeName = finalAttribute.name; 183 | NSLog(@"finalAttribute.name: '%s'; finalAttribute.value: '%s'", finalAttribute.name, finalAttribute.value); 184 | 185 | // Properties are typically backed by an instance variable with a leading underscore, so creating a property called firstName would have a backing instance variable with the name _firstName. You should only access that private instance variable if you override the getter/setter or if you need to setup the ivar in the class init method. 186 | Ivar ivar = class_getInstanceVariable([instance_ class], finalAttributeName); 187 | object_setIvar(instance_, ivar, marshalled); 188 | 189 | // Problem: I guess this means we skip past the setter and go straight to the backing variable, meaning 190 | // we side-step any (necessary) side-effects. We may need to keep looking for a better way of doing this. 191 | 192 | 193 | //// SEL sel = NSSelectorFromString(nameNSString); 194 | // Method method = class_getInstanceMethod([instance_ class], setterSel); 195 | // NSLog(@"Checking whether the class %@ have an instance method for the setter %@...", [instance_ class], nameNSString); 196 | // // [instance_ performSelector:sel] 197 | // 198 | // if (![instance_ respondsToSelector:setterSel]) { 199 | // throw jsi::JSError(runtime, [[NSString stringWithFormat:@"TypeError: No such setter: %@", nameNSString] cStringUsingEncoding:NSUTF8StringEncoding]); 200 | // } 201 | // 202 | // invokeClassInstanceMethod(runtime, [setterName cStringUsingEncoding:NSUTF8StringEncoding], setterSel, instance_); 203 | // 204 | // // [instance_ perf] 205 | // 206 | // // TODO: implement the setter itself 207 | // // ((void (*) (id,SEL,id)) objc_msgSend) (instance_,sel,marshalled); 208 | } 209 | -------------------------------------------------------------------------------- /ios/HostObjectObjc.h: -------------------------------------------------------------------------------- 1 | // Forked from: https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.h 2 | 3 | #pragma once 4 | 5 | #import 6 | 7 | using namespace facebook; 8 | 9 | class JSI_EXPORT HostObjectObjc: public jsi::HostObject { 10 | 11 | public: 12 | jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override; 13 | std::vector getPropertyNames(jsi::Runtime& rt) override; 14 | }; 15 | -------------------------------------------------------------------------------- /ios/HostObjectObjc.mm: -------------------------------------------------------------------------------- 1 | // Forked from https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.mm 2 | 3 | #import 4 | #import 5 | #import 6 | #import "HostObjectObjc.h" 7 | #import "HostObjectClass.h" 8 | #import "HostObjectClassInstance.h" 9 | #import "HostObjectSelector.h" 10 | #import "HostObjectProtocol.h" 11 | #import "JSIUtils.h" 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | 22 | std::vector HostObjectObjc::getPropertyNames(jsi::Runtime& rt) { 23 | std::vector result; 24 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString"))); 25 | 26 | // List out the classes 27 | int numClasses = objc_getClassList(NULL, 0); 28 | Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) *numClasses); 29 | numClasses = objc_getClassList(classes, numClasses); 30 | for (int i = 0; i < numClasses; i++) { 31 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string(class_getName(classes[i])))); 32 | } 33 | free(classes); 34 | 35 | // I'm not aware of any objc runtime function by which to list out selectors and protocols! 36 | return result; 37 | } 38 | 39 | jsi::Value HostObjectObjc::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) { 40 | auto name = propName.utf8(runtime); 41 | NSString* nameNSString = [NSString stringWithUTF8String:name.c_str()]; 42 | 43 | if (name == "toString") { 44 | auto toString = [this] (jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) -> jsi::Value { 45 | NSString* string = [NSString stringWithFormat:@"[object HostObjectObjc]"]; 46 | return jsi::String::createFromUtf8(runtime, string.UTF8String); 47 | }; 48 | return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString); 49 | } 50 | 51 | if (name == "getSelector") { 52 | auto getSelector = [this] (jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { 53 | if(!arguments[0].isString()){ 54 | throw jsi::JSError(runtime, "TypeError: expected to be passed a String."); 55 | } 56 | RCTBridge *bridge = [RCTBridge currentBridge]; 57 | auto jsCallInvoker = bridge.jsCallInvoker; 58 | NSString *selectorName = convertJSIValueToObjCObject(runtime, arguments[0], jsCallInvoker); 59 | 60 | SEL sel = NSSelectorFromString(selectorName); 61 | if(!sel){ 62 | return jsi::Value::undefined(); 63 | } 64 | 65 | jsi::Object object = jsi::Object::createFromHostObject(runtime, std::make_unique(sel)); 66 | return object; 67 | }; 68 | return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "getSelector"), 1, getSelector); 69 | } 70 | 71 | // Cover all the type lookup utilities one-by-one! 72 | // @see https://developer.apple.com/documentation/foundation/object_runtime/objective-c_runtime_utilities?language=objc 73 | 74 | Class clazz = NSClassFromString(nameNSString); 75 | if (clazz != nil) { 76 | // TODO: read up on std::make_shared, std::make_unique, etc. and choose the best one 77 | jsi::Object object = jsi::Object::createFromHostObject(runtime, std::make_unique(clazz)); 78 | return object; 79 | } 80 | 81 | Protocol *protocol = NSProtocolFromString(nameNSString); 82 | if (protocol != nil) { 83 | jsi::Object object = jsi::Object::createFromHostObject(runtime, std::make_unique(protocol)); 84 | return object; 85 | } 86 | 87 | // We don't attempt implicit selector lookup because surprisingly the string @"NSStringTransformLatinToHiragana" 88 | // yields a selector (when we'd expect it to yield a variable instead; it's a global variable). 89 | 90 | void *value = dlsym(RTLD_MAIN_ONLY, [nameNSString cStringUsingEncoding:NSUTF8StringEncoding]); 91 | if (!value) { 92 | value = dlsym(RTLD_SELF, [nameNSString cStringUsingEncoding:NSUTF8StringEncoding]); 93 | } 94 | if (!value) { 95 | value = dlsym(RTLD_DEFAULT, [nameNSString cStringUsingEncoding:NSUTF8StringEncoding]); 96 | } 97 | if(!value) { 98 | throw jsi::JSError(runtime, [[NSString stringWithFormat:@"ReferenceError: Can't find symbol within this executable: %@", nameNSString] cStringUsingEncoding:NSUTF8StringEncoding]); 99 | } 100 | 101 | // dlsym() returns a pointer to the given data. 102 | // e.g. if we looked up `NSString* NSStringTransformToLatin`, it would return us not the NSString* directly, but a pointer to that NSString*. 103 | // Thus, we need to dereference it using this piece of witchcraft below. 104 | // @see https://stackoverflow.com/questions/23742392/how-do-i-cast-void-to-nsstring-without-getting-a-runtime-error-in-objective-c 105 | id valueObjc = *((__unsafe_unretained id*)value); 106 | 107 | // isKindOfClass checks whether valueObjc is an instance of any subclass of NSObject or NSObject itself. 108 | if(![valueObjc isKindOfClass:[NSObject class]]){ 109 | throw jsi::JSError(runtime, [[NSString stringWithFormat:@"TypeError: Did find the symbol named '%@', but it's not a type we can currently handle (expected a class instance).", nameNSString] cStringUsingEncoding:NSUTF8StringEncoding]); 110 | } 111 | 112 | return jsi::Object::createFromHostObject(runtime, std::make_shared(valueObjc)); 113 | } 114 | -------------------------------------------------------------------------------- /ios/HostObjectProtocol.h: -------------------------------------------------------------------------------- 1 | // Forked from: https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.h 2 | // See also: https://github.com/facebook/react-native/blob/1465c8f3874cdee8c325ab4a4916fda0b3e43bdb/Libraries/Blob/RCTBlobCollector.h 3 | 4 | #pragma once 5 | 6 | #import 7 | 8 | using namespace facebook; 9 | 10 | class JSI_EXPORT HostObjectProtocol: public jsi::HostObject { 11 | public: 12 | HostObjectProtocol(Protocol *protocol); 13 | ~HostObjectProtocol(); 14 | 15 | public: 16 | jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override; 17 | std::vector getPropertyNames(jsi::Runtime& rt) override; 18 | 19 | Protocol *protocol_; 20 | }; 21 | -------------------------------------------------------------------------------- /ios/HostObjectProtocol.mm: -------------------------------------------------------------------------------- 1 | // Forked from https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.mm 2 | 3 | #import 4 | #import 5 | #import 6 | #import "HostObjectProtocol.h" 7 | #import "JSIUtils.h" 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import "HostObjectUtils.h" 14 | 15 | HostObjectProtocol::HostObjectProtocol(Protocol *protocol) 16 | : protocol_(protocol) {} 17 | 18 | HostObjectProtocol::~HostObjectProtocol() {} 19 | 20 | std::vector HostObjectProtocol::getPropertyNames(jsi::Runtime& rt) { 21 | std::vector result; 22 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString"))); 23 | 24 | return result; 25 | } 26 | 27 | jsi::Value HostObjectProtocol::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) { 28 | auto name = propName.utf8(runtime); 29 | NSString *nameNSString = [NSString stringWithUTF8String:name.c_str()]; 30 | if([nameNSString isEqualToString:@"Symbol.toStringTag"]){ 31 | NSString *stringification = @"[object HostObjectProtocol]"; 32 | return jsi::String::createFromUtf8(runtime, stringification.UTF8String); 33 | } 34 | 35 | throw jsi::JSError(runtime, "HostObjectProtocol::get: Mostly unimplemented."); 36 | } 37 | -------------------------------------------------------------------------------- /ios/HostObjectSelector.h: -------------------------------------------------------------------------------- 1 | // Forked from: https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.h 2 | // See also: https://github.com/facebook/react-native/blob/1465c8f3874cdee8c325ab4a4916fda0b3e43bdb/Libraries/Blob/RCTBlobCollector.h 3 | 4 | #pragma once 5 | 6 | #import 7 | 8 | using namespace facebook; 9 | 10 | class JSI_EXPORT HostObjectSelector: public jsi::HostObject { 11 | public: 12 | HostObjectSelector(SEL sel); 13 | ~HostObjectSelector(); 14 | 15 | public: 16 | jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override; 17 | std::vector getPropertyNames(jsi::Runtime& rt) override; 18 | 19 | SEL sel_; 20 | }; 21 | -------------------------------------------------------------------------------- /ios/HostObjectSelector.mm: -------------------------------------------------------------------------------- 1 | // Forked from https://github.com/mrousavy/react-native-vision-camera/blob/b7bfa5ef0ad9a1c0add3d3508d7a4e0c65d2f6da/ios/Frame%20Processor/FrameHostObject.mm 2 | 3 | #import 4 | #import 5 | #import 6 | #import "HostObjectSelector.h" 7 | #import "JSIUtils.h" 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import "HostObjectUtils.h" 14 | 15 | HostObjectSelector::HostObjectSelector(SEL sel) 16 | : sel_(sel) {} 17 | 18 | HostObjectSelector::~HostObjectSelector() {} 19 | 20 | std::vector HostObjectSelector::getPropertyNames(jsi::Runtime& rt) { 21 | std::vector result; 22 | result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString"))); 23 | 24 | return result; 25 | } 26 | 27 | jsi::Value HostObjectSelector::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) { 28 | auto name = propName.utf8(runtime); 29 | NSString *nameNSString = [NSString stringWithUTF8String:name.c_str()]; 30 | if([nameNSString isEqualToString:@"Symbol.toStringTag"]){ 31 | NSString *stringification = @"[object HostObjectSelector]"; 32 | return jsi::String::createFromUtf8(runtime, stringification.UTF8String); 33 | } 34 | 35 | throw jsi::JSError(runtime, "HostObjectSelector::get: mostly unimplemented."); 36 | } 37 | -------------------------------------------------------------------------------- /ios/HostObjectUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | using namespace facebook; 8 | using namespace facebook::react; 9 | 10 | // Returns a jsi::Function that, when called, will: 11 | // - invoke a class method with the given selector on the given class; 12 | // - capture its return value; 13 | // - marshals it to a JSI Object (either a jsi::Value or a jsi::HostObject as appropriate). 14 | jsi::Function invokeClassMethod(jsi::Runtime &runtime, std::string methodName, SEL sel, Class clazz); 15 | 16 | // Returns a jsi::Function that, when called, will: 17 | // - invoke a class method with the given selector on the given class; 18 | // - capture its return value; 19 | // - marshal it to a JSI Object (either a jsi::Value or a jsi::HostObject as appropriate). 20 | jsi::Function invokeClassInstanceMethod(jsi::Runtime &runtime, std::string methodName, SEL sel, NSObject *instance); 21 | -------------------------------------------------------------------------------- /ios/HostObjectUtils.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #import "JSIUtils.h" 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import "HostObjectClass.h" 12 | #import "HostObjectClassInstance.h" 13 | 14 | using namespace facebook; 15 | using namespace facebook::react; 16 | // FIXME: upon several fast refreshes, I found that our NSString instance (held by HostObjectClassInstance) had become 17 | // nil at the time of calling 'stringByApplyingTransform:reverse:'. 18 | // This likely indicates that nothing's keeping a strong reference to it. 19 | jsi::Function invokeClassInstanceMethod(jsi::Runtime &runtime, std::string methodName, SEL sel, NSObject *instance) 20 | { 21 | Method method = class_getInstanceMethod([instance class], sel); 22 | if(!method){ 23 | throw jsi::JSError(runtime, "invokeClassInstanceMethod: class instance responded to selector, but the corresponding method was unable to be retrieved."); 24 | } 25 | char observedReturnType[256]; 26 | method_getReturnType(method, observedReturnType, 256); 27 | 28 | // TODO: refactor redundant code between invokeClassInstanceMethod and invokeClassMethod 29 | 30 | // Not sure yet how we'll handle varargs, but if it comes to it, we can change approach to enforce a single argument which is strictly an array. 31 | // arguments 0 and 1 are self and _cmd respectively (effectively not of our concern) 32 | unsigned int reservedArgs = 2; 33 | unsigned int argsCount = method_getNumberOfArguments(method) - reservedArgs; 34 | auto classMethod = [reservedArgs, instance, sel, observedReturnType] (jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { 35 | RCTBridge *bridge = [RCTBridge currentBridge]; 36 | auto jsCallInvoker = bridge.jsCallInvoker; 37 | // @see https://stackoverflow.com/questions/313400/nsinvocation-for-dummies 38 | NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[instance class] instanceMethodSignatureForSelector:sel]]; 39 | [inv setSelector:sel]; 40 | [inv setTarget:instance]; 41 | for(unsigned int i = 0; i < count; i++){ 42 | // @see https://github.com/mrousavy/react-native-vision-camera/blob/0f7ee51333c47fbfdf432c8608b9785f8eec3c94/ios/Frame%20Processor/FrameProcessorRuntimeManager.mm#L71 43 | if(arguments[i].isObject()){ 44 | jsi::Object obj = arguments[i].asObject(runtime); 45 | if(obj.isHostObject((runtime))){ 46 | if(HostObjectClass* hostObjectClass = dynamic_cast(obj.asHostObject(runtime).get())){ 47 | [inv setArgument:&hostObjectClass->clazz_ atIndex: reservedArgs + i]; 48 | } else if(HostObjectClassInstance* hostObjectClassInstance = dynamic_cast(obj.asHostObject(runtime).get())){ 49 | [inv setArgument:&hostObjectClassInstance->instance_ atIndex: reservedArgs + i]; 50 | } else { 51 | throw jsi::JSError(runtime, "invokeClassInstanceMethod: Unwrapping HostObjects other than ClassHostObject not yet supported!"); 52 | } 53 | } else { 54 | id objcArg = convertJSIValueToObjCObject(runtime, arguments[i], jsCallInvoker); 55 | [inv setArgument:&objcArg atIndex: reservedArgs + i]; 56 | } 57 | } else { 58 | id objcArg = convertJSIValueToObjCObject(runtime, arguments[i], jsCallInvoker); 59 | [inv setArgument:&objcArg atIndex: reservedArgs + i]; 60 | } 61 | } 62 | [inv invoke]; 63 | 64 | // @see https://developer.apple.com/documentation/foundation/nsmethodsignature 65 | const char *voidReturnType = "v"; 66 | if(0 == strncmp(observedReturnType, voidReturnType, strlen(voidReturnType))){ 67 | return jsi::Value::undefined(); 68 | } 69 | 70 | id returnValue = NULL; 71 | [inv getReturnValue:&returnValue]; 72 | 73 | // isKindOfClass checks whether the returnValue is an instance of any subclass of NSObject or NSObject itself. 74 | // There is also isMemberOfClass if we ever want to check whether it is an instance of NSObject (not a subclass). 75 | if([returnValue isKindOfClass:[NSObject class]]){ 76 | return jsi::Object::createFromHostObject(runtime, std::make_shared(returnValue)); 77 | } 78 | 79 | // If we get blocked by "Did you forget to nest alloc and init?", we may be restricted to [NSString new]. 80 | 81 | // Boy is this unsafe..! 82 | return convertObjCObjectToJSIValue(runtime, returnValue); 83 | }; 84 | return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, methodName), argsCount, classMethod); 85 | } 86 | 87 | jsi::Function invokeClassMethod(jsi::Runtime &runtime, std::string methodName, SEL sel, Class clazz) 88 | { 89 | Method method = class_getClassMethod(clazz, sel); 90 | if(!method){ 91 | throw jsi::JSError(runtime, "invokeClassMethod: class responded to selector, but the corresponding method was unable to be retrieved. Perhaps it's an instance method? On classes, you can only call class methods."); 92 | } 93 | 94 | // Not sure yet how we'll handle varargs, but if it comes to it, we can change approach to enforce a single argument which is strictly an array. 95 | // arguments 0 and 1 are self and _cmd respectively (effectively not of our concern) 96 | unsigned int reservedArgs = 2; 97 | unsigned int argsCount = method_getNumberOfArguments(method) - reservedArgs; 98 | auto classMethod = [reservedArgs, clazz, sel] (jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value { 99 | RCTBridge *bridge = [RCTBridge currentBridge]; 100 | auto jsCallInvoker = bridge.jsCallInvoker; 101 | // @see https://jayeshkawli.ghost.io/nsinvocation-in-ios/ 102 | // @see https://stackoverflow.com/questions/8439052/ios-how-to-implement-a-performselector-with-multiple-arguments-and-with-afterd 103 | NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[clazz methodSignatureForSelector:sel]]; 104 | [inv setSelector:sel]; 105 | [inv setTarget:clazz]; 106 | for(unsigned int i = 0; i < count; i++){ 107 | // @see https://github.com/mrousavy/react-native-vision-camera/blob/0f7ee51333c47fbfdf432c8608b9785f8eec3c94/ios/Frame%20Processor/FrameProcessorRuntimeManager.mm#L71 108 | if(arguments[i].isObject()){ 109 | jsi::Object obj = arguments[i].asObject(runtime); 110 | if(obj.isHostObject((runtime))){ 111 | if(HostObjectClass* hostObjectClass = dynamic_cast(obj.asHostObject(runtime).get())){ 112 | [inv setArgument:&hostObjectClass->clazz_ atIndex: reservedArgs + i]; 113 | } else if(HostObjectClassInstance* hostObjectClassInstance = dynamic_cast(obj.asHostObject(runtime).get())){ 114 | [inv setArgument:&hostObjectClassInstance->instance_ atIndex: reservedArgs + i]; 115 | } else { 116 | throw jsi::JSError(runtime, "invokeClassMethod: Unwrapping HostObjects other than HostObjectClass not yet supported!"); 117 | } 118 | } else { 119 | id objcArg = convertJSIValueToObjCObject(runtime, arguments[i], jsCallInvoker); 120 | [inv setArgument:&objcArg atIndex: reservedArgs + i]; 121 | } 122 | } else { 123 | id objcArg = convertJSIValueToObjCObject(runtime, arguments[i], jsCallInvoker); 124 | [inv setArgument:&objcArg atIndex: reservedArgs + i]; 125 | } 126 | } 127 | [inv invoke]; 128 | id returnValue = NULL; 129 | [inv getReturnValue:&returnValue]; 130 | 131 | // isKindOfClass checks whether the returnValue is an instance of any subclass of NSObject or NSObject itself. 132 | // There is also isMemberOfClass if we ever want to check whether it is an instance of NSObject (not a subclass). 133 | if([returnValue isKindOfClass:[NSObject class]]){ 134 | return jsi::Object::createFromHostObject(runtime, std::make_shared(returnValue)); 135 | } 136 | 137 | // If we get blocked by "Did you forget to nest alloc and init?", we may be restricted to [NSString new]. 138 | 139 | // Boy is this unsafe..! 140 | return convertObjCObjectToJSIValue(runtime, returnValue); 141 | }; 142 | return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, methodName), argsCount, classMethod); 143 | } 144 | -------------------------------------------------------------------------------- /ios/JSIUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSIUtils.h 3 | // VisionCamera 4 | // 5 | // Forked by Jamie Birch on 29.09.21. 6 | // Created by Marc Rousavy on 30.04.21. 7 | // Copyright © 2021 mrousavy. All rights reserved. 8 | // Forked from: https://github.com/mrousavy/react-native-vision-camera/blob/71730a73ef5670e45e34185a13a260a374a96dcd/ios/React%20Utils/JSIUtils.h 9 | // 10 | 11 | #pragma once 12 | 13 | #import 14 | #import 15 | #import 16 | 17 | using namespace facebook; 18 | using namespace facebook::react; 19 | 20 | // NSNumber -> boolean 21 | jsi::Value convertNSNumberToJSIBoolean(jsi::Runtime& runtime, NSNumber* value); 22 | 23 | // NSNumber -> number 24 | jsi::Value convertNSNumberToJSINumber(jsi::Runtime& runtime, NSNumber* value); 25 | 26 | // NSNumber -> string 27 | jsi::String convertNSStringToJSIString(jsi::Runtime& runtime, NSString* value); 28 | 29 | // NSDictionary -> {} 30 | jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime& runtime, NSDictionary* value); 31 | 32 | // NSArray -> [] 33 | jsi::Array convertNSArrayToJSIArray(jsi::Runtime& runtime, NSArray* value); 34 | 35 | // id -> ??? 36 | jsi::Value convertObjCObjectToJSIValue(jsi::Runtime& runtime, id value); 37 | 38 | // string -> NSString 39 | NSString* convertJSIStringToNSString(jsi::Runtime& runtime, const jsi::String& value); 40 | 41 | // any... -> NSArray 42 | NSArray* convertJSICStyleArrayToNSArray(jsi::Runtime& runtime, const jsi::Value* array, size_t length, std::shared_ptr jsInvoker); 43 | 44 | // NSArray -> any... 45 | jsi::Value* convertNSArrayToJSICStyleArray(jsi::Runtime& runtime, NSArray* array); 46 | 47 | // [] -> NSArray 48 | NSArray* convertJSIArrayToNSArray(jsi::Runtime& runtime, const jsi::Array& value, std::shared_ptr jsInvoker); 49 | 50 | // {} -> NSDictionary 51 | NSDictionary* convertJSIObjectToNSDictionary(jsi::Runtime& runtime, const jsi::Object& value, std::shared_ptr jsInvoker); 52 | 53 | // any -> id 54 | id convertJSIValueToObjCObject(jsi::Runtime& runtime, const jsi::Value& value, std::shared_ptr jsInvoker); 55 | 56 | // (any...) => any -> (void)(id, id) 57 | RCTResponseSenderBlock convertJSIFunctionToCallback(jsi::Runtime& runtime, const jsi::Function& value, std::shared_ptr jsInvoker); 58 | -------------------------------------------------------------------------------- /ios/JSIUtils.mm: -------------------------------------------------------------------------------- 1 | // 2 | // JSIUtils.mm 3 | // VisionCamera 4 | // 5 | // Forked and Adjusted by Jamie Birch on 29.09.21. 6 | // Forked and Adjusted by Marc Rousavy on 02.05.21. 7 | // Copyright © 2021 Jamie Birch, mrousavy, and Facebook. All rights reserved. 8 | // 9 | // Forked and adjusted from: https://github.com/mrousavy/react-native-vision-camera/blob/5dc8ded62a4cb25971006287b7c634ca0981c5a2/ios/React%20Utils/JSIUtils.mm 10 | // ... which itself was forked and adjusted from: https://github.com/facebook/react-native/blob/900210cacc4abca0079e3903781bc223c80c8ac7/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm 11 | // Original Copyright Notice: 12 | // 13 | // Copyright (c) Facebook, Inc. and its affiliates. 14 | // 15 | // This source code is licensed under the MIT license found in the 16 | // LICENSE file in the root directory of this source tree. 17 | // 18 | 19 | #import "JSIUtils.h" 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | 26 | using namespace facebook; 27 | using namespace facebook::react; 28 | 29 | jsi::Value convertNSNumberToJSIBoolean(jsi::Runtime &runtime, NSNumber *value) 30 | { 31 | return jsi::Value((bool)[value boolValue]); 32 | } 33 | 34 | jsi::Value convertNSNumberToJSINumber(jsi::Runtime &runtime, NSNumber *value) 35 | { 36 | return jsi::Value([value doubleValue]); 37 | } 38 | 39 | jsi::String convertNSStringToJSIString(jsi::Runtime &runtime, NSString *value) 40 | { 41 | return jsi::String::createFromUtf8(runtime, [value UTF8String] ?: ""); 42 | } 43 | 44 | jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDictionary *value) 45 | { 46 | jsi::Object result = jsi::Object(runtime); 47 | for (NSString *k in value) { 48 | result.setProperty(runtime, [k UTF8String], convertObjCObjectToJSIValue(runtime, value[k])); 49 | } 50 | return result; 51 | } 52 | 53 | jsi::Array convertNSArrayToJSIArray(jsi::Runtime &runtime, NSArray *value) 54 | { 55 | jsi::Array result = jsi::Array(runtime, value.count); 56 | for (size_t i = 0; i < value.count; i++) { 57 | result.setValueAtIndex(runtime, i, convertObjCObjectToJSIValue(runtime, value[i])); 58 | } 59 | return result; 60 | } 61 | 62 | jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value) 63 | { 64 | if (value == nil) { 65 | return jsi::Value::undefined(); 66 | } else if ([value isKindOfClass:[NSString class]]) { 67 | return convertNSStringToJSIString(runtime, (NSString *)value); 68 | } else if ([value isKindOfClass:[NSNumber class]]) { 69 | if ([value isKindOfClass:[@YES class]]) { 70 | return convertNSNumberToJSIBoolean(runtime, (NSNumber *)value); 71 | } 72 | return convertNSNumberToJSINumber(runtime, (NSNumber *)value); 73 | } else if ([value isKindOfClass:[NSDictionary class]]) { 74 | return convertNSDictionaryToJSIObject(runtime, (NSDictionary *)value); 75 | } else if ([value isKindOfClass:[NSArray class]]) { 76 | return convertNSArrayToJSIArray(runtime, (NSArray *)value); 77 | } else if (value == (id)kCFNull) { 78 | return jsi::Value::null(); 79 | } 80 | return jsi::Value::undefined(); 81 | } 82 | 83 | NSString *convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::String &value) 84 | { 85 | return [NSString stringWithUTF8String:value.utf8(runtime).c_str()]; 86 | } 87 | 88 | NSArray* convertJSICStyleArrayToNSArray(jsi::Runtime &runtime, const jsi::Value* array, size_t length, std::shared_ptr jsInvoker) { 89 | if (length < 1) return @[]; 90 | NSMutableArray *result = [NSMutableArray new]; 91 | for (size_t i = 0; i < length; i++) { 92 | // Insert kCFNull when it's `undefined` value to preserve the indices. 93 | [result 94 | addObject:convertJSIValueToObjCObject(runtime, array[i], jsInvoker) ?: (id)kCFNull]; 95 | } 96 | return [result copy]; 97 | } 98 | 99 | jsi::Value* convertNSArrayToJSICStyleArray(jsi::Runtime &runtime, NSArray* array) { 100 | auto result = new jsi::Value[array.count]; 101 | for (size_t i = 0; i < array.count; i++) { 102 | result[i] = convertObjCObjectToJSIValue(runtime, array[i]); 103 | } 104 | return result; 105 | } 106 | 107 | NSArray* convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr jsInvoker) 108 | { 109 | size_t size = value.size(runtime); 110 | NSMutableArray *result = [NSMutableArray new]; 111 | for (size_t i = 0; i < size; i++) { 112 | // Insert kCFNull when it's `undefined` value to preserve the indices. 113 | [result 114 | addObject:convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker) ?: (id)kCFNull]; 115 | } 116 | return [result copy]; 117 | } 118 | 119 | NSDictionary* convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value, std::shared_ptr jsInvoker) 120 | { 121 | jsi::Array propertyNames = value.getPropertyNames(runtime); 122 | size_t size = propertyNames.size(runtime); 123 | NSMutableDictionary *result = [NSMutableDictionary new]; 124 | for (size_t i = 0; i < size; i++) { 125 | jsi::String name = propertyNames.getValueAtIndex(runtime, i).getString(runtime); 126 | NSString *k = convertJSIStringToNSString(runtime, name); 127 | id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker); 128 | if (v) { 129 | result[k] = v; 130 | } 131 | } 132 | return [result copy]; 133 | } 134 | 135 | id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr jsInvoker) 136 | { 137 | if (value.isUndefined() || value.isNull()) { 138 | return nil; 139 | } 140 | if (value.isBool()) { 141 | return @(value.getBool()); 142 | } 143 | if (value.isNumber()) { 144 | return @(value.getNumber()); 145 | } 146 | if (value.isString()) { 147 | return convertJSIStringToNSString(runtime, value.getString(runtime)); 148 | } 149 | if (value.isObject()) { 150 | jsi::Object o = value.getObject(runtime); 151 | if (o.isArray(runtime)) { 152 | return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker); 153 | } 154 | if (o.isFunction(runtime)) { 155 | return convertJSIFunctionToCallback(runtime, std::move(o.getFunction(runtime)), jsInvoker); 156 | } 157 | return convertJSIObjectToNSDictionary(runtime, o, jsInvoker); 158 | } 159 | 160 | throw std::runtime_error("Unsupported jsi::jsi::Value kind"); 161 | } 162 | 163 | RCTResponseSenderBlock convertJSIFunctionToCallback(jsi::Runtime &runtime, const jsi::Function &value, std::shared_ptr jsInvoker) 164 | { 165 | auto weakWrapper = CallbackWrapper::createWeak(value.getFunction(runtime), runtime, jsInvoker); 166 | BOOL __block wrapperWasCalled = NO; 167 | RCTResponseSenderBlock callback = ^(NSArray *responses) { 168 | if (wrapperWasCalled) { 169 | throw std::runtime_error("callback arg cannot be called more than once"); 170 | } 171 | 172 | auto strongWrapper = weakWrapper.lock(); 173 | if (!strongWrapper) { 174 | return; 175 | } 176 | 177 | strongWrapper->jsInvoker().invokeAsync([weakWrapper, responses]() { 178 | auto strongWrapper2 = weakWrapper.lock(); 179 | if (!strongWrapper2) { 180 | return; 181 | } 182 | 183 | const jsi::Value* args = convertNSArrayToJSICStyleArray(strongWrapper2->runtime(), responses); 184 | strongWrapper2->callback().call(strongWrapper2->runtime(), args, static_cast(responses.count)); 185 | strongWrapper2->destroy(); 186 | delete[] args; 187 | }); 188 | 189 | wrapperWasCalled = YES; 190 | }; 191 | 192 | return [callback copy]; 193 | } 194 | -------------------------------------------------------------------------------- /ios/ObjcRuntime.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ObjcRuntime : NSObject 4 | @property (nonatomic, assign) BOOL setBridgeOnMainQueue; 5 | @end 6 | -------------------------------------------------------------------------------- /ios/ObjcRuntime.mm: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "ObjcRuntime.h" 4 | #import "react-native-objc-runtime.h" 5 | 6 | // This is the entrypoint file for our native module. 7 | // It will be called by React Native as it initialises its native modules. 8 | 9 | @implementation ObjcRuntime 10 | 11 | @synthesize bridge=_bridge; 12 | 13 | + (BOOL)requiresMainQueueSetup { 14 | return YES; 15 | } 16 | 17 | - (void)setBridge:(RCTBridge *)bridge { 18 | _bridge = bridge; 19 | _setBridgeOnMainQueue = RCTIsMainQueue(); 20 | 21 | RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge; 22 | if (!cxxBridge.runtime) { 23 | return; 24 | } 25 | 26 | // Install the `objc` HostObject into the JS runtime in global scope. 27 | ObjcRuntimeJsi::install(*(facebook::jsi::Runtime *)cxxBridge.runtime); 28 | } 29 | 30 | - (void)invalidate { 31 | if(!self.bridge){ 32 | return; 33 | } 34 | RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge; 35 | if (!cxxBridge.runtime) { 36 | return; 37 | } 38 | 39 | // Overwrite the `objc` HostObject as undefined (my speculative way of cleaning it up). 40 | ObjcRuntimeJsi::uninstall(*(facebook::jsi::Runtime *)cxxBridge.runtime); 41 | } 42 | 43 | - (void)dealloc 44 | { 45 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 46 | } 47 | 48 | RCT_EXPORT_MODULE() 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /ios/ObjcRuntime.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 11 | 5E46D8CD2428F78900513E24 /* example.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E46D8CB2428F78900513E24 /* example.cpp */; }; 12 | 5E555C0D2413F4C50049A1A2 /* ObjcRuntime.mm in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* ObjcRuntime.mm */; }; 13 | 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | 58B511D91A9E6C8500147676 /* CopyFiles */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = "include/$(PRODUCT_NAME)"; 21 | dstSubfolderSpec = 16; 22 | files = ( 23 | ); 24 | runOnlyForDeploymentPostprocessing = 0; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 134814201AA4EA6300B7C361 /* libObjcRuntime.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libObjcRuntime.a; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 31 | 5E46D8CB2428F78900513E24 /* example.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = example.cpp; path = ../cpp/example.cpp; sourceTree = ""; }; 32 | 5E46D8CC2428F78900513E24 /* example.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = example.h; path = ../cpp/example.h; sourceTree = ""; }; 33 | B3E7B5891CC2AC0600A0062D /* ObjcRuntime.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ObjcRuntime.mm; sourceTree = ""; }; 34 | 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | 134814211AA4EA7D00B7C361 /* Products */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 134814201AA4EA6300B7C361 /* libObjcRuntime.a */, 52 | ); 53 | name = Products; 54 | sourceTree = ""; 55 | }; 56 | 58B511D21A9E6C8500147676 = { 57 | isa = PBXGroup; 58 | children = ( 59 | 60 | 5E46D8CB2428F78900513E24 /* example.cpp */, 61 | 5E46D8CC2428F78900513E24 /* example.h */, 62 | B3E7B5891CC2AC0600A0062D /* ObjcRuntime.mm */, 63 | 64 | 134814211AA4EA7D00B7C361 /* Products */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | /* End PBXGroup section */ 69 | 70 | /* Begin PBXNativeTarget section */ 71 | 58B511DA1A9E6C8500147676 /* ObjcRuntime */ = { 72 | isa = PBXNativeTarget; 73 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "ObjcRuntime" */; 74 | buildPhases = ( 75 | 58B511D71A9E6C8500147676 /* Sources */, 76 | 58B511D81A9E6C8500147676 /* Frameworks */, 77 | 58B511D91A9E6C8500147676 /* CopyFiles */, 78 | ); 79 | buildRules = ( 80 | ); 81 | dependencies = ( 82 | ); 83 | name = ObjcRuntime; 84 | productName = RCTDataManager; 85 | productReference = 134814201AA4EA6300B7C361 /* libObjcRuntime.a */; 86 | productType = "com.apple.product-type.library.static"; 87 | }; 88 | /* End PBXNativeTarget section */ 89 | 90 | /* Begin PBXProject section */ 91 | 58B511D31A9E6C8500147676 /* Project object */ = { 92 | isa = PBXProject; 93 | attributes = { 94 | LastUpgradeCheck = 0920; 95 | ORGANIZATIONNAME = Facebook; 96 | TargetAttributes = { 97 | 58B511DA1A9E6C8500147676 = { 98 | CreatedOnToolsVersion = 6.1.1; 99 | }; 100 | }; 101 | }; 102 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "ObjcRuntime" */; 103 | compatibilityVersion = "Xcode 3.2"; 104 | developmentRegion = English; 105 | hasScannedForEncodings = 0; 106 | knownRegions = ( 107 | English, 108 | en, 109 | ); 110 | mainGroup = 58B511D21A9E6C8500147676; 111 | productRefGroup = 58B511D21A9E6C8500147676; 112 | projectDirPath = ""; 113 | projectRoot = ""; 114 | targets = ( 115 | 58B511DA1A9E6C8500147676 /* ObjcRuntime */, 116 | ); 117 | }; 118 | /* End PBXProject section */ 119 | 120 | /* Begin PBXSourcesBuildPhase section */ 121 | 58B511D71A9E6C8500147676 /* Sources */ = { 122 | isa = PBXSourcesBuildPhase; 123 | buildActionMask = 2147483647; 124 | files = ( 125 | 126 | 5E46D8CD2428F78900513E24 /* example.cpp in Sources */, 127 | 5E555C0D2413F4C50049A1A2 /* ObjcRuntime.mm in Sources */, 128 | 129 | ); 130 | runOnlyForDeploymentPostprocessing = 0; 131 | }; 132 | /* End PBXSourcesBuildPhase section */ 133 | 134 | /* Begin XCBuildConfiguration section */ 135 | 58B511ED1A9E6C8500147676 /* Debug */ = { 136 | isa = XCBuildConfiguration; 137 | buildSettings = { 138 | ALWAYS_SEARCH_USER_PATHS = NO; 139 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 140 | CLANG_CXX_LIBRARY = "libc++"; 141 | CLANG_ENABLE_MODULES = YES; 142 | CLANG_ENABLE_OBJC_ARC = YES; 143 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 144 | CLANG_WARN_BOOL_CONVERSION = YES; 145 | CLANG_WARN_COMMA = YES; 146 | CLANG_WARN_CONSTANT_CONVERSION = YES; 147 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 148 | CLANG_WARN_EMPTY_BODY = YES; 149 | CLANG_WARN_ENUM_CONVERSION = YES; 150 | CLANG_WARN_INFINITE_RECURSION = YES; 151 | CLANG_WARN_INT_CONVERSION = YES; 152 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 153 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 154 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 155 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 156 | CLANG_WARN_STRICT_PROTOTYPES = YES; 157 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 158 | CLANG_WARN_UNREACHABLE_CODE = YES; 159 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 160 | COPY_PHASE_STRIP = NO; 161 | ENABLE_STRICT_OBJC_MSGSEND = YES; 162 | ENABLE_TESTABILITY = YES; 163 | GCC_C_LANGUAGE_STANDARD = gnu99; 164 | GCC_DYNAMIC_NO_PIC = NO; 165 | GCC_NO_COMMON_BLOCKS = YES; 166 | GCC_OPTIMIZATION_LEVEL = 0; 167 | GCC_PREPROCESSOR_DEFINITIONS = ( 168 | "DEBUG=1", 169 | "$(inherited)", 170 | ); 171 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 172 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 173 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 174 | GCC_WARN_UNDECLARED_SELECTOR = YES; 175 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 176 | GCC_WARN_UNUSED_FUNCTION = YES; 177 | GCC_WARN_UNUSED_VARIABLE = YES; 178 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 179 | MTL_ENABLE_DEBUG_INFO = YES; 180 | ONLY_ACTIVE_ARCH = YES; 181 | SDKROOT = iphoneos; 182 | }; 183 | name = Debug; 184 | }; 185 | 58B511EE1A9E6C8500147676 /* Release */ = { 186 | isa = XCBuildConfiguration; 187 | buildSettings = { 188 | ALWAYS_SEARCH_USER_PATHS = NO; 189 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 190 | CLANG_CXX_LIBRARY = "libc++"; 191 | CLANG_ENABLE_MODULES = YES; 192 | CLANG_ENABLE_OBJC_ARC = YES; 193 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 194 | CLANG_WARN_BOOL_CONVERSION = YES; 195 | CLANG_WARN_COMMA = YES; 196 | CLANG_WARN_CONSTANT_CONVERSION = YES; 197 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 198 | CLANG_WARN_EMPTY_BODY = YES; 199 | CLANG_WARN_ENUM_CONVERSION = YES; 200 | CLANG_WARN_INFINITE_RECURSION = YES; 201 | CLANG_WARN_INT_CONVERSION = YES; 202 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 203 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 204 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 205 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 206 | CLANG_WARN_STRICT_PROTOTYPES = YES; 207 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 208 | CLANG_WARN_UNREACHABLE_CODE = YES; 209 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 210 | COPY_PHASE_STRIP = YES; 211 | ENABLE_NS_ASSERTIONS = NO; 212 | ENABLE_STRICT_OBJC_MSGSEND = YES; 213 | GCC_C_LANGUAGE_STANDARD = gnu99; 214 | GCC_NO_COMMON_BLOCKS = YES; 215 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 216 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 217 | GCC_WARN_UNDECLARED_SELECTOR = YES; 218 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 219 | GCC_WARN_UNUSED_FUNCTION = YES; 220 | GCC_WARN_UNUSED_VARIABLE = YES; 221 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 222 | MTL_ENABLE_DEBUG_INFO = NO; 223 | SDKROOT = iphoneos; 224 | VALIDATE_PRODUCT = YES; 225 | }; 226 | name = Release; 227 | }; 228 | 58B511F01A9E6C8500147676 /* Debug */ = { 229 | isa = XCBuildConfiguration; 230 | buildSettings = { 231 | HEADER_SEARCH_PATHS = ( 232 | "$(inherited)", 233 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 234 | "$(SRCROOT)/../../../React/**", 235 | "$(SRCROOT)/../../react-native/React/**", 236 | ); 237 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 238 | OTHER_LDFLAGS = "-ObjC"; 239 | PRODUCT_NAME = ObjcRuntime; 240 | SKIP_INSTALL = YES; 241 | 242 | }; 243 | name = Debug; 244 | }; 245 | 58B511F11A9E6C8500147676 /* Release */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | HEADER_SEARCH_PATHS = ( 249 | "$(inherited)", 250 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 251 | "$(SRCROOT)/../../../React/**", 252 | "$(SRCROOT)/../../react-native/React/**", 253 | ); 254 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 255 | OTHER_LDFLAGS = "-ObjC"; 256 | PRODUCT_NAME = ObjcRuntime; 257 | SKIP_INSTALL = YES; 258 | 259 | }; 260 | name = Release; 261 | }; 262 | /* End XCBuildConfiguration section */ 263 | 264 | /* Begin XCConfigurationList section */ 265 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "ObjcRuntime" */ = { 266 | isa = XCConfigurationList; 267 | buildConfigurations = ( 268 | 58B511ED1A9E6C8500147676 /* Debug */, 269 | 58B511EE1A9E6C8500147676 /* Release */, 270 | ); 271 | defaultConfigurationIsVisible = 0; 272 | defaultConfigurationName = Release; 273 | }; 274 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "ObjcRuntime" */ = { 275 | isa = XCConfigurationList; 276 | buildConfigurations = ( 277 | 58B511F01A9E6C8500147676 /* Debug */, 278 | 58B511F11A9E6C8500147676 /* Release */, 279 | ); 280 | defaultConfigurationIsVisible = 0; 281 | defaultConfigurationName = Release; 282 | }; 283 | /* End XCConfigurationList section */ 284 | }; 285 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 286 | } 287 | -------------------------------------------------------------------------------- /ios/react-native-objc-runtime.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJC_RUNTIME_H 2 | #define OBJC_RUNTIME_H 3 | 4 | #include 5 | #include 6 | 7 | namespace ObjcRuntimeJsi { 8 | 9 | void install(facebook::jsi::Runtime &jsiRuntime); 10 | void uninstall(facebook::jsi::Runtime &jsiRuntime); 11 | 12 | } // namespace example 13 | 14 | #endif /* OBJC_RUNTIME_H */ 15 | -------------------------------------------------------------------------------- /ios/react-native-objc-runtime.mm: -------------------------------------------------------------------------------- 1 | #import "react-native-objc-runtime.h" 2 | #import "HostObjectObjc.h" 3 | #import "JSIUtils.h" 4 | #import 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | using namespace facebook; 12 | 13 | namespace ObjcRuntimeJsi { 14 | void install(jsi::Runtime& jsiRuntime) { 15 | std::cout << "Initialising Obj-C JSI" << "\n"; 16 | 17 | jsi::Object object = jsi::Object::createFromHostObject(jsiRuntime, std::make_shared()); 18 | jsiRuntime.global().setProperty(jsiRuntime, "objc", std::move(object)); 19 | } 20 | 21 | void uninstall(jsi::Runtime& jsiRuntime) { 22 | // We seemingly can't remove the property altogether, but let's at least try to set it to undefined. 23 | jsiRuntime.global().setProperty(jsiRuntime, "objc", jsi::Value::undefined()); 24 | } 25 | } // namespace ObjcRuntime 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-native-runtime", 3 | "version": "0.2.0", 4 | "description": "Access the Obj-C runtime from React Native via JSI!", 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 | "scripts/generate-objc-constants.js", 17 | "react-native-objc-runtime.podspec", 18 | "!lib/typescript/example", 19 | "!android/build", 20 | "!ios/build", 21 | "!**/__tests__", 22 | "!**/__fixtures__", 23 | "!**/__mocks__" 24 | ], 25 | "scripts": { 26 | "test": "jest", 27 | "typescript": "tsc --noEmit", 28 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 29 | "prepare": "bob build", 30 | "release": "release-it", 31 | "example": "yarn --cwd example", 32 | "pods": "cd example && pod-install --quiet", 33 | "bootstrap": "yarn example && yarn && yarn pods" 34 | }, 35 | "keywords": [ 36 | "react-native", 37 | "ios", 38 | "android" 39 | ], 40 | "repository": "https://github.com/shirakaba/react-native-objc-runtime", 41 | "author": "Jamie Birch <14055146+shirakaba@users.noreply.github.com> (https://github.com/shirakaba)", 42 | "license": "MIT", 43 | "bugs": { 44 | "url": "https://github.com/shirakaba/react-native-objc-runtime/issues" 45 | }, 46 | "homepage": "https://github.com/shirakaba/react-native-objc-runtime#readme", 47 | "publishConfig": { 48 | "registry": "https://registry.npmjs.org/" 49 | }, 50 | "devDependencies": { 51 | "@commitlint/config-conventional": "^11.0.0", 52 | "@react-native-community/eslint-config": "^2.0.0", 53 | "@release-it/conventional-changelog": "^2.0.0", 54 | "@types/jest": "^26.0.0", 55 | "@types/js-yaml": "^4.0.3", 56 | "@types/react": "^16.9.19", 57 | "@types/react-native": "0.64.13", 58 | "commitlint": "^11.0.0", 59 | "eslint": "^7.14.0", 60 | "eslint-config-prettier": "^7.0.0", 61 | "eslint-plugin-prettier": "^3.1.3", 62 | "husky": "^4.2.5", 63 | "jest": "^26.6.3", 64 | "pod-install": "^0.1.0", 65 | "prettier": "^2.0.5", 66 | "react": "^17.0.1", 67 | "react-native": "^0.64.2", 68 | "react-native-builder-bob": "^0.18.0", 69 | "release-it": "^14.2.2", 70 | "typescript": "^4.1.3" 71 | }, 72 | "peerDependencies": { 73 | "react": "*", 74 | "react-native": "*" 75 | }, 76 | "jest": { 77 | "preset": "react-native", 78 | "modulePathIgnorePatterns": [ 79 | "/example/node_modules", 80 | "/lib/" 81 | ] 82 | }, 83 | "commitlint": { 84 | "extends": [ 85 | "@commitlint/config-conventional" 86 | ] 87 | }, 88 | "release-it": { 89 | "git": { 90 | "commitMessage": "chore: release ${version}", 91 | "tagName": "v${version}" 92 | }, 93 | "npm": { 94 | "publish": true 95 | }, 96 | "github": { 97 | "release": true 98 | }, 99 | "plugins": { 100 | "@release-it/conventional-changelog": { 101 | "preset": "angular" 102 | } 103 | } 104 | }, 105 | "eslintConfig": { 106 | "root": true, 107 | "extends": [ 108 | "@react-native-community", 109 | "prettier" 110 | ], 111 | "rules": { 112 | "prettier/prettier": [ 113 | "error", 114 | { 115 | "quoteProps": "consistent", 116 | "singleQuote": true, 117 | "tabWidth": 2, 118 | "trailingComma": "es5", 119 | "useTabs": false 120 | } 121 | ] 122 | } 123 | }, 124 | "eslintIgnore": [ 125 | "node_modules/", 126 | "lib/" 127 | ], 128 | "prettier": { 129 | "quoteProps": "consistent", 130 | "singleQuote": true, 131 | "tabWidth": 2, 132 | "trailingComma": "es5", 133 | "useTabs": false 134 | }, 135 | "react-native-builder-bob": { 136 | "source": "src", 137 | "output": "lib", 138 | "targets": [ 139 | "commonjs", 140 | "module", 141 | [ 142 | "typescript", 143 | { 144 | "project": "tsconfig.build.json" 145 | } 146 | ] 147 | ] 148 | }, 149 | "dependencies": { 150 | "js-yaml": "^4.1.0", 151 | "yargs": "^17.2.1" 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /react-native-objc-runtime.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-objc-runtime" 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 => "10.0", :osx => "10.13", :tvos => "9.2" } 14 | s.source = { :git => "https://github.com/shirakaba/react-native-objc-runtime.git", :tag => "#{s.version}" } 15 | 16 | s.source_files = "ios/**/*.{h,m,mm}", "cpp/**/*.{h,cpp,mm}" 17 | 18 | # @see https://github.com/mrousavy/react-native-vision-camera/blob/main/VisionCamera.podspec 19 | s.pod_target_xcconfig = { 20 | # > If you are trying to instantiate a class from a static library, you must add the "-ObjC" 21 | # > flag to the "Other Linker Flags" build setting. 22 | # @see https://stackoverflow.com/questions/2227085/nsclassfromstring-returns-nil 23 | "OTHER_LDFLAGS" => ["$(inherited)", "-ObjC", "-lc++"], 24 | "DEFINES_MODULE" => "YES", 25 | "USE_HEADERMAP" => "YES", 26 | "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_TARGET_SRCROOT)\" \"$(PODS_ROOT)/Headers/Private/React-Core\"" 27 | } 28 | s.requires_arc = true 29 | 30 | s.dependency "React-callinvoker" 31 | s.dependency "React" 32 | s.dependency "React-Core" 33 | end 34 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const path = require('path'); 3 | const child_process = require('child_process'); 4 | 5 | const root = path.resolve(__dirname, '..'); 6 | const args = process.argv.slice(2); 7 | const options = { 8 | cwd: process.cwd(), 9 | env: process.env, 10 | stdio: 'inherit', 11 | encoding: 'utf-8', 12 | }; 13 | 14 | if (os.type() === 'Windows_NT') { 15 | options.shell = true 16 | } 17 | 18 | let result; 19 | 20 | if (process.cwd() !== root || args.length) { 21 | // We're not in the root of the project, or additional arguments were passed 22 | // In this case, forward the command to `yarn` 23 | result = child_process.spawnSync('yarn', args, options); 24 | } else { 25 | // If `yarn` is run without arguments, perform bootstrap 26 | result = child_process.spawnSync('yarn', ['bootstrap'], options); 27 | } 28 | 29 | process.exitCode = result.status; 30 | -------------------------------------------------------------------------------- /src/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | it.todo('write a test'); 2 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import { NativeModules, Platform } from 'react-native'; 2 | 3 | const LINKING_ERROR = 4 | `The package 'react-native-objc-runtime' doesn't seem to be linked. Make sure: \n\n` + 5 | Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) + 6 | '- You rebuilt the app after installing the package\n' + 7 | "- You are not running this in the Expo Go app (it doesn't include this module's native code)\n"; 8 | 9 | const ObjcRuntime = NativeModules.ObjcRuntime 10 | ? NativeModules.ObjcRuntime 11 | : new Proxy( 12 | {}, 13 | { 14 | get() { 15 | throw new Error(LINKING_ERROR); 16 | }, 17 | } 18 | ); 19 | 20 | export function multiply(a: number, b: number): Promise { 21 | return ObjcRuntime.multiply(a, b); 22 | } 23 | -------------------------------------------------------------------------------- /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-objc-runtime": ["./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 | } 27 | } 28 | --------------------------------------------------------------------------------