Setup React-Native for iOS and Android on Mac with VS Code

The very first step to start development in React-Native is to install the right tools to setup the environment correctly. I’m working on MacOS Catalina and I already have Xcode 11 installed on my machine which is one of the pre-requisites to run the iOS App.

Install the following dependencies for React-Native:

NodeJS: https://nodejs.org/en/download/

The recommended way to install Node is using Homebrew from your Terminal:

brew install node

Watchman: In react-native, Watchman watches the source code for any changes and rebuilds them.

brew install watchman

Install XCode from App Store.

Cocoapods: This is the Dependency Manager for iOS & Mac projects.

sudo gem install cocoapods

This requires you to enter system password.

Install Visual Studio Code for Mac from here. You might face issues on Mac OS Catalina to run VS Code due to Notarization requirements. You can check out this post for more details.

Create the folder inside say your Documents folder on your Mac as below:

mkdir Apps

Open VS Code and open New Terminal and type the following command to create a new React-Native App:

react-native init MyApp
cd MyApp
cd ios
pod install

Come back to the MyApp folder and run the below command to run your App in the iOS simulator:

react-native run-ios

This will install and launch the App in your iOS simulator. The first time might be very slow. It might be helpful to launch your simulator beforehand.

Running on Android:

Install the Android Studio from here, which will install the Android SDK and the required emulator. I’m using the Android 10 emulator for Pixel XL.

react-native run-android

Open the MyApp folder in VS Code to make further changes.

Issues

Running the Android App causes issues while launching the Emulator. The error message is: React Native adb reverse ENOENT

Starting with macOS Catalina, your Mac uses zsh as the default login shell and interactive shell. You can make zsh the default in earlier versions of macOS as well.

So on your Mac:

1 – Open your .zshrc file:

open ~/.zshrc

2 – if .zshrc file doesn’t exist, you need to create one & open again(Step 1)

touch ~/.zshrc

3 – Add this to your .zshrc file

export ANDROID_HOME=/Users/<username>/Library/Android/sdk
export PATH=/Users/<username>/Library/Android/sdk/platform-tools:$PATH

4 – Save and close

5 – Compile your changes

source ~/.zshrc

& make sure to restart your terminal.

Run the command in the Terminal and you’ll see something like below if everything is fine:

adb

Android Debug Bridge version 1.0.41

Version 30.0.0-6374843

Installed as /Users/<username>/Library/Android/sdk/platform-tools/adb

You can also run the code in your VS Code Terminal and see the same output.

The path to Android SDK is available in the Configure button -> SDK Manager (at the bottom right) when you launch Android Studio.

Install uninstall cocoapods in your iOS Swift app

Cocoapods are third-party libraries that help you do some of the work in your app for you saving your time. These Cocoapods are built with Ruby and is installable with the built-in Ruby on your Mac OS. Search for your required Cocoapod here.

First install on your macOS using the below command in your Mac terminal:

$ sudo gem install cocoapods

Your installed gems list may be a lot longer than shown above as I’ve already installed before.

Next, setup pods with:

$ pods setup --verbose

–verbose is optional, it’ll just show you the progress of the setup.

Once, the setup is completed, let’s install the individual cocoapods. Navigate to the folder of your Project and create the pod file. And then open it for editing with the following commands:

$ cd myapp

$ pod init

$ open -a Xcode podfile

Make changes to your podfile similar to shown above after uncommenting/removing a few lines. The first line tells the minimum supported iOS platform. The general rule of the thumb is to support the last 2 iOS releases. The target is the name of your project.

use_frameworks is required for Swift Projects. The provide the list of pods inside the do block which are required for your app. Make sure the name is correct as selected from the Cocoapods.org site. Please do check if the pods you’re using is well maintained and when was the last time it was updated. Should not be like last updated 4-5 years ago is not a good sign.

Once your changes are done, save the file and run the below command while within the app folder:

$ pod install

From now on, you would want to open your project in Xcode using .xcworkspace in your Project folder instead of .xcodeproj.

To use the pod in your project, just import the pod using the import statement in your class and you can use it as per the documentation e.g.

import VENTokenField
import SVProgressHUD

To uninstall and clean your Project completely from CocoaPods, run the following commands:

$ sudo gem install cocoapods-deintegrate cocoapods-clean
$ pod deintegrate && pod clean
$ rm Podfile

Add placeholder to UITextView Swift

There are instances where we need to use UITextView as a multi-line text field in our app. The UITextView does not provide a placeholder property like the UITextField. So, in order to achieve this we need to implement the following methods in our UIViewController:

class DemoVC: UIViewController, UITextViewDelegate {

//hook this to the storyboard UITextView
@IBOutlet weak var descriptionTxtView: UITextView!

func textViewDidBeginEditing(_ textView: UITextView) {
        if descriptionTxtView.text == "Add description..." {
            descriptionTxtView.text = ""
            descriptionTxtView.textColor = UIColor.black
        }
    }
    
    func textViewDidEndEditing(_ textView: UITextView) {
        if descriptionTxtView.text == "" {
            descriptionTxtView.text = "Add description..."
            descriptionTxtView.textColor = #colorLiteral(red: 0.4922404289, green: 0.7722371817, blue: 0.4631441236, alpha: 1)
        }
    }

}

