How to use type coersion in Javascript

Issue #391

People who make fun of Javascript probably don’t understand implicit type coersion and when to use triple equal. Javascript is very unexpected, but when we work with this language, we need to be aware.

  1. Coercion–Automatically changing a value from one type to another.
  2. If x is Number and y is String, return x == ToNumber(y)
  3. If x is String or Number and y is Object, return x == ToPrimitive(y)
  4. Empty array becomes empty string

Read more

How to safely access deeply nested object in Javascript

Issue #390

An object ‘s property can be null or undefined.

Accessing step by step is tedious

1
2
3
4
props.user &&
props.user.posts &&
props.user.posts[0] &&
props.user.posts[0].comments

Dynamic parsing path is too clever and involves string in the end, which is a no no

1
2
3
4
const get = (p, o) =>
p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o)

const getUserComments = get(['user', 'posts', 0, 'comments'])

Instead let’s use function and catch errors explicitly, and defaults with a fallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const get: (f, defaultValue) => {
try {
const value = f()
if (isNotNullOrUndefined(value)) {
return value
} else {
return defaultValue
}
} catch {
return defaultValue
}
}

const comments = get(() => { .user.posts[0].comments }, [])

Read more

How to use flow type in Javascript

Issue #389

Prefer flow over TypeScript for simplicity

Javascript primitive types number and string are too general and do not express the domain objects. Because lack of type alias in Javascript, we can use flow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export type Centimeter = number
export type Color = string
export type ImageSource = number
export type Kilogram = number
export type Kilocalorie = number // 1 cal = 4.1840 J
export type Second = number
export type SecondsSince1970 = number
export type Minute = number
export type DateString = string // 2018-11-20
export type DateTimeString = string // 2018-11-20T00:00:00
export type YearWeekString = string // 201838

export type FunctionWithVoid = () => void
export type FunctionWithString = (string) => void
export type FunctionWithBoolean = (boolean) => void
export type FunctionWithNumber = (number) => void
export type JSONObject = any
export type JSONString = string
export type StringToString = any

export type Degree = number
export type Radian = number

export type Animal = 'cat' | 'dog' | 'cow'

How to use React JSX with Babel in Electron

Issue #352

For a normal electron app created with npm init, we can use all features of ES6, but not the JSX syntax for React. We can use just Babel to transpile JSX, as used in IconGenerator

.babelrc

1
2
3
4
5
6
{
"plugins": [
"transform-react-jsx-source"
],
"presets": ["env", "react"]
}

And in package.json, call babel to transpile src to dist

1
2
3
4
5
"main": "dist/main.js",
"scripts": {
"start": "npm run babel && electron .",
"babel": "babel ./src --out-dir ./dist --copy-files"
},

Remember to use dist/main.js as our starting point, and in index.html, specify ./dist/renderer.js

1
2
3
4
5
6
<body>
<div id="root" />
<script type="text/javascript">
require('./dist/renderer.js')
</script>
</body>

How to submit electron app to AppStore

Issue #342

Before

Install electron as dev npm install electron --save-dev
Update electron-packager npm install electron-packager@latest --save-dev
Use no space in app name

Package with electron-packager

Follow https://github.com/electron/electron-osx-sign/wiki/Packaging-and-Submitting-an-Electron-App-to-the-Mac-App-Store

1
2
3
npx electron-packager . "MyApp" --app-bundle-id=com.onmyway133.MyApp --helper-bundle-id=com.onmyway133.MyApp.helper --app-version=1.4.0 --build-version=1.0.100 --platform=mas --arch=x64 --icon=Icon/Icon.icns --overwrite
npx electron-osx-sign "MyApp-mas-x64/MyApp.app" --verbose
npx electron-osx-flat "MyApp-mas-x64/MyApp.app" --verbose

If you have multiple developer identities in your keychain:

electron-osx-sign searches your keychain for the first signing certificates that it can locate. If you have multiple certificates then it may not know which cert you want to use for signing and you need to explicitly provide the name:

1
electron-osx-sign "My App-mas-x64/My App.app" --identity="3rd Party Mac Developer Application: My Company, Inc (ABCDEFG1234)" --verbose

Read more

Sign with electron-osx-sign

Read README https://github.com/electron/electron-osx-sign

For distribution in the Mac App Store: Have the provisioning profile for distribution placed in the current working directory and the signing identity installed in the default keychain.

Certificate

On developer.apple.com, create Mac App Distribution certificate. Make sure when we download in Keychain Access, it has associated private key

Manually upload

1
/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/itms/bin/iTMSTransporter -m upload  -assetFile MyApp/MyApp.pkg  -u onmyway133@gmail.com -p mypassword

Use Application Loader

Use Using app-specific passwords

Troubleshooting

electron Bad CFBundleExecutable. Cannot find executable file

ERROR ITMS-90261: “Bad CFBundleExecutable. Cannot find executable file that matches the value of CFBundleExecutable in the nested bundle MyApp [com.onmyway133.MyApp.pkg/Payload/MyApp.app/Contents/Frameworks/MyApp (GPU).app] property list file.”

https://github.com/electron/electron-packager/issues?utf8=%E2%9C%93&q=helper

Try electron 5.0.0 npm install electron@5.0.0 --save-dev

Specifically, we found that when the user closes the main application window there is no menu item to re-open it.

https://stackoverflow.com/questions/35008347/electron-close-w-x-vs-right-click-dock-and-quit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createMenu() {
const application = {
label: "MyApp",
submenu: [
{
label: "New",
accelerator: "Command+N",
click: () => {
if (win === null) {
createWindow()
}
}
}
]
}
}

‘electron-osx-flat@latest’ is not in the npm registry

1
npm install -g electron-osx-sign@latest

App sandbox not enabled

electron-osx-sign Command failed: codesign

1
ran xattr -cr *

Command failed: codesign bundle format is ambiguous

Perhaps you accidentally packaged the previous generated app bundle into your newly packaged app?

Remove dist folder generated by electron-builder

How to organise test files

Issue #327

In terms of tests, we usually have files for unit test, UI test, integeration test and mock.

Out of sight, out of mind.

Unit tests are for checking specific functions and classes, it’s more convenient to browse them side by side with source file. For example in Javascript, Kotlin and Swift

1
2
3
index.js
index.test.js
index.mock.js
1
2
3
LocationManager.kt
LocationManager.mock.kt
LocationManager.test.kt
1
2
3
BasketHandler.swift
BasketHandler.mock.swift
BasketHandler.test.swift

Integration tests check features or sub features, and may cover many source files, it’s better to place them in feature folders

1
2
3
4
5
6
7
8
9
10
11
- Features
- Cart
- Sources
- Tests
- Cart.test.swift
- Validator.test.swift
- Profile
- Sources
- Tests
- Updater.test.swift
- AvatarUploader.test.swift

What is create-react-native-app

Issue #279

Original post https://medium.com/fantageek/what-is-create-react-native-app-9f3bc5a6c2a3


As someone who comes to React Native from iOS and Android background, I like React and Javascript as much as I like Swift and Kotlin. React Native is a cool concept, but things that sound good in theory may not work well in practice.

Another layer of abstraction

Up until now, I still don’t get why big companies choose React Native over native ones, as in the end what we do is to deliver good experience to the end user, not the easy development for developers. I have seen companies saying goodbye to React Native and people saying that React Native is just React philosophy learn once, write anywhere, that they choose React Native mainly for the React style, not for cross platform development. But the fact can’t be denied that people choose React Native mostly for cross platform development. Write once, deploy many places is so compelling. We still of course need to tweak a bit for each platform, but most of the code is reuse.

I have to confess that I myself spend less time developing than digging into compiling and bundling issues. Nothing comes for free. To be successful with React Native, not only we need strong understanding on both iOS and Android, but also ready to step into solving issues. We can’t just open issues, pray and wait and hope someone solves it. We can’t just use other dependencies blindly and call it a day. A trivial bug can be super hard to fix in React Native, and our projects can’t wait. Big companies have, of course, enough resource to solve issues, but for small teams, React Native is a trade off decision to consider.

A wrapper for a wrapper

Enough about that. Let’s go to official React Native getting started guide. It recommend create-react-native-app. As someone who made electron.js apps and used create-react-app, I choose create-react-native-app right away, as I think it is the equivalent in terms of creating React Native apps. I know there is react-native init and though that it must have some problems, otherwise people don’t introduce create-react-native-app.

After playing with create-react-native-app and eject, and after that read carefully through its caveats, I know it’s time to use just react-native init . In the end I just need a quick way to bootstrap React Native app with enough tooling (Babel, JSX, Hot reloading), however create-react-native-app is just a limited experience in Expo with opinionated services like Expo push notification docs.expo.io/versions/latest/guides/push-notifications.html and connectivity to Google Cloud Platform and AWS. I know every thing has its use case, but in this case it’s not the tool for me. Dependencies are the root of all evils, and React Native already has a lot, I don’t want more unnecessary things.

The concept of Expo is actually nice, in that it allows newbie to step into React Native fast with recommended tooling already set up, and the ability to reject later. It also useful in sharing Snack to help reproduce issues. But the name of the repo create-react-native-app is a bit misleading.

Maybe it’s just me, or …

How to make tag selection view in React Native

Issue #271

Original post https://hackernoon.com/how-to-make-tag-selection-view-in-react-native-b6f8b0adc891


Besides React style programming, Yoga is another cool feature of React Native. It is a cross-platform layout engine which implements Flexbox so we use the same layout code for both platforms.

As someone who uses Auto Layout in iOS and Constraint Layout in Android, I find Flexbox bit hard to use at first, but there are many tasks that Flexbox does very well, they are distribute elements in space and flow layout. In this post we will use Flexbox to build a tag selection view using just Javascript code. This is very easy to do so we don’t need to install extra dependencies.

Our tag view will support both multiple selection and exclusive selection. First, we need a custom Button .

Button with Background

Button is of the basic elements in React Native, but it is somewhat limited if we want to have custom content inside the button, for example texts, images and background

import { Button } from 'react-native'
...

<Button
  onPress={onPressLearnMore}
  title="Learn More"
  color="#841584"
  accessibilityLabel="Learn more about this purple button"
/>

TouchableOpacity

Luckily we have TouchableOpacity, which is a wrapper for making views respond properly to touches. On press down, the opacity of the wrapped view is decreased, dimming it.

To implement button in our tag view, we need to a button with background a check image. Create a file called BackgroundButton.js

import React from 'react'
import { TouchableOpacity, View, Text, StyleSheet, Image } from 'react-native'
import R from 'res/R'

export default class BackgroundButton extends React.Component {
  render() {
    const styles = this.makeStyles()
    return (
      <TouchableOpacity style={styles.touchable} onPress={this.props.onPress}>
        <View style={styles.view}>
          {this.makeImageIfAny(styles)}
          <Text style={styles.text}>{this.props.title}</Text>
        </View>
      </TouchableOpacity>
    )
  }

makeImageIfAny(styles) {
    if (this.props.showImage) {
      return <Image style={styles.image} source={R.images.check} />
    }
  }

makeStyles() {
    return StyleSheet.create({
      view: {
        flexDirection: 'row',
        borderRadius: 23,
        borderColor: this.props.borderColor,
        borderWidth: 2,
        backgroundColor: this.props.backgroundColor,
        height: 46,
        alignItems: 'center',
        justifyContent: 'center',
        paddingLeft: 16,
        paddingRight: 16
      },
      touchable: {
        marginLeft: 4,
        marginRight: 4,
        marginBottom: 8
      },
      image: {
        marginRight: 8
      },
      text: {
        fontSize: 18,
        textAlign: 'center',
        color: this.props.textColor,
        fontSize: 16
      }
    })
  }
}

Dynamic styles

Normally we use const styles = StyleSheet.create({}) but since we want our button to be configurable, we make styles into a function, so on every render we get a new styles with proper configurations. The properties we support are borderColor, textColor, backgroundColor and showImage

In the makeImageIfAny we only need to return Image if the view is selected. We don’t have the else case, so in if showImage is false, this returns undefined and React won’t render any element

makeImageIfAny(styles) {
  if (this.props.showImage) {
    return <Image style={styles.image} source={R.images.check} />
  }
}

padding and margin

To understand padding and margin, visit CSS Box Model. Basically padding means clearing an area around the content and padding is transparent, while margin means clearing an area outside the border and the margin also is transparent.

Pay attention to styles . We have margin for touchable so that each tag button have a little margin outside each other.

touchable: {
  marginLeft: 4,
  marginRight: 4,
  marginBottom: 8
}

