Just Enough Swift for React Native Developers

The core concepts of Xcode and Swift

Photo by Joshua Aragon on Unsplash

So, you’re a React Native developer. That means you live with JavaScript. That means you didn’t much like Objective-C. That means you love Swift.

You’re here because you want to write native code in React Native using Swift.

But you don’t feel confident in this new world. “var” and “let” keywords look familiar but don’t work in a familiar way. What’s an exclamation mark? What’s a question mark? Whoa, double question marks? Underscore before a parameter? What’s a weak keyword? What’s lazy?

I am lazy so I don’t want to learn all that. I just want to learn enough Swift to write some Native code for React Native. So, let’s start…


So, first things first: “var” in Swift is “let” and “let” is “const”. So you can’t do this.

let k = 1
k = 2 // Cannot assign to value: 'k' is a 'let' constant

You need to use “var” instead of “let”, basically.


Classes and Functions

Some say Swift is functional, some say object oriented, some say it’s multi paradigm. Probably the last one is true but as a JavaScript developer it looks like OO to me and as you’ll be creating native components for iOS, it will feel like you’re writing classes and creating instances from it. You create classes similar to other OO languages but create instances without the “new” keyword. The constructor method is not the name of the class, it’s “init” for all classes. We can also overload the constructor, and override it if the classes have a superclass.

class MyAwesomeClass {
var k: Int = 0
init(){
self.k = 1
}
init(newK: Int){
self.k = newK
}
}
let a = MyAwesomeClass()
print(a.k) // prints 1
let b= MyAwesomeClass(newK: 12)
print(b.k) // prints 12

This is pretty obvious. What’s not obvious is why I need to put the label “newK” before the parameter itself. That’s Swift 3+. You have to use parameter names along with values. So check out the code below:

func sum(a: Int, b: Int) -> Int {
return a+b
}
let c = sum(3,5) // Missing argument labels 'a:b:' in calllet d = sum(a:3, b:5) // This will work
print(c) // prints 8

I am sure that somewhere, once I used a function likesum(3,4) and it worked. Yes it works sometimes — but when?

func sum(_ a: Int, _ b: Int) -> Int {
return a+b
}

Only when underscore is used before the parameter name, as above. Now function call withsum(3,5) will work. So we also revealed one of the mysteries of Swift: underscore. Parameter names preceded by underscore are ignored, so you can call them without parameter names.

By the way that “ → Int” is not an arrow function or lambda in some languages. That’s the return type of a function. For void don’t use anything.

Swift also supports variable number of parameters. For example, to write a function like “sum(3,5,6, 8)” you don’t need to overload many functions. Check the example below:

func sum(_ nums: Int...) -> Int {
var total = 0
nums.forEach { (val) in
total += val
}
return total
}

let t = sum(1,2,3,4,5,6,7)
print(t)

Sometimes before argument name you see another argument name. At first you might think it’s a reserved word with a special purpose. Then you see it a lot with different words, so you think it’s not a reserved word. You realise that this is called an argument label. “to” in the example below is an argument label:

class Building {
var color: String = "red"
func changeColor(to newColor:String){
self.color = newColor
}
}
var building = Building()
print(building.color) // prints red
building.changeColor(to: "yellow")
print(building.color) // prints yellow

When you call the function you use “to” as the variable name, but inside the function, “to” becomes “newColor”. To be honest, I don’t think this is useful. It looks like an unnecessary feature adopted from Objective-C. It’s supposed to make the function call more human readable. I have to say it does sound like “change the color to yellow”. Quite human readable…but that’s for humans,I am a developer! Why would I need such thing? It’s rather confusing to me. But that’s just my opinion.


Optionals

Probably the most eye catching things in Swift are the optionals. There are two types of optionals. Exclamation mark and question mark.

In order to have nil values in variables, you need to define them as optionals. Question mark optionals are safer but they need to be unwrapped before accessing them. Exclamation mark optionals can be assigned nil but when you access them, you need to be sure that they are not nil, or your app will crash.

The snippet below will compile but it will crash at runtime. Because the optionals are already unwrapped, so it’s only safe to use them when you know that they have a value.

var number: Int! = nilvar result = number * 2// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).

But this snippet will not even compile:

var number: Int? = nilprint(number*2)// Value of optional type 'Int?' must be unwrapped to a value of type 'Int'

Xcode will give you a hint. Either force unwrap or coalesce using “??”

If you force unwrap it will compile but crash in runtime because it does not have a value. Make sure that it has a value first and then unwrap it. Like this:

var number: Int? = nil
number = 2
print(number*2) // 4

Another option is to coalesce using “??”. This is very familiar for JavaScript developers. It’s is very similar to “||”. The below snippets are almost identical:

