• Stars
    star
    239
  • Rank 167,853 (Top 4 %)
  • Language Objective-C++
  • License
    Other
  • Created almost 3 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The Objective-C runtime exposed directly to React Native via JSI (Java runtime may follow)!

react-native-native-runtime

npm version

Access the native APIs directly from the React Native JS context!

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.

Installation

Please get in contact if these instructions don't work for you!

# WARNING: only tested with react-native@^0.64.2.
# Will not work on lower versions; *may* work on newer versions.

# Install the npm package
npm install --save react-native-native-runtime

# Install the Cocoapod
cd ios && pod install && cd ..

Usage

For now, as I haven't installed the package yet, just clone the repo and play around with example/src/App.tsx.

The Java runtime

🚧 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!

The Obj-C runtime

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.

TypeScript typings?

⚠️ 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 to get started.

The objc proxy object

As mentioned, this is available in the global scope on any Apple app.

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.

// Class lookup:
// Returns a JS HostObject wrapping a native class.
// This works via the Obj-C runtime helper NSClassFromString,
// so it has O(1) complexity.
objc.NSString;

// Protocol lookup:
// Returns a JS HostObject wrapping a native Protocol (if a class
// with the same name wasn't found first).
// This works via the Obj-C runtime helper NSProtocolFromString,
// so it has O(1) complexity.
objc.NSSecureCoding;

// Constant/variable lookup:
// Returns a JS HostObject wrapping any native global symbol (the
// most common ones being constants and variables).
// This works by looking up the symbol from the executable (a
// fallback undertaken when neither NSClassFromString nor
// NSProtocolFromString return a match for the given string).
// This works via dlsym, so I believe it has O(N) complexity, but
// probably isn't too slow anyway.
objc.NSStringTransformLatinToHiragana;

// Selector lookup:
// Returns a JS HostObject wrapping a native Selector.
// This works via the Obj-C runtime helper NSSelectorFromString, so
// it has O(1) complexity.
// I can't think of a good example for this, but it's possible.
objc.getSelector("NoGoodExample:soWhoKnows:");

// Will return an array of all Obj-C classes and all convenience
// methods, but that's all.
// Does not, for example, list out all constants/variables/protocols
// available. Those have to be looked up individually.
Object.keys(objc);

// Will return the string:
// [object HostObjectObjc]
objc.toString();

// Will cause an infinite loop and crash! Need some advice from JSI
// experts on this.
// It involves the following getters being called in sequence:
// $$typeof -> Symbol.toStringTag -> toJSON -> Symbol.toStringTag -> 
//   Symbol.toStringTag -> Symbol.toStringTag -> toString
console.log(objc);

Host Objects

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).

The objc proxy object is one such HostObject. I've made some others:

  • HostObjectClass (wraps a class)
  • HostObjectClassInstance (wraps a class instance)
  • HostObjectProtocol (wraps a protocol)
  • HostObjectSelector (wraps a selector)

TODO: I'll likely make these expand a common abstract class. For now, they all directly extend facebook::jsi::HostObject.

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.

HostObjectClass

You can obtain a HostObjectClass by looking up a class on the objc proxy object:

const nSStringClass: objc.NSString = objc.NSString;

You can also call class methods (AKA static methods, in other languages) on it:

const voice: objc.AVSpeechSynthesisVoice = 
  objc.AVSpeechSynthesisVoice['voiceWithLanguage:']('en-GB');

We'll cover what you can do with a class instance in the next section.

HostObjectClassInstance

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).

// Initialise an NSString
const hello: objc.NSString =
  objc.NSString.alloc()['initWithString:']('Hello');

// Return a new NSString by concatenating it 
const helloWorld: objc.NSString =
  hello['stringByAppendingString:'](', world!');

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:

  • string -> NSString
  • number -> NSNumber
  • boolean -> NSBoolean
  • Array -> NSArray
  • object -> NSDictionary
  • undefined -> nil
  • null -> nil

Conversely, you can also marshal the following types from Obj-C to JS:

  • NSString -> string
  • NSNumber -> number
  • NSBoolean -> boolean
  • NSArray -> Array (provided each of the constituent values are marshal-friendly)
  • NSDictionary -> object (provided each of the constituent values are marshal-friendly)
  • kCFNull -> null
  • nil -> undefined

Do so using the toJS() method on a HostObjectClassInstance:

// Marshal the NSString to a JS primitive string
console.log(helloWorld.toJS());

Beyond that, you can get the keys on the class instance:

// Will return a list of all instance variables, properties, and
// native methods, and some added methods like toString().
// TODO: list out all the *inherited* instance variables,
// properties, and methods as well.
Object.keys(helloWorld);

You can also use getters:

// Allocate a native class instance
const utterance: objc.AVSpeechUtterance =
  objc.AVSpeechUtterance.alloc()['initWithString:']('Hello, world!');

// Get the property
utterance.voice;

... and call setters:

// Allocate a native class instance
const utterance: objc.AVSpeechUtterance =
  objc.AVSpeechUtterance.alloc()['initWithString:']('Hello, world!');

// Set properties on it
utterance.voice =
  objc.AVSpeechSynthesisVoice['voiceWithLanguage:']('ja-JP');

... but both getters and setters are currently very experimental and I need some help from an Obj-C expert to get them right.

Is it complete?

The Java runtime isn't even started yet, for one thing.

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.

Is it production-ready?

No, but you can help that by contributing!