In the view we need flexDirection as row because React Native has flexDirection as column by default. And a row means we have Image and Text side by side horizontally inside the button. We also use alignItems and justifyContent to align elements centeredly on both main and cross axises. The padding is used to have some spaces between the inner text and the view.

view: {
  flexDirection: 'row',
  height: 46,
  alignItems: 'center',
  justifyContent: 'center',
  paddingLeft: 16,
  paddingRight: 16
}

Tag View

Create a file called TagsView.js This is where we parse tags and show a bunch of BackgroundButton

import React from 'react'
import { View, StyleSheet, Button } from 'react-native'
import R from 'res/R'
import BackgroundButton from 'library/components/BackgroundButton'
import addOrRemove from 'library/utils/addOrRemove'

export default class TagsView extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      selected: props.selected
    }
  }

render() {
    return (
      <View style={styles.container}>
        {this.makeButtons()}
      </View>
    )
  }

onPress = (tag) => {
    let selected
    if (this.props.isExclusive) {
      selected = [tag]
    } else {
      selected = addOrRemove(this.state.selected, tag)
    }

this.setState({
      selected
    })
  }

makeButtons() {
    return this.props.all.map((tag, i) => {
      const on = this.state.selected.includes(tag)
      const backgroundColor = on ? R.colors.on.backgroundColor : R.colors.off.backgroundColor
      const textColor = on ? R.colors.on.text : R.colors.off.text
      const borderColor = on ? R.colors.on.border : R.colors.off.border

return (
        <BackgroundButton
          backgroundColor={backgroundColor}
          textColor={textColor}
          borderColor={borderColor}
          onPress={() => {
            this.onPress(tag)
          }}
          key={i}
          showImage={on}
          title={tag} />
      )
    })
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    flexWrap: 'wrap',
    padding: 20
  }
})

We parse an array of tags to build BackgroundButton . We keep the selected array in state because this is mutated inside the TagsView component. If it is isExclusive then the new selected contains just the new selected tag. If it is multiple selection, then we add the new selected tag into the selected array.

The addOrRemove is a our homegrown utility function to add an item into an array if it does not exists, or remove if it exists, using the high orderfilter function.

const addOrRemove = (array, item) => {
  const exists = array.includes(item)

if (exists) {
    return array.filter((c) => { return c !== item })
  } else {
    const result = array
    result.push(item)
    return result
  }
}

Flex wrap

Pay attention to styles

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    flexWrap: 'wrap',
    padding: 20
  }
})

The hero here is flexWrap which specifies whether the flexible items should wrap or not. Take a look at CSS flex-wrap property for other options. Since we have main axis as row , element will be wrapped to the next row if there are not enough space. That’s how we can achieve a beautiful tag view.

Using TagsView

Then consuming TagsView is as easy as declare it inside render

const selected = ['Swift', Kotlin]
const tags = ['Swift', 'Kotlin', 'C#', 'Haskell', 'Java']

return (
  <TagsView
    all={tags}
    selected={selected}
    isExclusive={false}
  />
)

Learning Flexbox

Learning Flebox is crucial in using React and React Native effectively. The best places to learn it are w3school CSS Flexbox and Basic concepts of flexbox by Mozzila.
Basic concepts of flexbox
The Flexible Box Module, usually referred to as flexbox, was designed as a one-dimensional layout model, and as a…developer.mozilla.org

There is a showcase of all possible Flexbox properties
The Full React Native Layout Cheat Sheet
A simple visual guide with live examples for all major React Native layout propertiesmedium.com

Yoga has its own YogaKit published on CocoaPods, you can learn it with native code in iOS
Yoga Tutorial: Using a Cross-Platform Layout Engine
Learn about Yoga, Facebook’s cross-platform layout engine that helps developers write more layout code in style akin to…www.raywenderlich.com

And when we use flexbox, we should compose element instead of hardcoding values, for example we can use another View with justifyContent: flex-end to move a button down the screen. This follows flexbox style and prevent rigid code.
Position element at the bottom of the screen using Flexbox in React Native
React Native uses Yoga to achieve Flexbox style layout, which helps us set up layout in a declarative and easy way.medium.com

Where to go from here

I hope you learn something useful in this post. For more information please consult the official guide Layout with Flexbox and layout-props for all the possible Flexbox properties.

Getting to know some pragmatic programming language features

Issue #270

As you know, in the Pragmatic Programmer, section Your Knowledge Portfolio, it is said that

Learn at least one new language every year. Different languages solve the same problems in different ways. By learning several different approaches, you can help broaden your thinking and avoid getting stuck in a rut. Additionally, learning many languages is far easier now, thanks to the wealth of freely available software on the Internet

I see learning programming languages as a chance to open up my horizon and learn some new concepts. It also encourage good habit like immutability, composition, modulation, …

I’d like to review some of the features of all the languages I have played with. Some are useful, some just make me interested or say “wow”

Curly braces

Each language can have its own style of grouping block of code, but I myself like the curly braces the most, which are cute :]

Some like C, Java, Swift, … use curly braces

Swift

init(total: Double, taxPct: Double) {
  self.total = total
  self.taxPct = taxPct
  subtotal = total / (taxPct + 1)
}

Some like Haskell, Python, … use indentation

Haskell

bmiTell :: (RealFloat a) => a -> String  
bmiTell bmi  
    | bmi <= 18.5 = "You're underweight, you emo, you!"  
    | bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"  
    | bmi <= 30.0 = "You're fat! Lose some weight, fatty!"  
    | otherwise   = "You're a whale, congratulations!"

Some like Elixir use keyword list

ELixir

if false, do: :this, else: :that

Named parameter

Language like Objective C, Swift offer named parameter, which make it easier to reason about a function call

func sayHello(to person: String, and anotherPerson: String) -> String {
    return "Hello \(person) and \(anotherPerson)!"
}

Explicit type

Language like C, Swift, Java, … have type information in parameter and in return, which make it easier to reason about a function call

Swift

func sayHello(personName: String, alreadyGreeted: Bool) -> String {
    if alreadyGreeted {
        return sayHelloAgain(personName)
    } else {
        return sayHello(personName)
    }
}

List comprehension

Languages like Haskell, Python, Elixir, support list comprehension

Elixir

iex> for n <- [1, 2, 3, 4], do: n * n
[1, 4, 9, 16]

First class function

I enjoy functional programming, so first class function support in Javascript, Swift, Haskell, Elixir, … really make me happy

Haskell

zipWith' (*) (replicate 5 2) [1..]

Curry

Currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument (partial application)

Language like Swift 2, Haskell, … have curry by default. Some like Javascript can use libraries (Lodash, …) to achieve this. In Haskell, every function officially only takes one parameter.

In Swift 3, curry was removed :(

Haskell

1
2
multThree :: (Num a) => a -> a -> a -> a  
multThree x y z = x * y * z

By calling functions with too few parameters, we’re creating new functions on the fly.

Javascript

1
2
3
4
5
6
7
8
9
var curry = require('lodash.curry');
var map = curry(function(f, ary) {
return ary.map(f);
});
var getChildren = function(x) {
return x.childNodes;
};

var allTheChildren = map(getChildren);

Pattern matching

I find pattern matching as a better way around if else statement

Swift supports pattern matching in switch statement

Swift

1
2
3
4
5
6
7
8
9
10
11
enum Trades {
case Buy(stock: String, amount: Int, stockPrice: Float)
case Sell(stock: String, amount: Int, stockPrice: Float)
}
let aTrade = Trades.Buy(stock: "APPL", amount: 200, stockPrice: 115.5)
switch aTrade {
case .Buy(let stock, let amount, _):
process(stock, amount)
case .Sell(let stock, let amount, _):
process(stock, amount * -1)
}

Some like Haskell, Elixir, … also pattern matches on function name, which makes it work great for recursion

Haskell

sayMe :: (Integral a) => a -> String  
sayMe 1 = "One!"  
sayMe 2 = "Two!"  
sayMe 3 = "Three!"  
sayMe 4 = "Four!"  
sayMe 5 = "Five!"  
sayMe x = "Not between 1 and 5"  


map _ []     = []
map f (x:xs) = f x : map f xs

In Elixir, the = operator is actually a match operator

Elixir

iex> x = 1
1
iex> x
1
iex> 1 = x
1
iex> 2 = x
** (MatchError) no match of right hand side value: 1

Recursion

Some language like Haskell, Elixir, … don’t use loop, they use recursion with performance in mind, no overflow.

Haskell

length' :: (Num b) => [a] -> b  
length' [] = 0  
length' (_:xs) = 1 + length' xs

Laziness

Some languages support infinite collection, thanks to their laziness.

Haskell is lazy, if you map something over a list several times and filter it several times, it will only pass over the list once

Haskell

largestDivisible :: (Integral a) => a  
largestDivisible = head (filter p [100000,99999..])  
    where p x = x `mod` 3829 == 0

Elixir defines the concept of Eager with Enum and Lazy with Stream

Elixir

1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum

Custom operator

Elixir is famous for its pipe |> operator

The |> symbol used in the snippet above is the pipe operator: it simply takes the output from the expression on its left side and passes it as the first argument to the function call on its right side

Elixir

1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum

Haskell often takes advantage of this custom -: operator Haskell

x -: f = f x  

(0,0) -: landLeft 1 -: landRight 1 -: landLeft 2

Functor, Applicative Functor, Monoid, Monad

I really like enjoy Haskell because of these typeclasses. It realizes common pattern (map, apply, join, bind, …) with comptutational context. It really enlightens me when I find that function is a Monad as well (you should read the Reader monad)

Haskell

instance Monad Maybe where  
    return x = Just x  
    Nothing >>= f = Nothing  
    Just x >>= f  = f x  
    fail _ = Nothing  

landLeft :: Birds -> Pole -> Maybe Pole  
landLeft n (left,right)  
    | abs ((left + n) - right) < 4 = Just (left + n, right)  
    | otherwise                    = Nothing  

landRight :: Birds -> Pole -> Maybe Pole  
landRight n (left,right)  
    | abs (left - (right + n)) < 4 = Just (left, right + n)  
    | otherwise                    = Nothing

ghci> return (0,0) >>= landLeft 1 >>= banana >>= landRight 1  
Nothing

List comprehension in Haskell is just syntactic sugar for using lis as Monad Haskell

ghci> [ (n,ch) | n <- [1,2], ch <- ['a','b'] ]  
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

Swift

enum Result<T> {
    case Value(T)
    case Error(NSError)
}

extension Result {
    func map<U>(f: T -> U) -> Result<U> {
        switch self {
            case let .Value(value):
                return Result<U>.Value(f(value))
            case let .Error(error):
                return Result<U>.Error(error)
        }
    }
}

extension Result {
    static func flatten<T>(result: Result<Result<T>>) -> Result<T> {
        switch result {
            case let .Value(innerResult):
                return innerResult
            case let .Error(error):
                return Result<T>.Error(error)
        }
    }
}

extension Result {
    func flatMap<U>(f: T -> Result<U>) -> Result<U> {
        return Result.flatten(map(f))
    }
}

Trait and mixin

Languages like Scala, … support trait

Similar to interfaces in Java, traits are used to define object types by specifying the signature of the supported methods. Unlike Java, Scala allows traits to be partially implemented; i.e. it is possible to define default implementations for some methods

Scala

trait Similarity {
  def isSimilar(x: Any): Boolean
  def isNotSimilar(x: Any): Boolean = !isSimilar(x)
}

class Point(xc: Int, yc: Int) extends Similarity {
  var x: Int = xc
  var y: Int = yc
  def isSimilar(obj: Any) =
    obj.isInstanceOf[Point] &&
    obj.asInstanceOf[Point].x == x
}

Swift can uses Protocol Extension to achieve trait

Swift

protocol GunTrait {
    func shoot() -> String {
        return "Shoot"
    }
}

protocol RenderTrait {
    func render() -> String {
        return "Render"
    }
}

struct Player: GameObject, AITrait, GunTrait, RenderTrait, HealthTrait {

}

Ruby supports Mixin via Module

Ruby

module Greetings
  def hello
    puts "Hello!"
  end

  def bonjour
    puts "Bonjour!"
  end

  def hola
    puts "Hola!"
  end
end

class User
  include Greetings
end

Delegate property

There are certain common kinds of properties that would be very nice to implement once and for all like lazy, observable and storing. An example is in Kotlin

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

Where to go from here

Hope you find something interesting. Each language has its own pros and is designed for specific purpose. So no list will be enough to cover them all.

To take a quick peek into other programming languages, I find Learn in One Video by Derek very helpful.

There are things that intrigue us every day like Swift initialization rule make it explicit when using initializer, Go goroutine and channel for concurrent code, Elixir process for easy concurrent and message communication. You’ll be amazed by how process encapsulates state, Haskell data type encourages immutability and thread safe code, Elixir macro for great extension of the language. The best way to to learn is to use and dive into the languages often.

May your code continue to compile.

While you are here, you may like my other posts


Updated at 2020-12-05 05:44:54

How to add app icons and splash screens to a React Native app in staging and production

Issue #268

React Native was designed to be “learn once, write anywhere,” and it is usually used to build cross platform apps for iOS and Android. And for each app that we build, there are times we need to reuse the same code, build and tweak it a bit to make it work for different environments. For example, we might need multiple skins, themes, a free and paid version, or more often different staging and production environments.

And the task that we can’t avoid is adding app icons and splash screens to our apps.

In fact, to add a staging and production environment, and to add app icons, requires us to use Xcode and Android Studio, and we do it the same way we do with native iOS or Android projects.

Let’s call our app MyApp and bootstrap it with react-native init MyApp . There will of course, be tons of libraries to help us with managing different environments.

In this post, we will do just like we did with native apps, so that we know the basic steps.

Build configuration, target, build types, production flavor, and build variant

There are some terminologies we needed to remember. In iOS, debug and releases are called build configurations, and staging and production are called targets.

A build configuration specifies a set of build settings used to build a target’s product in a particular way. For example, it is common to have separate build configurations for debug and release builds of a product.

A target specifies a product to build and contains the instructions for building the product from a set of files in a project or work-space. A target defines a single product; it organizes the inputs into the build system — the source files and instructions for processing those source files — required to build that product. Projects can contain one or more targets, each of which produces one product

In Android, debug and releases are called build types, and staging and production are called product flavors. Together they form build variants.

For example, a “demo” product flavor can specify different features and device requirements, such as custom source code, resources, and minimum API levels, while the “debug” build type applies different build and packaging settings, such as debug options and signing keys. The resulting build variant is the “demoDebug” version of your app, and it includes a combination of the configurations and resources included in the “demo” product flavor, “debug” build type, and main/ source set.

Staging and production targets in iOS

Open MyApp.xcodeproj inside ios using Xcode. Here is what we get after bootstrapping:

React Native creates iOS and tvOS apps, and two test targets. In Xcode, a project can contain many targets, and each target means a unique product with its own build settings — Info.plist and app icons.

Duplicate target

If we don’t need the tvOS app, we can delete the MyApp-tvOS and MyApp-tvOSTests . Let’s use MyApp target as our production environment, and right click -> Duplicate to make another target. Let’s call it MyApp Staging.

Each target must have unique bundle id. Change the bundle id of MyApp to com.onmyway133.MyApp and MyApp Staging to com.onmyway133.MyApp.Staging.

Info.plist

When we duplicate MyApp target , Xcode also duplicates Info.plist into MyApp copy-Info.plist for the staging target. Change it to a more meaningful name Info-Staging.plist and drag it to the MyApp group in Xcode to stay organised. After dragging, MyApp Staging target can’t find the plist, so click Choose Info.plist File and point to the Info-Staging.plist.

Scheme

Xcode also duplicates the scheme when we duplicate the target, so we get MyApp copy:

Click Manage Schemes in the scheme drop-down to open Scheme manager:

I usually delete the generated MyApp copy scheme, then I create a new scheme again for the MyApp Staging target. You need to make sure that the scheme is marked as Shared so that it is tracked into git.

For some reason, the staging scheme does not have all the things set up like the production scheme. You can run into issues like ‘React/RCTBundleURLProvider.h’ file not found or RN: ‘React/RCTBridgeModule.h’ file not found . It is because React target is not linked yet.

To solve it, we must disable Parallelise Build and add React target and move it above MyApp Staging.

Staging and production product flavors in Android

Open the android folder in Android Studio. By default there are only debug and release build types:

They are configured in the app module build.gradle:

buildTypes {
    release {
        minifyEnabled enableProguardInReleaseBuilds
        proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
    }
}

First, let’s change application id to com.onmyway133.MyApp to match iOS. It is not required but I think it’s good to stay organised. Then create two product flavors for staging and production. For staging, let’s add .Staging to the application id.

From Android Studio 3, “all flavors must now belong to a named flavor dimension” — normally we just need default dimensions. Here is how it looks in build.gradle for our app module:

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    flavorDimensions "default"

defaultConfig {
        applicationId "com.onmyway133.MyApp"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"
        ndk {
            abiFilters "armeabi-v7a", "x86"
        }
    }
    splits {
        abi {
            reset()
            enable enableSeparateBuildPerCPUArchitecture
            universalApk false  // If true, also generate a universal APK
            include "armeabi-v7a", "x86"
        }
    }
    buildTypes {
        release {
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        }
    }

productFlavors {
        staging {
            applicationIdSuffix ".Staging"
        }

        production {

        }
    }
}