Please note in the above code that the View Controller class also conforms to the UITextViewDelegate. All of the methods in the protocol are optional, however use them to adjust the text as needed.

The above code is implemented using Swift 4.

Add and delete rows in UITableView Swift

In this post, we’ll discuss the scenario where we need to add or remove rows from a UITableView in Swift. Below are the parts of code required in the ViewController to achieve this.

First, we need to create the Outlet for the UITableView:

class NewRecipeVC: UIViewController, UITableViewDelegate, UITableViewDataSource {

// don't forget to hook this up from the storyboard
@IBOutlet weak var newRecipeTableView: UITableView!

//hook this to the textbox to add the ingredient
@IBOutlet weak var ingredientTxt: UITextField!

//This array holds the ingredients
var ingredients = ["1 slice lemon","1 table spoon Sugar","1 glass Water","1 spoon Rock Salt"]

//hook this to a button
@IBAction func addIngredient(_ sender: Any) {
        if ingredientTxt.text != "" {
            ingredients.append(ingredientTxt.text!)
        
            let indexPath = IndexPath(row: ingredients.count - 1, section: 0)
            newRecipeTableView.beginUpdates()
            newRecipeTableView.insertRows(at: [indexPath], with: .automatic)
            newRecipeTableView.endUpdates()
        
            ingredientTxt.text = ""
            view.endEditing(true)

        }
        else {
            let alert = UIAlertController(title: "Missing!", message: "Missing ingredient...", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
    }

}

Deleting a row requires the commit editing style method as shown below. This method allows you to right to left swipe on the row and get the delete button option.

extension NewRecipeVC {    

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        
        if editingStyle == .delete {
            ingredients.remove(at: indexPath.row)
            newRecipeTableView.beginUpdates()
            newRecipeTableView.deleteRows(at: [indexPath], with: .automatic)
            newRecipeTableView.endUpdates()
        }
    }

}

Below are some methods that are used and are defined as per Apple documentation:

beginUpdates(): Begins a series of method calls that insert, delete, or select rows and sections of the table view.

insertRows(): Inserts rows in the table view at the locations identified by an array of index paths, with an option to animate the insertion.

deleteRows(): Deletes the rows specified by an array of index paths, with an option to animate the deletion.

endUpdates(): Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view.

Implement follow unfollow button with mutating function Swift

To modify the properties of a value type, you have to use the mutating keyword in the instance method. This keyword gives your method can then have the ability to mutate the values of the properties and write it back to the original structure when the method implementation ends.

Add follow button as shown in the UIViewController below inside the UITableView Prototype cell.

Add Protocol Togglable.swift:

protocol Togglable {
    mutating func toggle()
}

Add FollowStatus.swift class with the below code that implements Togglable Protocol:

enum FollowStatus: Togglable {
    case follow, unfollow
    
    mutating func toggle() {
        switch self {
            case .follow:
                self = .unfollow
            case .unfollow:
                self = .follow
        }
    }
}

Inside the UITableViewCell class, add the follow Action method:

class TableViewCellClass: UITableViewCell {
    @IBOutlet weak var followBtn: UIButton!
    var followStatus: FollowStatus = .follow

    @IBAction func followBtnTapped(_ sender: Any) {        
        followStatus.toggle()
        if followStatus == .unfollow{
            followBtn?.setTitle("unfollow",for: .normal)
        }
        else {
            followBtn?.setTitle("follow",for: .normal)
        }
    }
}

Run the App and test clicking the button to see the status of the button changing between follow/unfollow.

Using Xcode version 10.1.

Implement Safe area guidelines for iPhone X and above

I recently faced a very common problem of supporting the App interface to the new iPhones X and above. The top label in the App was hiding behind the status bar not adapting properly to the interface of the iPhone X.

Safe area layout guide provided by Apple is a no coding solution to this problem. Select the Storyboard and on the File Inspector, check the “Use Safe Area Layout Guides” checkbox.

Next, select the label from the Storyboard and edit the top alignment constraint by changing the second item to Safe Area instead of Super View.

Running the App, will make the Top label appear below the status bar on the iPhone X and above as shown below.

Using Xcode version 10.1.

Add UIPickerView to ViewController in Swift

  1. Drag-drop UIPickerView component on to the View Controller.

    2. Create a Swift class and add it to the ViewController.

     3. Add the below code to the class:

 Control-drag the reference for the UIPickerView in the PickerVC class.
class PickerVC: UIViewController {
    @IBOutlet weak var categoryPicker: UIPickerView!
    var categories = ["Indian", "Mexican", "American", "Chinese"]

    override func viewDidLoad() {
        super.viewDidLoad()
        //Set the ViewController as the DataSource and Delegate    for the Category Picker.
        categoryPicker.dataSource = self
        categoryPicker.delegate = self
    }
}

Implement the below functions for UIPickerViewDataSource and UIPickerViewDelegate
extension PickerVC: UIPickerViewDataSource, UIPickerViewDelegate {
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return categories.count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return categories[row]
    }
}

Using Xcode Version 10.1