Seriously, expect errors to be thrown if you don't know what you're doing (particularly as there are no TypeScript typings yet).

Contributing

Get in touch on the #objc-runtime channel of the React Native JSI Discord, or send me a message on Twitter!

I'll start putting some issues together to indicate tasks that need help.

Acknowledgements

JSI is an API that is not yet publicly documented, so I've stood on the shoulders of a few giants to get here.

I couldn't have done this without Marc Rousavy's 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.

Thanks also to Oscar Franco for his JSI guides (e.g. this one) and starter template.

I also got a lot of mileage out of Ammar Ahmed's JSI guide, too – well worth a read.

Thank you to Takuya Matsuyama too, for his very minimal react-native-quick-base64 JSI module, and for being one of the first community members to jump into the ring to wrestle with JSI.

License

MIT

More Repositories

1

react-nativescript

React renderer for NativeScript
TypeScript
280
star
2

nside

A JavaScript IDE for accessing a phone/tablet's native runtime via NativeScript bindings!
TypeScript
101
star
3

react-native-web-browser-app

Not another wrapper around SFSafariViewController. A fully-featured, cross-platform web browser written in React Native.
TypeScript
58
star
4

react-native-typescript-2d-game

A simple 2D game (called The Box) implemented in React Native, as an Expo app, using TypeScript.
TypeScript
37
star
5

crank-native

A Crank renderer for iOS and Android apps, based on NativeScript.
TypeScript
25
star
6

dom-events-wintercg

A standalone implementation of the DOM Events APIs, extracted from the Node.js codebase, for usage in WinterCG runtimes.
JavaScript
17
star
7

react-native-nativescript-runtime

Use the NativeScript runtime from React Native
Java
14
star
8

subscripto

An app that handles subscriptions and local receipt validation – used in a real app, LinguaBrowse!
C
14
star
9

react-native-safe-popover

A faithful port of UIPopoverPresentationController, which respects the safe area, to React Native.
TypeScript
10
star
10

nativescript-grimoire

A grimoire of NativeScript incantations to invoke native APIs
9
star
11

mecab-ko

A fork of Mecab, with support for both Japanese and Korean, organised as a Cocoapod and npm package for usage with iOS/macOS.
C++
7
star
12

nativescript-html

HTML and some web platform features for NativeScript
TypeScript
7
star
13

cocoapojs

CocoaPods, ported to JavaScript
TypeScript
7
star
14

uikit-dom

Implementing DOM for UIKit; wish me luck
Swift
6
star
15

jp-pitch-accent

A demo of how to use Svelte components to take Japanese pitch accent notes.
Svelte
6
star
16

nativescript-syntax-highlighter

Syntax Highlighter for NativeScript. Currently only supports iOS (being completely based on Highlightr).
CSS
6
star
17

clang_metagen_deno

A Deno-based program for generating FFI metadata for languages like Obj-C using the Clang APIs.
TypeScript
6
star
18

react-nativescript-navigation

React Navigation integration for React NativeScript
TypeScript
4
star
19

react-nativescript-compat-react-native

Compatibility layer to allow React NativeScript projects to run React Native codebases
TypeScript
3
star
20

mecab-naist-jdic-utf-8

mecab-naist-jdic (a Japanese dictionary for use with Mecab), in UTF-8 format, organised as a Cocoapod and npm package for usage with iOS/macOS.
Ruby
3
star
21

pitch-accent

Predict pitch accent in Japanese
TypeScript
2
star
22

elementr

Convenience framework for assembling and styling a tree of HTML Elements.
TypeScript
2
star
23

seiyuu-ranker

Visualise which seiyuus were the most prolific in a given year or season.
Svelte
2
star
24

rnobjc

Repo for the code behind my React Native EU 2022 talk, "How to access all the Objective‑C APIs using JSI". A cleaner version of shirakaba/react-native-native-runtime.
Objective-C++
2
star
25

rpstrackerrns

Alex Ziskind's RPS Tracker app, ported from NativeScript Core to React NativeScript
TypeScript
2
star
26

react-nativescript-site

Source code for the React NativeScript documentation website
JavaScript
2
star
27

tns-template-blank-crank

NativeScript CLI template for building a Crank Native app.
JavaScript
1
star
28

migrate-ts-loader-to-babel-loader

JavaScript
1
star
29

url-wintercg

A polyfill for WHATWG URL and related APIs, powered by Ada.
C++
1
star
30

GrafanaSimpleJsonValueMapper-docker

Docker container for setting up GrafanaSimpleJsonValueMapper.
Shell
1
star
31

anime-diverge

JavaScript
1
star
32

JBSearchBar

React Native searchbar implemented in Swift
Swift
1
star
33

easytimeline-docker

EasyTimeline, Dockerised
Perl
1
star
34

react-nativescript-pikatalk

A real-world app, based on HelloTalk, made with React NativeScript.
TypeScript
1
star
35

rns-browser

[Under construction] A browser written in React NativeScript, based on the Cliqz and Firefox iOS browser UIs.
TypeScript
1
star
36

executor

Wrap up any executable as a Cocoa app!
Swift
1
star
37

Kanji-etymology-browser

Tools to create a lightweight, browsable digital version using scans of the 1988 kanji etymology paperback dictionary by Kenneth G. Henshall. For personal educational use.
HTML
1
star
38

nativescript-flowlayout

TypeScript
1
star
39

jmdict-sqlite

A crude script to download a release of `jmdict-simplified` and build an SQLite database from it.
JavaScript
1
star