Click Sync Now to let gradle do the syncing job. After that, we can see that we have four build variants:

How to run staging and production

To run the Android app, we can specify a variant like react-native run-android — variant=productionDebug, but I prefer to go to Android Studio, select the variant, and run.

To run iOS app, we can specify the scheme like react-native run-ios — simulator=’iPhone X’ — scheme=”MyApp Staging” . As of react-native 0.57.0 this does not work. But it does not matter as I usually go to Xcode, select the scheme, and run.

Add app icon for iOS

According to the Human Interface Guideline, we need app icons of different sizes for different iOS versions, device resolutions, and situations (notification, settings, Spring Board). I’ve crafted a tool called IconGenerator, which was previously mentioned in Best Open Source Tools For Developers. Drag the icon that you want — I prefer those with 1024x1024 pixels for high resolution app icons — to the Icon Generator MacOS app.

Click Generate and we get AppIcon.appiconset . This contains app icons of the required sizes that are ready to be used in Asset Catalog. Drag this to Asset Catalog in Xcode. That is for production.

For staging, it’s good practice to add a “Staging” banner so that testers know which is staging, and which is production. We can easily do this in Sketch.

Remember to set a background, so we don’t get a transparent background. For an app icon with transparent background, iOS shows the background as black which looks horrible.

After exporting the image, drag the staging icon to the IconGenerator the same way we did earlier. But this time, rename the generated appiconset to AppIcon-Staging.appiconset. Then drag this to Asset Catalog in Xcode.

For the staging target to use staging app icons, open MyApp Staging target and choose AppIcon-Staging as App Icon Source.

Add app icon for Android

I like to switch to Project view, as it is easier to change app icons. Click res -> New -> Image Asset to open Asset Studio. We can use the same app icons that we used in iOS:

Android 8.0 (API level 26) introduced Adaptive Icons so we need to tweak the Resize slider to make sure our app icons look as nice as possible.

Android 8.0 (API level 26) introduces adaptive launcher icons, which can display a variety of shapes across different device models. For example, an adaptive launcher icon can display a circular shape on one OEM device, and display a squircle on another device. Each device OEM provides a mask, which the system then uses to render all adaptive icons with the same shape. Adaptive launcher icons are also used in shortcuts, the Settings app, sharing dialogs, and the overview screen. — Android developers

We are doing for production first, which means the main Res Directory. This step will replace the existing placeholder app icons generated by Android Studio when we bootstrapped React Native projects.

Now that we have production app icons, let’s make staging app icons. Android manages code and assets via convention. Click on src -> New -> Directory and create a staging folder. Inside staging, create a folder called res . Anything we place in staging will replace the ones in main — this is called source sets.

You can read more here: Build with source sets.

You can use source set directories to contain the code and resources you want packaged only with certain configurations. For example, if you are building the “demoDebug” build variant, which is the crossproduct of a “demo” product flavor and “debug” build type, Gradle looks at these directories, and gives them the following priority:
src/demoDebug/ (build variant source set)
src/debug/ (build type source set)
src/demo/ (product flavor source set)
src/main/ (main source set)

Right click on staging/res -> New -> Image Asset to make app icons for staging. We also use the same staging app icons like in iOS, but this time we choose staging as Res Directory. This way Android Studio knows how to generate different ic_launcher and put them into staging.

Add launch screen for iOS