// JavaScript code below
let number = undefined
const newNum = number || 0
// Swift Code below
let number: Int? = nil
let newNum = number ?? 0

Optional chaining

If you’ve used babel with JavaScript, chances are that you’ve used “tc39/proposal-optional-chaining” which will probably become a standard for JavaScript in the future. Swift already has this built-in.

Look at the example below. This may cause Xcode to give some (ignorable) warnings, but it’s completely fine. It will compile and won’t throw any runtime errors.

class City {
var name = "BigCity"
}
class Town{
var name = "BigTown"
var city: City?
}
let town = Town()print(town.city?.name) // Ignored
town.city = City()
print(town.city?.name) // Prints BigCity

Objective-C

I know you don’t really like Objective-C, but there’s no escaping it in React Native. However, you can minimise it. I’m not talking about AppDelegate.m file — in most cases you don’t even need to touch that. I’m talking about your own native code.

You don’t need to write complete classes or functions in Objective-C. You can write all of them in Swift. However, you need to export them to JavaScript with Objective-C. So, you can think them as interfaces. In Swift you just need to flag them as exportable to Objective-C and from there to JavaScript. You flag a function or class exportable by using the “@objc” attribute — this is not a React Native specific attribute: any Xcode projects using Swift and Objective-C together can use it.

There are two different Native Modules. One of them is the UI component where we implement visual components derived from UIView. The other is what we call a headless component, which does not have a UI but can perform tasks like playing audio, managing notifications, handling GPS, etc.

UI Native modules

Let’s say we want to implement a Map (Google maps, MapBox, TomTom, etc) SDK. We want to add markers on the map and when it’s ready we call a direct/imperative function on the native side. Something like this:

/// JavaScript
import { NativeModules} from 'react-native'
const locateUser = () => {
NativeModules.MapViewController.locateUser([0,0])
}<Map
markers={[{lat: 100, lng:100}, {lat: -50, lng:50}]}
onMapReady={ this.locateUser }
/>

Typically we will need these components:

View Controller: (or View Manager) A class derived from RCTViewManager flagged with “@objc”. View controllers are exported using RCT_EXTERN_MODULE.

View: A class derived from UIView and returned by view() function in a view controller. Views are not exported as they are already shipped with the controller.

View Property: A variable or a function (flagged with objc). Those are the React props that are being sent from JavaScript. A function means it’s a callback and their type is always RCTDirectEventBlock. If it’s a variable it can be a simple object (like string or integer), array(NSArray) or a JSON object(NSDictionary).

Direct Methods: React props work declaratively. This means you don’t invoke them directly. You change the prop, it triggers React to take action. Sometimes you may want to invoke native functions directly. You use RCT_EXTERN_METHOD to export those methods. Look at the locateUser function below:

// Objective-C
@interface RCT_EXTERN_MODULE(MapViewController, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(mapMarkers, NSArray)
RCT_EXPORT_VIEW_PROPERTY(onMapReady, RCTDirectEventBlock)
RCT_EXTERN_METHOD(locateUser:(nonnull NSArray *)location resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@end// Swift// View Controller
@objc(MyViewController)
class MapViewController: RCTViewManager {

@objc func locateUser(_ location:NSArray, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock){

}
override func view() -> UIView? {
let mapView = MapView()
return mapView
}}//Viewclass GenericMap: UIView{
@objc var mapMarkers: NSArray = []
}

Headless native modules

I am not sure if they are called this but by headless native modules, I mean non-UI native modules. They are usually not JSX modules. They are the ones that you invoke directly. So they’re used and defined exactly like the “locateUser” method in the UI modules. They use RCT_EXTERN_MODULE to export the module and RCT_EXTERN_METHOD to export the method. An example for a headless native module would be an Audio Manager or a Notification Manager. They are invoked using the NativeModules object of the ‘react-native’ library.

Since you invoke functions directly and they get executed in the native side, you expect this to be done asynchronously. So we need to use callbacks or promises. If you look at “locateUser” function has 2 React (RCT is a prefix for React if you haven’t noticed) objects RCTPromiseResolveBlock and RCTPromiseRejectBlock. They have to be the last two parameters of such functions, in that order. So when you’re done with your native side execution, you will call one of those functions which will trigger its JavaScript counterpart.

This is just a small step into Swift world from a React Native developer. The concepts presented were the main ones I had to learn before I felt relatively confident with native modules. I’m still learning Swift and implementing React Native libraries using native modules both for Android and IOS. But I don’t feel like I have no idea what I’m doing when I work with Xcode and Swift, now that I’ve learnt some core concepts. We developers can figure out the rest on the way!

Happy learning.

Better Programming

Advice for programmers.

Cüneyt Aliustaoğlu

Written by

http://cuneyt.aliustaoglu.biz/

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade