Updated documentation to include enhanced support for custom method types.
This commit is contained in:
Nick Lockwood 2015-04-19 18:04:45 +01:00
Родитель 9ba30b0825
Коммит 74252c9ec7
1 изменённых файлов: 52 добавлений и 17 удалений

Просмотреть файл

@ -15,20 +15,19 @@ This is a more advanced guide that shows how to build a native module. It assume
## iOS Calendar module example
This guide will use [iOS Calendar API](https://developer.apple.com/library/mac/documentation/DataManagement/Conceptual/EventKitProgGuide/Introduction/Introduction.html) example. Let's say we would like to be able to access the iOS calendar from JavaScript.
This guide will use the [iOS Calendar API](https://developer.apple.com/library/mac/documentation/DataManagement/Conceptual/EventKitProgGuide/Introduction/Introduction.html) example. Let's say we would like to be able to access the iOS calendar from JavaScript.
Native module is just an Objective-C class that implements `RCTBridgeModule` protocol. If you are wondering, RCT is a shorthand for ReaCT.
A native module is just an Objective-C class that implements the `RCTBridgeModule` protocol. If you are wondering, RCT is a shorthand for ReaCT.
```objective-c
// CalendarManager.h
#import "RCTBridgeModule.h"
#import "RCTLog.h"
@interface CalendarManager : NSObject <RCTBridgeModule>
@end
```
React Native will not expose any methods of `CalendarManager` to JavaScript unless explicitly asked. Fortunately this is pretty easy with `RCT_EXPORT_METHOD`:
In addition to implementing the `RCTBridgeModule` protocol, your class must also include the `RCT_EXPORT_MODULE()` macro. This takes an optional argument that specifies the name that the module will accessed by in your JavaScript code (more on this later). If you do not specify a name, the JavaScript module name will match the Objective-C class name.
```objective-c
// CalendarManager.m
@ -36,15 +35,19 @@ React Native will not expose any methods of `CalendarManager` to JavaScript unle
RCT_EXPORT_MODULE();
@end
```
React Native will not expose any methods of `CalendarManager` to JavaScript unless explicitly told to. This is done using the `RCT_EXPORT_METHOD()` macro:
```objective-c
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}
@end
```
Now from your JavaScript file you can call the method like this:
Now, from your JavaScript file you can call the method like this:
```javascript
var CalendarManager = require('NativeModules').CalendarManager;
@ -53,13 +56,13 @@ CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');
> **NOTE**: JavaScript method names
>
> The name of the method exported to JavaScript is the native method's name up to the first colon. React Native also defines a macro called `RCT_REMAP_METHOD` to specify the JavaScript method's name. This is useful when multiple native methods are the same up to the first colon and would have conflicting JavaScript names.
> The name of the method exported to JavaScript is the native method's name up to the first colon. React Native also defines a macro called `RCT_REMAP_METHOD()` to specify the JavaScript method's name. This is useful when multiple native methods are the same up to the first colon and would have conflicting JavaScript names.
The return type of bridge methods is always `void`. React Native bridge is asynchronous, so the only way to pass a result to JavaScript is by using callbacks or emitting events (see below).
## Argument types
React Native supports several types of arguments that can be passed from JavaScript code to native module:
`RCT_EXPORT_METHOD` supports all standard JSON object types, such as:
- string (`NSString`)
- number (`NSInteger`, `float`, `double`, `CGFloat`, `NSNumber`)
@ -68,15 +71,47 @@ React Native supports several types of arguments that can be passed from JavaScr
- map (`NSDictionary`) with string keys and values of any type from this list
- function (`RCTResponseSenderBlock`)
In our `CalendarManager` example, if we want to pass event date to native, we have to convert it to a string or a number:
But it also works with any type that is supported by the `RCTConvert` class (see [`RCTConvert`](https://github.com/facebook/react-native/blob/master/React/Base/RCTConvert.h) for details). The `RCTConvert` helper functions all accept a JSON value as input and map it to a native Objective-C type or class.
In our `CalendarManager` example, we to pass the event date to the native method. We can't send JavaScript Date objects over the bridge, so we need to convert the date to a string or number. We could write our native function like this:
```objective-c
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSInteger)secondsSinceUnixEpoch)
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSNumber *)secondsSinceUnixEpoch)
{
NSDate *date = [NSDate dateWithTimeIntervalSince1970:secondsSinceUnixEpoch];
NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
}
```
or like this:
```objective-c
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSString *)ISO8601DateString)
{
NSDate *date = [RCTConvert NSDate:ISO8601DateString];
}
```
But by using the automatic type conversion feature, we can skip the manual conversion step completely, and just write:
```objective-c
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSDate *)date)
{
// Date it ready to use!
}
```
You would then call this from JavaScript by using:
```javascript
CalendarManager.addEvent('Birthday Party', date.toTime()); // passing date as number of seconds since Unix epoch
```
or
```javascript
CalendarManager.addEvent('Birthday Party', date.toISOString()); // passing date as ISO-8601 string
```
As `CalendarManager.addEvent` method gets more and more complex, the number of arguments will grow. Some of them might be optional. In this case it's worth considering changing the API a little bit to accept a dictionary of event attributes, like this:
```objective-c
@ -84,7 +119,8 @@ As `CalendarManager.addEvent` method gets more and more complex, the number of a
RCT_EXPORT_METHOD(addEvent:(NSString *)name details:(NSDictionary *)details)
{
NSString *location = [RCTConvert NSString:details[@"location"]]; // ensure location is a string
NSString *location = [RCTConvert NSString:details[@"location"]];
NSDate *time = [RCTConvert NSDate:details[@"time"]];
...
}
```
@ -101,14 +137,13 @@ CalendarManager.addEvent('Birthday Party', {
> **NOTE**: About array and map
>
> React Native doesn't provide any guarantees about the types of values in these structures. Your native module might expect an array of strings, but if JavaScript calls your method with an array containing numbers and strings, you'll get `NSArray` with `NSNumber` and `NSString`. It is the developer's responsibility to check array/map value types (see [`RCTConvert`](https://github.com/facebook/react-native/blob/master/React/Base/RCTConvert.h) for helper methods).
> Objective-C doesn't provide any guarantees about the types of values in these structures. Your native module might expect an array of strings, but if JavaScript calls your method with an array containing numbers and strings, you'll get an `NSArray` containing a mix of `NSNumber` and `NSString`. For arrays, `RCTConvert` provides some typed collections you can use in your method declaration, such as `NSStringArray`, or `UIColorArray`. For maps, it is the developer's responsibility to check the value types individually by manually calling `RCTConvert` helper methods.
## Callbacks
> **WARNING**
>
> This section is even more experimental than others, we don't have a set of best practices around callbacks yet.
> This section is more experimental than others, we don't have a set of best practices around callbacks yet.
Native module also supports a special kind of argument- a callback. In most cases it is used to provide the function call result to JavaScript.
@ -136,7 +171,7 @@ Native module is supposed to invoke its callback only once. It can, however, sto
If you want to pass error-like object to JavaScript, use `RCTMakeError` from [`RCTUtils.h`](https://github.com/facebook/react-native/blob/master/React/Base/RCTUtils.h).
## Implementing native module
## Implementing a native module
The native module should not have any assumptions about what thread it is being called on. React Native invokes native modules methods on a separate serial GCD queue, but this is an implementation detail and might change. If the native module needs to call main-thread-only iOS API, it should schedule the operation on the main queue: