Making libraries for React Native

Part II: iOS

Charlie L
Gbox
5 min readAug 25, 2016

--

This is the second part of my articles series.

Wow! I didn’t expect such an amazing answer from my last post!

It’s exciting to know that there are a lot of developers looking for a way to share their inventions. So, let’s continue with our main goal.

So far, we have the Android side. There are cases when a library just applies for one platform, and that’s ok. But if we can reach others, that would be awesome, right?

Fortunately, Chromecast SDK is present in both Android and iOS.

I’ll follow the same steps from the last post, don’t worry!

A different beginning

Wait! Let’s think a little bit more about wrapping this SDK.

There are two ways to include a library in iOS:

  1. Adding the library manually.
  2. Using a dependency manager.

Remember we added the Cast Companion Library in Android using gradle? It was pretty simple, right?

There are other advantages of using a dependency manager too.

iOS Chromecast SDK is over 300 mb ! Github won’t let us add it to our repository. Apart from that, could you imagine updating the library just to include a minor update from the SDK?

In Android, we use gradle to accomplish this. In iOS, we’re going to use Cocoapods.

Just to be specific, our library won’t implement Cocoapods. Instead, guys that use our project will need integrate Cocoapods in their project.

Don’t worry, it is pretty straightforward and really easy to use. And a big amount of libraries use it too! So, we’re going to assume in this article that they already installed Chromecast SDK, and we’re ready to start wrapping it.

Just as a reference, the library we’re going to use from Cocoapods is called ‘google-cast-sdk’. In the next chapter we will use it, so keep it in mind!

Wrapping the SDK (iOS flavor)

Guess what? react-native-create-library already created a great iOS template for us!

Inside the iOS folder, there are two files: RNChromecastLibrary.h and RNChromecastLibrary.m

What is the difference between them? Good question!

The .h file is an interface, and the .m is the implementation. The last one is where our code goes.

Let’s work around the same method we done in Android: startScan()

RCT_EXPORT_METHOD(startScan)
{
RCTLogInfo(@"start scanning chromecast!");
// Initialize device scanner.
GCKFilterCriteria *filterCriteria =
[GCKFilterCriteria
criteriaForAvailableApplicationWithID: kGCKMediaDefaultReceiverApplicationID];
self.deviceScanner =
[[GCKDeviceScanner alloc]
initWithFilterCriteria:filterCriteria];
[self.deviceScanner addListener:self];
[self.deviceScanner startScan];
[self.deviceScanner setPassiveScan:YES];
}

Let’s compare it with Android, shall we?

RCT_EXPORT_METHOD exposes the method to React Native. It’s the iOS version of @ReactMethod.

Same as in Android, we need to set the configuration options. And we’re going to use a default id to work with. kGCKMediaDefaultReceiverApplicationID is the one we need.

We’re not done yet! deviceScanner variable must be declared somewhere.

.h file to the rescue.

Delegates, events and more!

In iOS development, it’s common to hear about delegates.

What is a delegate, you may ask?

According to StackOverflow:

A delegate allows one object to send messages to another object when an event happens.

By using Chromecast SDK delegates, we can listen to events related to connections and status, for example.

Let’s take a look to RNChromecastLibrary.h. It may look like this

#import "RCTBridgeModule.h"

@interface RNChromecastLibrary : NSObject <RCTBridgeModule>

@end

We’re are importing RCTBridgeModule.h, which help us with the communication with React Native. And, right next to NSObject, we’re using RCTBridgeModule as a delegate.

Let’s add the missing pieces!

#import "RCTBridgeModule.h"
#import <GoogleCast/GoogleCast.h>

@interface RNChromecastLibrary : NSObject <RCTBridgeModule, GCKDeviceScannerListener, GCKDeviceManagerDelegate,GCKMediaControlChannelDelegate>

@property(nonatomic, strong) GCKDeviceScanner* deviceScanner;
@end

First, we added the Chromecast SDK header (GoogleCast.h), so we can start using data from it.

And now, all the delegates we need!

And remember the deviceScanner variable? Here is a good place to place it. So others can see our interface and have an idea of what we have in a quick look. Really handy!

Let’s get back to RNChromecastLibrary.m

How we can tell to our implementation that we want to listen the events? Well, here’s what we are looking for:

[self.deviceScanner addListener:self];

And that’s it! Now, we just need to find a way to send this information to React Native.

Using delegates

According to Chromecast SDK documentation, there is a method that we can use to know if there are devices around us.

- (void)deviceDidComeOnline:(GCKDevice *)device {
}

We have already subscribed to the delegate that sends this notification. It will be invoked immediately when receiving the alert of an active Chromecast.

- (void)deviceDidComeOnline:(GCKDevice *)device {
NSLog(@"device found!! %@", device.friendlyName);
[self emitMessageToRN:@"GoogleCast:DeviceAvailable"
:@{@"device_available": @YES}];
}

Great! Just like we did in the Android tutorial, we will create a method that will send this information to React Native.

- (void) emitMessageToRN: (NSString *)eventName
:(NSDictionary *)params{
[self.bridge.eventDispatcher sendAppEventWithName: eventName
body: params];
}

Yeah, I know. Objective-C methods look weird.

Essentially, its structure is:

- (returnType) methodName: (Type *)varName{

}

And, if you need more parameters, just use “:” between them.

Let’s explain our emitMessageToRN!

By using the self.bridge.eventDispatcher, we can send events to React Native. And like the Android code we wrote last time, it receives an object, ready to be read by javascript.

Last step is to add this self.bridge reference. Right from the React Native documentation, we just need to declare the bridge variable.

@synthesize bridge = _bridge;

Main thread!

If we play with the startScan method, it will crash. Oh, bummer.

Just exactly like the Android example, Chromecast SDK needs to run in the main thread. So:

RCT_EXPORT_METHOD(startScan)
{
RCTLogInfo(@"start scan chromecast!");
// Initialize device scanner.
dispatch_async(dispatch_get_main_queue(), ^{
GCKFilterCriteria *filterCriteria =
[GCKFilterCriteria criteriaForAvailableApplicationWithID: kGCKMediaDefaultReceiverApplicationID];
self.deviceScanner = [[GCKDeviceScanner alloc] initWithFilterCriteria:filterCriteria];
[self.deviceScanner addListener:self];
[self.deviceScanner startScan];
[self.deviceScanner setPassiveScan:YES];

});
}

dispatch_async is the line we need. This let us run code on the main thread.

No more worries, let’s continue!

Sharing libraries with the world

One big step further!

Although, we didn’t share this library yet.

And we didn’t test it neither. Even knowing it compiles correctly, we should create an example project.

What about using both Android and iOS codes in a common API? That’s the reason we worked so hard, right?

Patience, young grasshopper. Next chapter will talk all about this common API, neat tricks to try our implementation, and more!

Remember you can take a look to the full implementation of my Chromecast SDK wrapper. Pull requests are welcome!

Do you have any question? Or just want to say Hello (World)? Tweet me!

--

--

Charlie L
Gbox
Editor for

Software engineer. Happily located at the intersection of technology and art.