The splash screen is called a [Launch Screen](http://Launch Screen) in iOS, and it is important.

A launch screen appears instantly when your app starts up. The launch screen is quickly replaced with the first screen of your app, giving the impression that your app is fast and responsive

In the old days, we needed to use static launch images with different sizes for each device and orientation.

Launch Screen storyboard

For now the recommended way is to use Launch Screen storyboard . The iOS project from React Native comes with LaunchScreen.xib but xib is a thing of the past. Let’s delete it and create a file called Launch Screen.storyboard .

Right click on MyApp folder -> New and chose Launch Screen, add it to both targets as usually we show the same splash screen for both staging and production.

Image Set

Open asset catalog, right click and select New Image Set . We can name it anything. This will be used in the Launch Screen.storyboard.

Open Launch Screen.storyboard and add an UIImageView . If you are using Xcode 10, click the Library button in the upper right corner and choose Show Objects Library.

Set image for Image View, and make sure Content Mode is set to Aspect Filled, as this ensures that the image always covers the full screen (although it may be cropped). Then connect ImageView using constraints to the View, not the Safe Area. You do this by Control+drag from the Image View (splash) to the View.

Constrains without margin

Click into each constraint and uncheck Relative to Margin. This makes our ImageView pin to the very edges of the view and with no margin at all.

Now go to both targets and select Launch Screen.storyboard as Launch Screen File:

On iOS, the launch screen is often cached, so you probably won’t see the changes. One way to avoid that is to delete the app and run it again.

Add a launcher theme for Android

There are several ways to add splash screen for Android, from using launcher themes, Splash Activity, and a timer. For me, a reasonable splash screen for Android should be a very minimal image.

As there are many Android devices with different ratios and resolutions, if you want to show a full screen splash image, it will probably not scale correctly for each device. This is just about UX.

For the splash screen, let’s use the launcher theme with splash_background.xml .

Learn about Device Metric

There is no single splash image that suits all Android devices. A more logical approach is to create multiple splash images for all common resolutions in portrait and landscape. Or we can design a minimal splash image that works. You can find more info here: Device Metric.

Here is how to add splash screen in 4 easy steps:

Add splash image

We usually need a common splash screen for both staging and production. Drag an image into main/res/drawble . Android Studio seems to have a problem with recognising some jpg images for the splash screen, so it’s best to choose png images.

Add splash_background.xml

Right click on drawable -> New -> Drawable resource file . Name it whatever you want — I choose splash_background.xml . Choose the root element as layer-list:

A [Layer List](http://Layer List) means “a Drawable that manages an array of other Drawables. These are drawn in array order, so the element with the largest index is drawn on top”. Here is how splash_background.xml looks like:

<?xml version="1.0" encoding="utf-8"?>

<!-- The android:opacity=”opaque” line — this is critical in preventing a flash of black as your theme transitions. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:opacity="opaque">

    <!-- The background color, preferably the same as your normal theme -->
    <item android:drawable="@android:color/white"/>

    <!-- Your splash image -->
    <item>
        <bitmap
            android:src="@drawable/iron_man"
            android:gravity="center"/>

    </item>

</layer-list>

Note that we point to our splash image we added earlier with android:src=”@drawable/iron_man”.

Declare style

Open styles.xml and add SplashTheme:

<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@drawable/splash_background</item>
</style>

Use SplashTheme

Go to Manifest.xml and change the theme of the the launcher activity, which has category android:name=”android.intent.category.LAUNCHER” . Change it to android:theme=”@style/SplashTheme” . For React Native, the launcher activity is usually MainActivity . Here is how Manifest.xml looks:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myapp">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    <application
      android:name=".MainApplication"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:allowBackup="false"
      android:theme="@style/AppTheme">
      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:theme="@style/SplashTheme"
        android:windowSoftInputMode="adjustResize">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
      </activity>
      <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    </application>

</manifest>

Run the app now and you should see the splash screen showing when the app starts.

Managing environment configurations

The differences between staging and production are just about app names, application ids, and app icons. We probably use different API keys, and backend URL for staging and production.

Right now the most popular library to handle these scenarios is react-native-config, which is said to “bring some 12 factor love to your mobile apps”. It requires lots of steps to get started, and I hope there is a less verbose solution.

Where to go from here

In this post, we touched Xcode and Android Studio more than Visual Studio Code, but this was inevitable. I hope this post was useful to you. Here are some more links to read more about this topic:


I originally published on Free Code Camp https://medium.freecodecamp.org/how-to-add-app-icons-and-splash-screens-to-a-react-native-app-in-staging-and-production-d1dab615e7c6

Updated at 2021-01-13 09:14:18

Get to know different JavaScript environments in React Native

Issue #266

Original post https://medium.freecodecamp.org/get-to-know-different-javascript-environments-in-react-native-4951c15d61f5


React Native can be very easy to get started with, and then at some point problems occur and we need to dive deep into it.

The other day we had a strange bug that was only occurring in production build, and in iOS only. A long backtrace in the app revealed that it was due to Date constructor failure.

const date = new Date("2019-01-18 12:00:00")

This returns the correct Date object in debug mode, but yields Invalid Date in release. What’s special about Date constructor? Here I’m using react native 0.57.5 and no Date libraries.

Date constructor

The best resource for learning Javascript is via Mozilla web docs, and entering Date:

Creates a JavaScript Date instance that represents a single moment in time. Date objects use a Unix Time Stamp, an integer value that is the number of milliseconds since 1 January 1970 UTC.

Pay attention to how Date can be constructed by dateString:

dateStringString value representing a date. The string should be in a format recognized by the Date.parse() method (IETF-compliant RFC 2822 timestamps and also a version of ISO8601).

So Date constructor uses static method Date.parse under the hood. This has very specific requirement about the format of date string that it supports

The standard string representation of a date time string is a simplification of the ISO 8601 calendar date extended format (see Date Time String Format section in the ECMAScript specification for more details). For example, “2011-10-10” (date-only form), “2011-10-10T14:48:00” (date-time form), or “2011-10-10T14:48:00.000+09:00” (date-time form with milliseconds and time zone) can be passed and will be parsed. When the time zone offset is absent, date-only forms are interpreted as a UTC time and date-time forms are interpreted as local time.
The ECMAScript specification states: If the String does not conform to the standard format the function may fall back to any implementation–specific heuristics or implementation–specific parsing algorithm. Unrecognizable strings or dates containing illegal element values in ISO formatted strings shall cause Date.parse() to return NaN.

The reason that we get Invalid Date in iOS must be because the code was run in two different JavaScript environments and they somehow have different implementation of the Date parsing function.

JavaScript Environment

React Native guide has a dedicated section about JavaScript environments.

When using React Native, you’re going to be running your JavaScript code in two environments:

  • In most cases, React Native will use JavaScriptCore, the JavaScript engine that powers Safari. Note that on iOS, JavaScriptCore does not use JIT due to the absence of writable executable memory in iOS apps.

  • When using Chrome debugging, all JavaScript code runs within Chrome itself, communicating with native code via WebSockets. Chrome uses V8 as its JavaScript engine.

While both environments are very similar, you may end up hitting some inconsistencies. We’re likely going to experiment with other JavaScript engines in the future, so it’s best to avoid relying on specifics of any runtime.

React Native also uses Babel and some polyfills to have some nice syntax transformers, so some of the code that we write may not be necessarily supported natively by JavascriptCore.

Now it is clear that while we debug our app via Chrome debugger, it works because V8 engine handles that. Now try turning off Remote JS Debugging: we can see that the above Date constructor fails, which means it is using JavascriptCore.

To confirm this issue, let’s run our app in Xcode and go to the Safari app on MacOS to enter Development menu. Select the active Simulator and choose JSContext on the current iOS app. Remember to turn off Remote JS Debugging so that the app uses JavascriptCore:

Now open the Console in Safari dev tools, and we should have access to JavascriptCore inside our app. Try running the above Date constructor to confirm that it fails:

What are legit date string formats?

Since 2016, JavascriptCore supports most ES6 features:

As of r202125, JavaScriptCore supports all of the new features in the ECMAScript 6 (ES6) language specification

And it was fully confirmed a year later in JSC 💕 ES6

ES2015 (also known as ES6), the version of the JavaScript specification ratified in 2015, is a huge improvement to the language’s expressive power thanks to features like classes, for-of, destructuring, spread, tail calls, and much more
WebKit’s JavaScript implementation, called JSC (JavaScriptCore), implements all of ES6

For more details about JavaScript features supported by different JavaScript engines, visit this ECMAScript comparison table.

Now for the date string format, from Date.parse, let’s visit ECMAScript 2015 specification to see what it says about date string format:

ECMAScript defines a string interchange format for date-times based upon a simplification of the ISO 8601 Extended Format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ

Where the fields are as follows:

“T” appears literally in the string, to indicate the beginning of the time element.

So JavascriptCore requires T specifier and V8 can work without it. The fix for now is to always include that T specifier. This way we always follow ECMAScript standards to make sure it works across different JavaScript environments.

const date = new Date("2019-01-18 12:00:00".replace(' ', 'T'))

And now it returns correct Date object. There may be difference between JavascriptCore on iOS and macOS, and among different iOS versions. The lesson learned here is that we should always test our app thoroughly in production and on devices to make sure it works as expected.

How to Make Linear Gradient View with Bridging in React Native

Issue #264

Original post https://medium.com/react-native-training/react-native-bridging-how-to-make-linear-gradient-view-83c3805373b7


React Native lets us build mobile apps using only Javascript. It works by providing a common interface that talks to native iOS and Android components. There are enough essentials components to get started, but the cooler thing is that it is easy to build our own, hence we are not limited by React Native. In this post we will implement a linear gradient view, which is not supported by default in React Native, using native UI component, particularly CAGradientLayer in iOS and GradientDrawable in Android.

In Javascript there are hundreds of libraries for a single problem and you should check if you really need it or not. A search on Google for linear gradient shows a bunch of libraries, like react-native-linear-gradient. The less dependencies the better. Linear gradient is in fact very easy to build and we probably don’t need to add extra dependencies. Also integrating and following updates with 3rd libraries are painful, I would avoid that as much as possible.

Native UI component vs Native module

In React Native, there are native UI component and native module. React Native moves pretty fast so most of the articles will be outdated, it’s best to consult official documentation for the latest React Native version. This post will try to give you overview of the whole picture because for now the official guide seems not completed.

In simple explanation, native UI component is about making UIView in iOS or View in Android available as React.Component and used in render function in Javascript.

There are tons of native UI widgets out there ready to be used in the latest apps — some of them are part of the platform, others are available as third-party libraries, and still more might be in use in your very own portfolio. React Native has several of the most critical platform components already wrapped, like ScrollView and TextInput, but not all of them, and certainly not ones you might have written yourself for a previous app.

Native module is more general in that we make any native class available in Javascript.

Sometimes an app needs access to platform API, and React Native doesn’t have a corresponding module yet. Maybe you want to reuse some existing Objective-C, Swift or C++ code without having to reimplement it in JavaScript, or write some high performance, multi-threaded code such as for image processing, a database, or any number of advanced extensions.

View Manager

To expose native UI views, we use the ViewManager as the bridge, it is RCTViewManager in iOS and SimpleViewManager in Android. Then inside this ViewManager we can just return our custom view. I see it’s good to use Objective C/Java for the ViewManager to match React Native classes, and the custom view we can use either Swift/Objective C in iOS and Kotlin/Java in Android.

I prefer to use Swift, but in this post to remove the overhead of introducing bridging header from Swift to Objective C, we use Objective C for simplicity. We also add the native source code directly into iOS and Android project, but in the future we can extract them easily to a React Native library.

For now let ‘s use the name RNGradientViewManager and RNGradientView to stay consistent between iOS and Android. The RN prefix is arbitrary, you can use any prefix you want, but here I use it to indicate that these classes are meant to be used in Javascript side in React Native.

Implement in iOS

Project structure

Add these Objective-C classes to the projects, I usually place them inside NativeComponents folder

Native views are created and manipulated by subclasses of RCTViewManager. These subclasses are similar in function to view controllers, but are essentially singletons - only one instance of each is created by the bridge. They expose native views to the RCTUIManager, which delegates back to them to set and update the properties of the views as necessary. The RCTViewManagers are also typically the delegates for the views, sending events back to JavaScript via the bridge.

RNGradientViewManager

Create a RNGradientViewManager that inherits from RCTViewManager

RNGradientViewManager.h

#import <React/RCTViewManager.h>
@interface RNGradientViewManager : RCTViewManager
@end

RNGradientViewManager.m

#import "RNGradientViewManager.h"
#import "RNGradientView.h"

[@implementation](http://twitter.com/implementation) RNGradientViewManager

RCT_EXPORT_MODULE()

- (UIView *)view {
  return [[RNGradientView alloc] init];
}

RCT_EXPORT_VIEW_PROPERTY(progress, NSNumber);
RCT_EXPORT_VIEW_PROPERTY(cornerRadius, NSNumber);
RCT_EXPORT_VIEW_PROPERTY(fromColor, UIColor);
RCT_EXPORT_VIEW_PROPERTY(toColor, UIColor);

[@end](http://twitter.com/end)

In iOS we use macro RCT_EXPORT_MODULE() to automatically register the module with the bridge when it loads. The optional js_name argument will be used as the JS module name. If omitted, the JS module name will match the Objective-C class name.

#define RCT_EXPORT_MODULE(js_name)

The ViewManager, not the View, is the facade to the Javascript side, so we expose properties using RCT_EXPORT_VIEW_PROPERTY . Note that we do that inside @implementation RNGradientViewManager

Here we specify the types as NSNumber and UIColor , and later in Javascript we can just pass number and color hex string, and React Native can do the conversions for us. In older versions of React Native, we need processColor in Javascript or RCTConvert color in iOS side, but we don’t need to perform manual conversion now.

RNGradientView

In the Native UI component example for iOS, they use WKWebView but here we make a RNGradientView which subclasses from RCTView to take advantage of many features of React Native views, and to avoid some problems we can get if using a normal UIView

RNGradientView.h

#import <UIKit/UIKit.h>
#import <React/RCTView.h>

[@interface](http://twitter.com/interface) RNGradientView : RCTView

[@end](http://twitter.com/end)

RNGradientView.m

#import "RNGradientView.h"
#import <UIKit/UIKit.h>

[@interface](http://twitter.com/interface) RNGradientView()
[@property](http://twitter.com/property) CAGradientLayer *gradientLayer;

[@property](http://twitter.com/property) UIColor *_fromColor;
[@property](http://twitter.com/property) UIColor *_toColor;
[@property](http://twitter.com/property) NSNumber *_progress;
[@property](http://twitter.com/property) NSNumber *_cornerRadius;
[@end](http://twitter.com/end)

[@implementation](http://twitter.com/implementation) RNGradientView

// MARK: - Init

- (instancetype)initWithFrame:(CGRect)frame
{
  self = [super initWithFrame:frame];
  if (self) {
    self.gradientLayer = [self makeGradientLayer];
    [self.layer addSublayer:self.gradientLayer];

self._fromColor = [UIColor blackColor];
    self._toColor = [UIColor whiteColor];
    self._progress = [@0](http://twitter.com/0).5;

[self update];
  }
  return self;
}

// MARK: - Life cycle

- (void)layoutSubviews {
  [super layoutSubviews];

self.gradientLayer.frame = CGRectMake(
    0, 0,
    self.bounds.size.width*self._progress.floatValue,
    self.bounds.size.height
  );
}

// MARK: - Properties

- (void)setFromColor:(UIColor *)color {
  self._fromColor = color;
  [self update];
}

- (void)setToColor:(UIColor *)color {
  self._toColor = color;
  [self update];
}

- (void)setProgress:(NSNumber *)progress {
  self._progress = progress;
  [self update];
}

- (void)setCornerRadius:(NSNumber *)cornerRadius {
  self._cornerRadius = cornerRadius;
  [self update];
}

// MARK: - Helper

- (void)update {
  self.gradientLayer.colors = @[
    (id)self._fromColor.CGColor,
    (id)self._toColor.CGColor
  ];

self.gradientLayer.cornerRadius = self._cornerRadius.floatValue;

[self setNeedsLayout];
}

- (CAGradientLayer *)makeGradientLayer {
  CAGradientLayer *gradientLayer = [CAGradientLayer layer];

gradientLayer.masksToBounds = true;

gradientLayer.startPoint = CGPointMake(0.0, 0.5);
  gradientLayer.endPoint = CGPointMake(1.0, 0.5);
  gradientLayer.anchorPoint = CGPointZero;

return gradientLayer;
}

[@end](http://twitter.com/end)

We can implement anything we want in this native view, in this case we use CAGradientLayer to get nicely displayed linear gradient. Since RNGradientViewManager exposes some properties like progress, cornerRadius, fromColor, toColor we need to implement some setters as they will be called by React Native when we update values in Javascript side. In the setter we call setNeedsLayout to tell the view to invalidate the layout, hence layoutSubviews will be called again.

requireNativeComponent

Open project in Visual Studio Code, add GradientView.js to src/nativeComponents . The folder name is arbitrary, but it’s good to stay organised.

import { requireNativeComponent } from 'react-native'

module.exports = requireNativeComponent('RNGradientView', null)

Here we use requireNativeComponent to load our RNGradientView . We only need this one Javascript file for interacting with both iOS and Android. You can name the module as RNGradientView but I think the practice in Javascript is that we don’t use prefix, so we name just GradientView .

const requireNativeComponent = (uiViewClassName: string): string =>
  createReactNativeComponentClass(uiViewClassName, () =>
    getNativeComponentAttributes(uiViewClassName),
  );

module.exports = requireNativeComponent;

Before I tried to use export default for the native component, but this way the view is not rendered at all, even if I wrap it inside React.Component . It seems we must use module.exports for the native component to be properly loaded.

Now using it is as easy as declare the GradientView with JSX syntax

import GradientView from 'nativeComponents/GradientView'

export default class Profile extends React.Component {
  render() {
    return (
      <SafeAreaView style={styles.container}>
        <GradientView
          style={styles.progress}
          fromColor={R.colors.progress.from}
          toColor={R.colors.progress.to}
          cornerRadius={5.0}
          progress={0.8} />
    )
  }
}

Implement in Android

Project structure

Add these Java classes to the projects, I usually place them inside nativeComponents folder

RNGradientManager

Create a RNGradientManager that extends SimpleViewManager
RNGradientManager.java

package com.onmyway133.myApp.nativeComponents;

import android.support.annotation.Nullable;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;

public class RNGradientViewManager extends SimpleViewManager<RNGradientView> {
    [@Override](http://twitter.com/Override)
    public String getName() {
        return "RNGradientView";
    }

[@Override](http://twitter.com/Override)
    protected RNGradientView createViewInstance(ThemedReactContext reactContext) {
        return new RNGradientView(reactContext);
    }

// Properties

[@ReactProp](http://twitter.com/ReactProp)(name = "progress")
    public void setProgress(RNGradientView view, [@Nullable](http://twitter.com/Nullable) float progress) {
        view.setProgress(progress);
    }

[@ReactProp](http://twitter.com/ReactProp)(name = "cornerRadius")
    public void setCornerRadius(RNGradientView view, [@Nullable](http://twitter.com/Nullable) float cornerRadius) {
        view.setCornerRadius(cornerRadius);
    }

[@ReactProp](http://twitter.com/ReactProp)(name = "fromColor", customType = "Color")
    public void setFromColor(RNGradientView view, [@Nullable](http://twitter.com/Nullable) int color) {
        view.setFromColor(color);
    }

[@ReactProp](http://twitter.com/ReactProp)(name = "toColor", customType = "Color")
    public void setToColor(RNGradientView view, [@Nullable](http://twitter.com/Nullable) int color) {
        view.setToColor(color);
    }
}

We usually use Color as android.graphics.Color , but for the GradientDrawable that we are going to use, it use color as ARGB integer. So it’s nifty that React Native deals with Color as int type. We also need to specify customType = “Color” as Color is something kinda custom.

RNGradientView

This is where we implement our view, we can do that in Kotlin if we like.

RNGradientView.java

package com.onmyway133.myApp.nativeComponents;

import android.content.Context;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.ScaleDrawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;

public class RNGradientView extends View {

    float progress;
    float cornerRadius;
    int fromColor;
    int toColor;

    public RNGradientView(Context context) {
        super(context);
    }

    public RNGradientView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public RNGradientView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public RNGradientView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    // update

    void update() {
        GradientDrawable gradient = new GradientDrawable();
        gradient.setColors(new int[] {
            this.fromColor,
            this.toColor
        });
         gradient.setOrientation(GradientDrawable.Orientation.*LEFT_RIGHT*);
        gradient.setGradientType(GradientDrawable.*LINEAR_GRADIENT*);
        gradient.setShape(GradientDrawable.*RECTANGLE*);
        gradient.setCornerRadius(this.cornerRadius * 4);

        ScaleDrawable scale = new ScaleDrawable(gradient, Gravity.*LEFT*, 1, -1);
        scale.setLevel((int)(this.progress * 10000));

        this.setBackground(scale);
    }

    // Getter & setter

    public void setProgress(float progress) {
        this.progress = progress;
        this.update();
    }

    public void setCornerRadius(float cornerRadius) {
        this.cornerRadius = cornerRadius;
        this.update();
    }

    public void setFromColor(int fromColor) {
        this.fromColor = fromColor;
        this.update();
    }

    public void setToColor(int toColor) {
        this.toColor = toColor;
        this.update();
    }
}

Pay attention to the setColors as it use an array of int

Sets the colors used to draw the gradient.
Each color is specified as an ARGB integer and the array must contain at least 2 colors.

If we call setBackground with the GradientDrawable it will be stretched to fill the view. In our case we want to support progress which determines how long the gradient should show. To fix that we use ScaleDrawable which is a Drawable that changes the size of another Drawable based on its current level value.

The same value for cornerRadius works in iOS, but for Android we need to use higher values, that’s why the multiplication in gradient.setCornerRadius(this.cornerRadius * 4)

Shape drawable

Another way to implement gradient is to use Shape Drawable with xml , it’s the equivalent of using xib in iOS. We can create something like gradient.xml and put that inside /res/drawable

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
    android:shape="rectangle">
    <gradient
        android:startColor="#3B5998"
        android:endColor="#00000000"
        android:angle="45"/>    
</shape>

For more information, you can read
Android Shape Drawables Tutorial
Have you ever wanted to reduce your Android application’s size or make it look more interesting? If yes, then you…android.jlelse.eu

We can also use the class directly ShapeDrawable in code

A Drawable object that draws primitive shapes. A ShapeDrawable takes a Shape object and manages its presence on the screen. If no Shape is given, then the ShapeDrawable will default to a RectShape.
This object can be defined in an XML file with the element.

GradientManagerPackage

In iOS we use RCT_EXPORT_MODULE to register the component, but in Android, things are done explicitly using Package . A package can register both native module and native UI component. In this case we deal with just UI component, so let’s return RNGradientManager in createViewManagers

GradientManagerPackage.java

package com.onmyway133.myApp.nativeComponents;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class RNGradientViewPackage implements ReactPackage {
    [@Override](http://twitter.com/Override)
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

[@Override](http://twitter.com/Override)
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.<ViewManager>asList(
            new RNGradientViewManager()
        );
    }
}

Then head over to MainApplication.java to declare our package

[@Override](http://twitter.com/Override)
protected List<ReactPackage> getPackages() {
  return Arrays.<ReactPackage>asList(
      new MainReactPackage(),
      new RNGradientViewPackage()
  );
}

That’s it for Android. We already have the GradientView.js written earlier, when running the app in Android, it will look up and load our RNGradientView

Where to go from here

Hope you learn something about native UI component. In the post we only touch the surfaces on what native UI component can do, which is just passing configurations from Javascript to native. There are a lot more to discover, like event handling, thread, styles, custom types, please consult the official documentation for correct guidance.

How to dismiss keyboard with react-navigation in React Native apps

Issue #263

Original post https://medium.com/react-native-training/how-to-dismiss-keyboard-with-react-navigation-in-react-native-apps-4b987bbfdc48


Showing and dismiss keyboard seems like a trivial thing to do in mobile apps, but it can be tricky in automatically dismissing it when it comes together with react-navigation and modal presentation. At least that’s according to my initial assumption. This article aims to detail what I have learned about keyboard handling and how to avoid extra tap when dealing with TextInput There will also be lots of code spelunking, thanks to the all the libraries being open source. The version of React Native I’m using at the time of writing is 0.57.5

The built in TextInput component

React Native comes with a bunch of basic components, one of them is the TextInput for inputting text into the app via a keyboard.

import React, { Component } from 'react';
import { AppRegistry, TextInput } from 'react-native';

export default class UselessTextInput extends Component {
  constructor(props) {
    super(props);
    this.state = { text: 'Useless Placeholder' };
  }

render() {
    return (
      <TextInput
        style={{height: 40, borderColor: 'gray', borderWidth: 1}}
        onChangeText={(text) => this.setState({text})}
        value={this.state.text}
      />
    );
  }
}

That’s it, whenever we click on the text input, keyboard appears allowing us to enter values. To dismiss the keyboard by pressing anywhere on the screen, the easy solution is to TouchableWithoutFeedback together with Keyboard . This is similar to having UITapGestureRecognizer in iOS UIView and calling view.endEditing

import { Keyboard } from 'react-native'

Keyboard.dismiss()

TextInput inside ScrollView

Normally we should have some text inputs inside a scrolling component, in React Native that is mostly ScrollView to be able to handle long list of content and avoid keyboard. If TextInput is inside ScrollView then the way keyboard gets dismissed behaves a bit differently, and depends on keyboardShouldPersistTaps

Determines when the keyboard should stay visible after a tap.

  • ‘never’ (the default), tapping outside of the focused text input when the keyboard is up dismisses the keyboard. When this happens, children won’t receive the tap.

  • ‘always’, the keyboard will not dismiss automatically, and the scroll view will not catch taps, but children of the scroll view can catch taps.

  • ‘handled’, the keyboard will not dismiss automatically when the tap was handled by a children, (or captured by an ancestor).

The never mode should be the desired behaviour in most cases, clicking anywhere outside the focused text input should dismiss the keyboard.

In my app, there are some text inputs and an action button. The scenario is that users enter some infos and then press that button to register data. With the never mode, we have to press button twice, one for dismissing the keyboard, and two for the onPress of the Button . So the solution is to use always mode. This way the Button always gets the press event first.

<ScrollView keyboardShouldPersistTaps='always' />

ScrollView cares about keyboard

The native RCTScrollView class that power react native ScrollView has code to handle dismiss mode

RCT_SET_AND_PRESERVE_OFFSET(setKeyboardDismissMode, keyboardDismissMode, UIScrollViewKeyboardDismissMode)

The option that it chooses is UIScrollViewKeyboardDismissMode for keyboardDismissMode property

The manner in which the keyboard is dismissed when a drag begins in the scroll view.

As you can see, the possible modes are onDrag and interactive . And react native exposes customization point for this via keyboardShouldPersistTaps

case none The keyboard does not get dismissed with a drag.

case onDrag The keyboard is dismissed when a drag begins.

case interactive The keyboard follows the dragging touch offscreen, and can be pulled upward again to cancel the dismiss.

ScrollView inside a Modal

But that does not work when ScrollView is inside Modal . By Modal I meant the Modal component in React Native. The only library that I use is react-navigation , and it supports Opening a full-screen modal too, but they way we declare modal in react-navigation looks like stack and it is confusing, so I would rather not use it. I use Modal in react-native and that works pretty well.

So if we have TextInput inside ScrollView inside Modal then keyboardShouldPersistTaps does not work. Modal seems to be aware of parent ScrollView so we have to declare keyboardShouldPersistTaps=’always’ on every parent ScrollView . In React Native FlatList and SectionList uses ScrollView under the hood, so we need to be aware of all those ScrollView components.

Spelunking react-navigation

Since my app relies heavily on react-navigation , it’s good to have a deep understanding about its components so we make sure where the problem lies. I’ve written a bit about react-navigation structure below.
Using react-navigation 3.0 in React Native apps
react-navigation is probably the only dependency I use in React Native apps. I’m happy with it so far, then version 3.0…codeburst.io

Like every traditional mobile apps, my app consists of many stack navigators inside tab navigator. In iOS that means many UINavigationViewController inside UITabbarController . In react-navigation I use createMaterialTopTabNavigator inside createBottomTabNavigator

import { createMaterialTopTabNavigator } from 'react-navigation'
import { createBottomTabNavigator, BottomTabBar } from 'react-navigation-tabs'

The screen I have keyboard issue is a Modal presented from the 2nd screen in one of the stack navigators, so let’s examine every possible ScrollView up the hierarchy. This process involves lots of code reading and this’s how I love open source.

First let’s start with createBottomTabNavigator which uses createTabNavigator together with its own TabNavigationView

class TabNavigationView extends React.PureComponent<Props, State>

export default createTabNavigator(TabNavigationView);

Tab navigator has tab bar view below ScreenContainer , which is used to contain view. ScreenContainer is from react-native-screens “This project aims to expose native navigation container components to React Native”. Below is how tab navigator works.

render() {
  const { navigation, renderScene, lazy } = this.props;
  const { routes } = navigation.state;
  const { loaded } = this.state
  return (
    <View style={styles.container}>
      <ScreenContainer style={styles.pages}>
        {routes.map((route, index) => {
          if (lazy && !loaded.includes(index)) {
            // Don't render a screen if we've never navigated to it
            return null;

          const isFocused = navigation.state.index === index
          return (
            <ResourceSavingScene
              key={route.key}
              style={StyleSheet.absoluteFill}
              isVisible={isFocused}
            >
              {renderScene({ route })}
            </ResourceSavingScene>
          );
        })}
      </ScreenContainer>
      {this._renderTabBar()}
    </View>
  );
}

Tab bar is rendered using BottomTabBar in _renderTabBar function. Looking at the code, the whole tab navigator has nothing to do with ScrollView .

So there is only createMaterialTopTabNavigator left on the suspecting list. I use it in the app with swipeEnabled: true . And by looking at the imports, top tab navigator has

import MaterialTopTabBar, {  type TabBarOptions,} from '../views/MaterialTopTabBar';

MaterialTopTabBar has import from react-native-tab-view

import { TabBar } from 'react-native-tab-view';

which has ScrollView

<View style={styles.scroll}>
  <Animated.ScrollView
    horizontal
    keyboardShouldPersistTaps="handled"

The property keyboardShouldPersistTaps was initial set to always , then set back to handled to avoid the bug that we can’t press any button in tab bar while keyboard is open https://github.com/react-native-community/react-native-tab-view/issues/375

But this TabBar has nothing with our problem, because it’s just for containing tab bar buttons.

Swiping in createMaterialTopTabNavigator

Taking another look at createMaterialTopTabNavigator we see more imports from react-native-tab-view

import { TabView, PagerPan } from 'react-native-tab-view';

TabView has swipeEnabled passed in

return (
  <TabView
    {...rest}
    navigationState={navigation.state}
    animationEnabled={animationEnabled}
    swipeEnabled={swipeEnabled}
    onAnimationEnd={this._handleAnimationEnd}
    onIndexChange={this._handleIndexChange}
    onSwipeStart={this._handleSwipeStart}
    renderPager={renderPager}
    renderTabBar={this._renderTabBar}
    renderScene={
      /* $FlowFixMe */
      this._renderScene
    }
  />
);

and it renders PagerDefault, which in turn uses PagerScroll for iOS

import { Platform } from 'react-native';

let Pager;

switch (Platform.OS) {
  case 'android':
    Pager = require('./PagerAndroid').default;
    break;
  case 'ios':
    Pager = require('./PagerScroll').default;
    break;
  default:
    Pager = require('./PagerPan').default;
    break;
}

export default Pager;

So PagerScroll uses ScrollView to handle scrolling to match material style that user can scroll between pages, and it has keyboardShouldPersistTaps=”always” which should be correct.

return (
  <ScrollView
    horizontal
    pagingEnabled
    directionalLockEnabled
    keyboardDismissMode="on-drag"
    keyboardShouldPersistTaps="always"

So nothing looks suspicious in react-navigation , which urges me to look at code from my project.

Debugging FlatList, SectionList and ScrollView

Like I stated in the beginning of this article, the root problem is that we need to declare keyboardShouldPersistTaps for all parent ScrollView in the hierarchy. That means to look out for any FlatList, SectionList and ScrollView

Luckily, there is react-devtools that shows tree of all rendered components in react app, and that is also guided in Debugging section of react native.

You can use the standalone version of React Developer Tools to debug the React component hierarchy. To use it, install the react-devtools package globally:

npm install -g react-devtools

So after searching I found out that there is a SectionList up the hierarchy that should have keyboardShouldPersistTaps=’always’ while it didn’t.

Taking a thorough look at the code, I found out that the Modal is trigged from a SectionList item. We already know that triggering Modal in react native means that to embed that Modal inside the view hierarchy and control its visibility via a state. So in terms of view and component, that Modal is inside a SectionList . And for your interest, if you dive deep into react native code, SectionList in my case is just VirtualizedSectionList , which is VirtualizedList, which uses ScrollView

So after I declare keyboardShouldPersistTaps=’always’ in that SectionList , the problem is solved. User can now just enters some values in the text inputs, then press once on the submit button to submit data. The button now captures touch events first instead of scrollview.

Where to go from here

The solution for this is fortunately simple as it involves fixing our code without having to alter react-navigation code. But it’s good to look at the library code to know what it does, and to trace where the problem originates. Thanks for following such long exploring and hope you learn something.

How to structure your project and manage static resources in React Native

Issue #256

Original post https://medium.freecodecamp.org/how-to-structure-your-project-and-manage-static-resources-in-react-native-6f4cfc947d92


React and React Native are just frameworks, and they do not dictate how we should structure our projects. It all depends on your personal taste and the project you’re working on.

In this post, we will go through how to structure a project and how to manage local assets. This of course is not written in stone, and you are free to apply only the pieces that suit you. Hope you learn something.

For a project bootstrapped with react-native init , we get only the basic structure.

There is the ios folder for Xcode projects, the android folder for Android projects, and an index.js and an App.js file for the React Native starting point.

ios/
android/
index.js
App.js

As someone who has worked with native on both Windows Phone, iOS and Android, I find that structuring a project all comes down to separating files by type or feature

type vs feature

Separating by type means that we organise files by their type. If it is a component, there are container and presentational files. If it is Redux, there are action, reducer, and store files. If it is view, there are JavaScript, HTML, and CSS files.

Group by type

redux
  actions
  store
  reducers
components
  container
  presentational
view
  javascript
  html
  css

This way, we can see the type of each file, and easily run a script toward a certain file type. This is general for all projects, but it does not answer the question “what is this project about?” Is it news application? Is it a loyalty app? Is it about nutrition tracking?

Organising files by type is for a machine, not for a human. Many times we work on a feature, and finding files to fix in multiple directories is a hassle. It’s also a pain if we plan to make a framework out of our project, as files are spread across many places.

Group by feature

A more reasonable solution is to organise files by feature. Files related to a feature should be placed together. And test files should stay close to the source files. Check out this article to learn more.

A feature can be related to login, sign up, onboarding, or a user’s profile. A feature can contain sub-features as long as they belong to the same flow. If we wanted to move the sub feature around, it would be easy, as all related files are already grouped together.

My typical project structure based on features looks like this:

index.js
App.js
ios/
android/
src
  screens
    login
      LoginScreen.js
      LoginNavigator.js
    onboarding
      OnboardingNavigator    
      welcome 
        WelcomeScreen.js
      term
        TermScreen.js
      notification
        NotificationScreen.js
    main
      MainNavigator.js
      news
        NewsScreen.js
      profile
        ProfileScreen.js
      search
        SearchScreen.js
  library
    package.json
    components
      ImageButton.js
      RoundImage.js
    utils
      moveToBottom.js
      safeArea.js
    networking
      API.js
      Auth.js
  res
    package.json
    strings.js
    colors.js
    palette.js
    fonts.js
    images.js
    images
      logo@2x.png
      logo@3x.png
      button@2x.png
      button@3x.png
scripts
  images.js
  clear.js

Besides the traditional files App.js and index.js and the ios1 and android folders, I put all the source files inside the src folder. Inside src I have res for resources, library for common files used across features, and screens for a screen of content.

As few dependencies as possible

Since React Native is heavily dependent on tons of dependencies, I try to be pretty aware when adding more. In my project I use just react-navigation for navigation. And I’m not a fan of redux as it adds unneeded complexity. Only add a dependency when you really need it, otherwise you are just setting yourself up for more trouble than value.

The thing I like about React is the components. A component is where we define view, style and behavior. React has inline style — it’s like using JavaScript to define script, HTML and CSS. This fits the feature approach we are aiming for. That’s why I don’t use styled-components. Since styles are just JavaScript objects, we can just share comment styles in library .

src

I like Android a lot, so I name src and res to match its folder conventions.

react-native init sets up babel for us. But for a typical JavaScript project, it’s good to organise files in the src folder. In my electron.js application IconGenerator, I put the source files inside the src folder. This not only helps in terms of organising, but also helps babel transpile the entire folder at once. Just a command and I have the files in src transpiled to dist in a blink.

babel ./src --out-dir ./dist --copy-files

Screen

React is based around components. Yup. There are container and presentational components, but we can compose components to build more complex components. They usually end in showing in the full screen. It is called Page in Windows Phone, ViewController in iOS and Activity in Android. The React Native guide mentions screen very often as something that covers the entire space:

Mobile apps are rarely made up of a single screen. Managing the presentation of, and transition between, multiple screens is typically handled by what is known as a navigator.

index.js or not?

Each screen is considered the entry point for each feature. You can rename the LoginScreen.js to index.js by leveraging the Node module feature:

Modules don’t have to be files. We can also create a find-me folder under node_modules and place an index.js file in there. The same require(‘find-me’) line will use that folder’s index.js file

So instead of import LoginScreen from ‘./screens/LoginScreen’ , we can just do import LoginScreen from ‘./screens’.

Using index.js results in encapsulation and provides a public interface for the feature. This is all personal taste. I myself prefer explicit naming for a file, hence the name LoginScreen.js.

react-navigation seems to be the most popular choice for handling navigation in a React Native app. For a feature like onboarding, there are probably many screens managed by a stack navigation, so there is OnboardingNavigator .

You can think of Navigator as something that groups sub screens or features. Since we group by feature, it’s reasonable to place Navigator inside the feature folder. It basically looks like this:

import { createStackNavigator } from 'react-navigation'
import Welcome from './Welcome'
import Term from './Term'

const routeConfig = {
  Welcome: {
    screen: Welcome
  },
  Term: {
    screen: Term
  }
}

const navigatorConfig = {
  navigationOptions: {
    header: null
  }
}

export default OnboardingNavigator = createStackNavigator(routeConfig, navigatorConfig)

library

This is the most controversial part of structuring a project. If you don’t like the name library, you can name it utilities, common, citadel , whatever…

This is not meant for homeless files, but it is where we place common utilities and components that are used by many features. Things like atomic components, wrappers, quick fixes function, networking stuff, and login info are used a lot, and it’s hard to move them to a specific feature folder. Sometimes we just need to be practical and get the work done.

In React Native, we often need to implement a button with an image background in many screens. Here is a simple one that stays inside library/components/ImageButton.js . The components folder is for reusable components, sometimes called atomic components. According to React naming conventions, the first letter should be uppercase.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React from 'react'
import { TouchableOpacity, View, Image, Text, StyleSheet } from 'react-native'
import images from 'res/images'
import colors from 'res/colors'

export default class ImageButton extends React.Component {
render() {
return (
<TouchableOpacity style={styles.touchable} onPress={this.props.onPress}>
<View style={styles.view}>
<Text style={styles.text}>{this.props.title}</Text>
</View>
<Image
source={images.button}
style={styles.image} />
</TouchableOpacity>
)
}
}

const styles = StyleSheet.create({
view: {
position: 'absolute',
backgroundColor: 'transparent'
},
image: {

},
touchable: {
alignItems: 'center',
justifyContent: 'center'
},
text: {
color: colors.button,
fontSize: 18,
textAlign: 'center'
}
})

And if we want to place the button at the bottom, we use a utility function to prevent code duplication. Here is library/utils/moveToBottom.js:

import React from 'react'
import { View, StyleSheet } from 'react-native'

function moveToBottom(component) {
  return (
    <View style={styles.container}>
      {component}
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'flex-end',
    marginBottom: 36
  }
})

export default moveToBottom

Use package.json to avoid relative path

Then somewhere in the src/screens/onboarding/term/Term.js , we can import by using relative paths:

import moveToBottom from '../../../../library/utils/move'
import ImageButton from '../../../../library/components/ImageButton'

This is a big red flag in my eyes. It’s error prone, as we need to calculate how many .. we need to perform. And if we move feature around, all of the paths need to be recalculated.

Since library is meant to be used many places, it’s good to reference it as an absolute path. In JavaScript there are usually 1000 libraries to a single problem. A quick search on Google reveals tons of libraries to tackle this issue. But we don’t need another dependency as this is extremely easy to fix.

The solution is to turn library into a module so node can find it. Adding package.json to any folder makes it into a Node module . Add package.json inside the library folder with this simple content:

{
  "name": "library",
  "version": "0.0.1"
}

Now in Term.js we can easily import things from library because it is now a module:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

import React from 'react'
import { View, StyleSheet, Image, Text, Button } from 'react-native'
import strings from 'res/strings'
import palette from 'res/palette'
import images from 'res/images'
import ImageButton from 'library/components/ImageButton'
import moveToBottom from 'library/utils/moveToBottom'

export default class Term extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.heading}>{strings.onboarding.term.heading.toUpperCase()}</Text>
{
moveToBottom(
<ImageButton style={styles.button} title={strings.onboarding.term.button.toUpperCase()} />
)
}
</View>
)
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center'
},
heading: {
...palette.heading, ...{
marginTop: 72
}
}
})

res

You may wonder what res/colors, res/strings , res/images and res/fonts are in the above examples. Well, for front end projects, we usually have components and style them using fonts, localised strings, colors, images and styles. JavaScript is a very dynamic language, and it’s easy to use stringly types everywhere. We could have a bunch of #00B75D color across many files, or Fira as a fontFamily in many Text components. This is error-prone and hard to refactor.

Let’s encapsulate resource usage inside the res folder with safer objects. They look like the examples below:

res/colors

const colors = {
  title: '#00B75D',
  text: '#0C222B',
  button: '#036675'
}

export default colors

res/strings

const strings = {
  onboarding: {
    welcome: {
      heading: 'Welcome',
      text1: "What you don't know is what you haven't learn",
      text2: 'Visit my GitHub at [https://github.com/onmyway133'](https://github.com/onmyway133'),
      button: 'Log in'
    },
    term: {
      heading: 'Terms and conditions',
      button: 'Read'
    }
  }
}

export default strings

res/fonts

const fonts = {
  title: 'Arial',
  text: 'SanFrancisco',
  code: 'Fira'
}

export default fonts

res/images

const images = {
  button: require('./images/button.png'),
  logo: require('./images/logo.png'),
  placeholder: require('./images/placeholder.png')
}

export default images

Like library , res files can be access from anywhere, so let’s make it a module . Add package.json to the res folder:

{
  "name": "res",
  "version": "0.0.1"
}

so we can access resource files like normal modules:

import strings from 'res/strings'
import palette from 'res/palette'
import images from 'res/images'

Group colors, images, fonts with palette

The design of the app should be consistent. Certain elements should have the same look and feel so they don’t confuse the user. For example, the heading Text should use one color, font, and font size. The Image component should use the same placeholder image. In React Native, we already use the name styles with const styles = StyleSheet.create({}) so let’s use the name palette.

Below is my simple palette. It defines common styles for heading and Text:

res/palette

import colors from './colors'

const palette = {
  heading: {
    color: colors.title,
    fontSize: 20,
    textAlign: 'center'
  },
  text: {
    color: colors.text,
    fontSize: 17,
    textAlign: 'center'
  }
}

export default palette

And then we can use them in our screen:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center'
  },
  heading: {...palette.heading, ...{
    marginTop: 72
  }}
})

Here we use the object spread operator to merge palette.heading and our custom style object. This means that we use the styles from palette.heading but also specify more properties.

If we were to reskin the app for multiple brands, we could have multiple palettes. This is a really powerful pattern.

Generate images

You can see that in /src/res/images.js we have properties for each image in the src/res/images folder:

const images = {
  button: require('./images/button.png'),
  logo: require('./images/logo.png'),
  placeholder: require('./images/placeholder.png')
}

export default images

This is tedious to do manually, and we have to update if there’s changes in image naming convention. Instead, we can add a script to generate the images.js based on the images we have. Add a file at the root of the project /scripts/images.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const fs = require('fs')

const imageFileNames = () => {
const array = fs
.readdirSync('src/res/images')
.filter((file) => {
return file.endsWith('.png')
})
.map((file) => {
return file.replace('[@2x](http://twitter.com/2x).png', '').replace('[@3x](http://twitter.com/3x).png', '')
})

return Array.from(new Set(array))
}

const generate = () => {
let properties = imageFileNames()
.map((name) => {
return `${name}: require('./images/${name}.png')`
})
.join(',\n ')

const string = `const images = {
${properties}
}

export default images
`

fs.writeFileSync('src/res/images.js', string, 'utf8')
}

generate()

The cool thing about Node is that we have access to the fs module, which is really good at file processing. Here we simply traverse through images, and update /src/res/images.js accordingly.

Whenever we add or change images, we can run:

node scripts/images.js

And we can also declare the script inside our main package.json :

"scripts": {
  "start": "node node_modules/react-native/local-cli/cli.js start",
  "test": "jest",
  "lint": "eslint *.js **/*.js",
  "images": "node scripts/images.js"
}

Now we can just run npm run images and we get an up-to-date images.js resource file.

How about custom fonts

React Native has some custom fonts that may be good enough for your projects. You can also use custom fonts.

One thing to note is that Android uses the name of the font file, but iOS uses the full name. You can see the full name in Font Book app, or by inspecting in running app

for (NSString* family in [UIFont familyNames]) {
  NSLog(@"%@", family);

for (NSString* name in [UIFont fontNamesForFamilyName: family]) {
    NSLog(@"Family name:  %@", name);
  }
}

For custom fonts to be registered in iOS, we need to declare UIAppFonts in Info.plist using the file name of the fonts, and for Android, the fonts need to be placed at app/src/main/assets/fonts .

It is good practice to name the font file the same as full name. React Native is said to dynamically load custom fonts, but in case you get “Unrecognized font family”, then simply add those fonts to target within Xcode.

Doing this by hand takes time, luckily we have rnpm that can help. First add all the fonts inside res/fonts folder. Then simply declare rnpm in package.json and run react-native link . This should declare UIAppFonts in iOS and move all the fonts into app/src/main/assets/fonts for Android.

"rnpm": {
  "assets": [
    "./src/res/fonts/"
  ]
}

Accessing fonts by name is error prone, we can create a script similar to what we have done with images to generate a safer accession. Add fonts.js to our scripts folder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const fs = require('fs')

const fontFileNames = () => {
const array = fs
.readdirSync('src/res/fonts')
.map((file) => {
return file.replace('.ttf', '')
})

return Array.from(new Set(array))
}

const generate = () => {
const properties = fontFileNames()
.map((name) => {
const key = name.replace(/\s/g, '')
return `${key}: '${name}'`
})
.join(',\n ')

const string = `const fonts = {
${properties}
}

export default fonts
`

fs.writeFileSync('src/res/fonts.js', string, 'utf8')
}

generate()

Now you can use custom font via R namespace.

1
2
3
4
5
6
7
import R from 'res/R'

const styles = StyleSheet.create({
text: {
fontFamily: R.fonts.FireCodeNormal
}
})

The R namespace

This step depends on personal taste, but I find it more organised if we introduce the R namespace, just like how Android does for assets with the generated R class.

Once you externalize your app resources, you can access them using resource IDs that are generated in your project’s Rclass. This document shows you how to group your resources in your Android project and provide alternative resources for specific device configurations, and then access them from your app code or other XML files.

This way, let’s make a file called R.js in src/res:

import strings from './strings'
import images from './images'
import colors from './colors'
import palette from './palette'

const R = {
  strings,
  images,
  colors,
  palette
}

export default R

And access it in the screen:

1
2
3
4
5
6
7
8
9
10
11
12
13
import R from 'res/R'
render() {
return (
<SafeAreaView style={styles.container}>
<Image
style={styles.logo}
source={R.images.logo} />
<Image
style={styles.image}
source={R.images.placeholder} />
<Text style={styles.title}>{R.strings.onboarding.welcome.title.toUpperCase()}</Text>
)
}

Replace strings with R.strings, colors with R.colors, and images with R.images. With the R annotation, it is clear that we are accessing static assets from the app bundle.

This also matches the Airbnb convention for singleton, as our R is now like a global constant.

23.8 Use PascalCase when you export a constructor / class / singleton / function library / bare object.

const AirbnbStyleGuide = {
  es6: {
  },
}

export default AirbnbStyleGuide

Where to go from here

In this post, I’ve shown you how I think you should structure folders and files in a React Native project. We’ve also learned how to manage resources and access them in a safer manner. I hope you’ve found it useful. Here are some more resources to explore further:

How to overlay view on another view in React Native

Issue #254

Original post https://stackoverflow.com/a/54108708/1418457


Make our own convenient OverlayContainer. The trick is to use absolute with 100% size

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// @flow

import React from 'react'
import { View, StyleSheet } from 'react-native'

type Props = {
behind: React.Component,
front: React.Component,
under: React.Component
}

// Show something on top of other
export default class OverlayContainer extends React.Component<Props> {
render() {
const { behind, front, under } = this.props

return (
<View style={styles.container}>
<View style={styles.center}>
<View style={styles.behind}>
{behind}
</View>
{front}
</View>
{under}
</View>
)
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
height: 100,
justifyContent: 'center',
},
center: {
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
},
behind: {
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%'
}
})

How to remove Cartography in iOS

Issue #252

Read more https://medium.com/flawless-app-stories/how-to-make-auto-layout-more-convenient-in-ios-df3b42fed37f

Description

This is a script to remove Cartography, and use plain NSLayoutAnchor syntax.
Use Constraint.on() from Sugar.
It will change all .swift files recursively under provided folder.

1
2
3
4
Constraint.on(
logoImageView.widthAnchor.constraint(equalToConstant: 74),
view1.leftAnchor.constraint(equalTo: view2.leftAnchor, constant: 20),
)

Features

  • Parse recursively
  • Parse a single file
  • Parse each constrain block separately
  • Handle ==, >=, <=
  • Handle center, edges
  • Infer indentation
  • Handle relation with other item
  • Handle relation with constant
  • Handle multiplier
  • Auto import Sugar
  • Infer superView

Prepare

Install tool if needed

1
brew install yarn

How to use

1
2
yarn install
yarn start /Users/khoa/path/to/project

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

const Glob = require('glob')
const Fs = require('fs')

function main() {
const projectPath = getProjectPath()
const files = getAllFiles(projectPath)

files.forEach((file) => {
handleFile(file)
})
}

/// The 1st argument is node, 2nd is index.js, 3rd is path
function getProjectPath() {
return process.argv[2]
}

/// Only select `swift` file
function getAllFiles(projectPath) {
if (projectPath.endsWith('.swift')) {
return [projectPath]
} else {
const files = Glob.sync(`${projectPath}/**/*.swift`)
return files
}
}

/// Read file, replace by matches, and write again
function handleFile(file) {
console.log(file)

Fs.readFile(file, 'utf8', (error, data) => {
// non greedy `*?`
const pattern = '(.)*constrain\\(.+\\) {\\s(\\s|.)*?\\n(\\s)*}'
const regex = new RegExp(pattern)

let matches = data.match(regex)

// RegEx only return the 1st match per execution, let's do a recursion
while (matches != null) {
const match = matches[0]

const indentationLength = findIndentationLength(match)
const rawTransforms = handleMatch(match)
const transforms = handleSuperview(rawTransforms, match)
const statements = handleTransforms(transforms, indentationLength)
const string = handleStatements(statements, indentationLength)
data = data.replace(match, string)

// Examine again
matches = data.match(regex)
}

// Handle import
data = handleImport(data)


Fs.writeFile(file, data, 'utf8')
})
}

/// Replace or remove import
function handleImport(data) {
if (data.includes('import Sugar')) {
return data.replace('import Cartography', '')
} else {
return data.replace('import Cartography', 'import Sugar')
}
}

/// Format transform to have `,` and linebreaks
function handleTransforms(transforms, indentationLength) {
let string = ''

// Expect the first is always line break
if (transforms[0] != '\n') {
transforms.unshift('\n')
}

const indentation = makeIndentation(indentationLength + 2)

transforms.forEach((line, index) => {
if (line.length > 1) {
string = string.concat(indentation + line)
if (index < transforms.length - 1) {
string = string.concat(',\n')
}
} else {
string = string.concat('\n')
}
})

return string
}

/// Replace superView
function handleSuperview(transforms, match) {
const superView = findSuperview(match)
if (superView == null) {
return transforms
}

const superViewPattern = ' superView.'
transforms = transforms.map((transform) => {
if (transform.includes(superViewPattern)) {
return transform.replace(superViewPattern, ` ${superView}.`)
} else {
return transform
}
})

return transforms
}

/// Embed the statements inside `Constraint.on`
function handleStatements(statements, indentationLength) {
const indentation = makeIndentation(indentationLength)
return `${indentation}Constraint.on(${statements}\n${indentation})`
}

/// Turn every line into flatten transformed line
function handleMatch(match) {
let lines = match.split('\n')
lines = lines.map((line) => {
return handleLine(line.trim())
}).filter((transforms) => {
return transforms != null
})

let flatten = [].concat.apply([], lines)
return flatten
}

/// Check to handle lines, turn them into `LayoutAnchor` statements
function handleLine(line) {
const itemPattern = '\\w*\\.\\w* (=|<|>)= \\w*\\.*\\..*'
const sizePattern = '\w*\.\w* (=|<|>)= (\s|.)*'

if (line.includes('edges == ')) {
return handleEdges(line)
} else if (line.includes('center == ')) {
return handleCenter(line)
} else if (hasPattern(line, itemPattern) && !hasSizeKeywords(line)) {
return handleItem(line)
} else if (hasPattern(line, sizePattern)) {
return handleSize(line)
} else if (line.includes('>=') || line.includes('>=')) {
return line
} else if (line.includes('.')) {
// return the line itself to let the human fix
return line
} else if (line.length == 0) {
return ['\n']
} else {
return null
}
}

/// For ex: listView.bottom == listView.superview!.bottom - 43
/// listView.bottom == listView.superview!.bottom - Metrics.BackButtonWidth
function handleItem(line) {
const equalSign = getEqualSign(line)
const parts = line.split(` ${equalSign} `)
const left = parts[0]

let rightParts = parts[1].trim()
let right = rightParts.split(' ')[0]
let number = rightParts.replace(right, '').replace('- ', '-').trim()
if (number.startsWith('+ ')) {
number = number.slice(2)
}

let equal = getEqual(line)

if (number == null || number.length == 0 ) {
return [
`${left}Anchor.constraint(${equal}: ${right}Anchor)`
]
} else {
return [
`${left}Anchor.constraint(${equal}: ${right}Anchor, constant: ${number})`
]
}
}

/// For ex: segmentedControl.height == 24
/// backButton.width == Metrics.BackButtonWidth
function handleSize(line) {
const equalSign = getEqualSign(line)
const parts = line.split(` ${equalSign} `)
const left = parts[0]
const right = parts[1]

let equal = getEqual(line)

return [
`${left}Anchor.constraint(${equal}Constant: ${right})`
]
}

/// For ex: mapView.edges == listView.edges
function handleEdges(line) {
const parts = line.split(' == ')
const left = parts[0].split('.')[0]
const right = removeLastPart(parts[1])

return [
`${left}.topAnchor.constraint(equalTo: ${right}.topAnchor)`,
`${left}.bottomAnchor.constraint(equalTo: ${right}.bottomAnchor)`,
`${left}.leftAnchor.constraint(equalTo: ${right}.leftAnchor)`,
`${left}.rightAnchor.constraint(equalTo: ${right}.rightAnchor)`
]
}

/// For ex: mapView.center == listView.center
function handleCenter(line) {
const parts = line.split(' == ')
const left = parts[0].split('.')[0]
const right = removeLastPart(parts[1])

return [
`${left}.centerXAnchor.constraint(equalTo: ${right}.centerXAnchor)`,
`${left}.centerYAnchor.constraint(equalTo: ${right}.centerYAnchor)`
]
}

function hasPattern(string, pattern) {
const regex = new RegExp(pattern)
let matches = string.match(regex)

if (matches == null) {
return false
}

matches = matches.filter((match) => {
return match !== undefined && match.length > 1
})

return matches.length > 0
}

function removeLastPart(string) {
const parts = string.split('.')
parts.pop()

return parts.join('.')
}

function getEqual(line) {
if (line.includes('==')) {
return 'equalTo'
} else if (line.includes('<=')) {
return 'lessThanOrEqualTo'
} else {
return 'greaterThanOrEqualTo'
}
}

function getEqualSign(line) {
if (line.includes('==')) {
return '=='
} else if (line.includes('>=')) {
return '>='
} else {
return '<='
}
}

function findIndentationLength(match) {
return match.split('constrain')[0].length + 1
}

function makeIndentation(length) {
return Array(length).join(' ')
}

// For ex: constrain(tableView, headerView, view) { tableView, headerView, superView in
function findSuperview(match) {
const line = match.split('\n')[0]
if (!line.includes(' superView in')) {
return null
}

const pattern = ', \\w*\\)'
const regex = RegExp(pattern)
const string = line.match(regex)[0] // `, view)

return string.replace(', ', '').replace(')', '')
}

// Check special size keywords
function hasSizeKeywords(line) {
const keywords = ['Metrics', 'Dimensions', 'UIScreen']
return keywords.filter((keyword) => {
return line.includes(keyword)
}).length != 0
}

main()

How to sort strings with number in Javascript

Issue #251

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function sort() {
const string =
`
- Favorite WWDC 2017 sessions https://github.com/onmyway133/blog/issues/56
- Favorite WWDC 2018 sessions https://github.com/onmyway133/blog/issues/245
- How to do clustering with Google Maps in iOS https://github.com/onmyway133/blog/issues/191
`

const lines = string
.split('\n')
.filter((line) => { return line.length > 0 })
.map((line) => {
let parts = line.trimEnd().split(' ')
let lastPart = parts[parts.length-1]
let number = lastPart.replace('https://github.com/onmyway133/blog/issues/', '')
return {
line,
number: parseInt(number)
}
})

lines.sort((a, b) => {
return (a.number < b.number) ? -1 : 1
})

const sortedString = lines
.map((tuple) => {
return tuple.line
})
.join('\n')

console.log(sortedString)
}

Then node index.js

How to catch error in ApolloClient

Issue #192

Read https://www.apollographql.com/docs/react/features/error-handling
How to catch actual error https://github.com/apollographql/apollo-client/issues/4016 🤔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import { Observable } from 'apollo-link'
import ApolloClient from 'apollo-boost'
import Token from 'library/models/Token'
import TokenService from './TokenService'
import { TokenRefreshException } from 'library/utils/Exception'

const client = new ApolloClient({
uri: 'https://www.myapp.no/api/',
request: async (operation) => {
const token = await TokenService.loadToken()
updateOperation(operation, token)
},
onError: (error) => {
console.log('ApolloClient error', error)
if (isAuthError(error)) {
return handleAuthError(error)
} else {
return error.forward(error.operation)
}
},
fetchOptions: {
mode: 'no-cors',
}
})

const isValidErrorCode = (statusCode) => {
if (typeof (statusCode) === 'undefined' || statusCode === null) {
return false
} else {
return true
}
}

const isAuthError = (error) => {
console.log('client error', error)
if (error.networkError) {
return error.networkError.statusCode === 401
} else {
return false
}
}

const handleAuthError = (error) => {
return new Observable((observer) => {
TokenService.refreshToken()
.then((token) => {
updateOperation(error.operation, token)
})
.then(() => {
const subscriber = {
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer)
}

error.forward(error.operation).subscribe(subscriber)
})
.catch((e) => {
observer.error(e)
})
})
}

const updateOperation = (operation, token: Token) => {
const tokenHeader = `${token.token_type} ${token.access_token}`

operation.setContext((context) => {
return {
headers: {
authorization: tokenHeader,
'Content-Type': 'application/json'
}
}
})
}

Curry in Swift and Javascript

Issue #185

You may encounter curry in everyday code without knowing it. Here is a bit of my reflections on curry and how to apply it in Javascript and Swift.

Taking one parameter in Haskell

In Haskell, all function officially takes only 1 parameter. Function with many parameters are just curried, which means they will be partially applied. Calling sum 1 just returns a function with 1 parameter, then 2is passed into this function. The following 2 function calls are the same.

ghci> sum 1 2
3 
ghci> (max 1) 2
3

I tend to think of curried function or partially applied function as something that carry dependencies at each application step. Each curried function can be assigned to variable or pass around, or as returned value.

Curry in Swift for predicate

When I was trying to make my own Signal library, I have

and Event

filter

Then there should be a filter for Signal. The idea of filter is that we should update signal if the Event is Next with right filtered value

public func filter(f: T -> Bool) -> Signal<T>{
    let signal = Signal<T>()
    subscribe { result in
        switch(result) {
        case let .Success(value):
            if f(value) {
                signal.update(result)
            }
        case let .Error(error): signal.update(.Error(error))
        }
    }
    return signal
}

2 parameters

But having Event as another monad, I think it should be more encapsulated if that switching logic gets moved into the Event. Here the filter takes 2 params

Event.swift

func filter(f: T -> Bool, callback: (Event<T> -> Void)) {
        switch self {
        case let .Next(value) where f(value):
            callback(self)
        case .Failed:
            callback(self)
        default:
            break
    }
}

Signal.swift

public func filter(f: T -> Bool) -> Signal<T> {
    let signal = Signal<T>()

    subscribe { event in
        event.filter(f, callback: signal.update)
    }

    return signal
}

Currying

With currying, we can make filter a more abstract function, and defer the decision to pass the callback param. It is a little carried away but I find it helpful this way

Now filter accepts 1 param, and it returns a function that takes callback as its param

Event.swift

func filter(f: T -> Bool) -> ((Event<T> -> Void) -> Void) {
        return { g in
            switch self {
            case let .Next(value) where f(value):
                g(self)
            case .Failed:
                g(self)
            default:
                break
            }
        }
    }

Signal.swift

public func filter(f: T -> Bool) -> Signal<T> {
        let signal = Signal<T>()

        subscribe { event in
            event.filter(f)(signal.update)
        }

        return signal
    }

Curry syntax in Swift 2 and above

Swift 2 supports curry syntax function

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

let sumWith5 = sum(5)
let result = sumWith5(b: 10)

Unfortunately, the syntactic sugar for declaring curry has been dropped since Swift 3. You may want to find out in Bidding farewell to currying. But it’s not a big deal as we can easily create curry function. It is just a function that returns another function.

Using curry for partial application in UIKit

I used this curry technique in my Xkcd app. See MainController.swift. MainController is vanilla UITabBarController with ComicsController and FavoriteController , all embedded in UINavigationViewController .

The feature is that when a comic is selected, a comic detail screen should be pushed on top of the navigation stack. For example in ComicsController

/// Called when a comic is selected  
var selectComic: ((Comic) -> Void)?

All ComicsController needs to know is to call that selectComic closure with the chosen Comic, and someone should know how to handle that selection. Back to the handleFlow function inside MainController.

private func handleFlow() {
  typealias Function = (UINavigationController) -> (Comic) -> Void
  let selectComic: Function = { [weak self] navigationController in
    return { (comic: Comic) in
      guard let self = self else {
        return
      }

  let detailController = self.makeDetail(comic: comic)
      navigationController.pushViewController(detailController, animated: true)
    }
  }

  comicsController.selectComic = selectComic(comicNavigationController)
  favoriteController.selectComic = selectComic(favoriteNavigationController)
}

I declared Function as typealias to explicitly state the curry function that we are going to build

typealias Function = (UINavigationController) -> (Comic) -> Void

We build selectComic as curried function, that takes UINavigationViewController and returns a function that takes Comic and returns Void . This way when we partially apply selectComic with the a navigationController , we get another function that has navigationController as dependency, and ready to be assigned to selectComic property in comicsController .

Curry promised function in Javascript

I like to work with Promise and async/await in Javascript. It allows chainable style and easy to reason about. So when working with callbacks in Javascript, for example callback from native modules in React Native, I tend to convert them into Promise.

For example when working with HealthKit, we need to expose a native modules around it

// [@flow](http://twitter.com/flow)

import { NativeModules } from 'react-native'

type HealthManagerType = {
  checkAuthorisation: ((string) => void)) => void,
  authorise: ((boolean) => void)) => void,
  readWorkout: (Date, Date, () => void)) => void,
  readDailySummary: (Date, Date, () => void)) => void,
  readMeasurement: (Date, Date, () => void)) => void
}

const HealthManager: HealthManagerType = NativeModules.HealthManager
export default HealthManager

We can build a toPromise function that can convert a function with callback into Promise

// [@flow](http://twitter.com/flow)

const toPromise = (f: (any) => void) => {
  return new Promise<any>((resolve, reject) => {
    try {
      f((result) => {
        resolve(result)
      })
    } catch (e) {
      reject(e)
    }
  })
}

export default toPromise

However, as you can see in the signature, it only works with a callback of type (any) => void In other words, this callback must have exactly 1 parameter, because a Promise can either returns a value or throws an error.

To remedy this, we can build a curry function that can turns function with either 1, 2, 3 parameters into curried function. Thanks to the dynamic nature of Javascript, we have

// [@flow](http://twitter.com/flow)

function curry0(f: () => void) {
  return f()
}

function curry1(f: (any) => void) {
  return (p1: any) => {
    return f(p1)
  }
}

function curry2(f: (any, any) => void) {
  return (p1: any) => {
    return (p2: any) => {
      return f(p1, p2)
    }
  }
}

function curry3(f: (any, any, any) => void) {
  return (p1: any) => {
    return (p2: any) => {
      return (p3: any) => {
        return f(p1, p2, p3)
      }
    }
  }
}

export default {
  curry0,
  curry1,
  curry2,
  curry3
}

So with a function that have 3 parameters, we can use curry3 to partially apply the first 2 parameters. Then we have a function that accepts just a callback, and this is turned into Promise via toPromise

const readWorkout = curry.curry3(HealthManager.readWorkout)(DateUtils.startDate))(DateUtils.endDate))

const workouts = await toPromise(readWorkout)

Where to go from here

Here are some of my favorite posts to read more about curry