プログラム関係の備忘録。技術系記事1000記事以上を目指すブログ

react-nativeでRealm!!ディレクトリ構成や基本的な使い方

はじめに

react nativeのソースを貪っているとrealmというパッケージを使っていて、初めて聞いたワードだったのでなんのことやらだったので、調べてみました。

Realmってなにするもの?

Realm(レルム)はiOSやAndroidアプリで使えるDBで、モバイル用データベースという立ち位置のようです。

Realmの特徴

  • SQL文が不要
  • 組み込みデータベース(デバイス上で動作)
  • メモリ効率が良く、動作が高速

予備知識

iOSにはCoreData、AndroidにはSQLiteが標準のデータベースとしてあるようなのでRealm一択というわけではないが、話題なのはRealmらしい。

react-native-cliでRealmを使うことは可能だが、expoでは使えないらしい。

JavaScript以外の言語でもRealmは使えるとのことです。
JavaScriptではreact-native,node.js(サーバーサイドで使う場合)で使えます。
今回はフロントのreact-nativeで使う方法として解説します。

データ処理をAPIサーバーとDBサーバーでやるのももちろんできるので、他プラットフォームからの移植の際などはデータ処理は全て既存のAPIにまかせてreact-nativeでのフロント処理に専念といった方法でもいいと思います。

しかし、その場合でもやはりサーバー側にアクセス時の認証情報等をアプリに永続化させておく必要があります。

公式サイト

各開発環境別の導入方法と簡単なサンプルが詳しく記載されています。
https://realm.io/jp

具体的な使い方などはググるといろいろでてきますが、チュートリアルをならったものが多く、どういう構成で使うのがやりやすいかなーとかいろいろ考えていましたが、海外の方のyoutubeでこれはいいなっていう使い方を見つけたので、日本語版として以下に残しておきます。

Realmをreact nativeの環境に導入する

react nativeのプロジェクト配下に移動し、npmでRealmを追加

npm install --save realm
react-native link realm

※私の場合npmで権限エラーが発生してしまいましたので、以下の方法で解消
https://qiita.com/okoysm/items/ced3c3de30af1035242d

Realmで実装してみる

プロジェクトの構成

srcディレクトリと同階層に、別途「databases」というディレクトリを作って、その中にallSchemas.jsというファイルを作りました。

この辺は好みだと思いますが、他の方をみていると「strage」という名称にしていたり、modelとコントローラーを作ったりという方もいましたが、小規模なアプリなので一つにまとめたままです。

スキーマの設定

allSchemas.jsの中に書きます。

import Realm from 'realm';
export const DATA_SCHEMA = "DataList";
export const USER_SCHEMA = "UserData";

export const DataSchema = {
    name: DATA_SCHEMA,
    primaryKey: 'id',
    properties: {
        id: 'string',
        genre: 'string',
        word: { type: 'string', indexed: true},
        detail: 'string',
        title: 'string',
        author: 'string',
        memo: 'string',
        isLike: 'bool'
    }
};

export const UserSchema = {
    name: USER_SCHEMA,
    primaryKey: 'id',
    properties: {
        id: 'string',
        name: 'string',
    }
};

DataListというテーブルとUserDataというテーブル2つを定義しています。

RealmはオートインクリメントによるIDの採番ができないようなので、primaryKeyのデータ型は一般的なDBでのintではなく、Stringとしました。

Stringにしたのはidの項目を一意にするためにUUIDを設定することにしたのが理由です。

データベースの設定を定義

allSchemas.jsの中に続けて書きます。
データベースの設定を書きます。

const databaseOptions = {
    path: 'testApp.realm',
    schema: [DataSchema,UserSchema],
    schemaVersion: 1,
}

schemaVersionというのは、マイグレーションのために必要です。

無理やりスキーマ(テーブル)の項目に変更を加えた場合、既存のデータと合わなくなってしまいアプリがクラッシュします。

クラッシュを防ぐために、schemaVersionで管理し、変更する項目を定義することで既存データをよしなにしてもらうという作業が必要です。

リリース済みのアプリのスキーマに変更を加える際など必要になりますが、いまは開発環境なので、スキーマに変更を加えた際はこのschemaVersionを変更するだけでOK。(古いデータは削除されます。)

クエリ用の関数を定義

別コンポーネントから汎用的、かつ簡単に呼び出すことができるように、クエリ用の関数も続けて書いていきます。

とりあえず用意するのは5つ。

  • インサート用の関数
  • 更新用の関数
  • 削除用の関数
  • 全削除用の関数
  • セレクト用の関数
// DataListスキーマにインサート
export const insertNewDataList = newDataList => new Promise((resolve, reject) => {
    Realm.open(databaseOptions).then(realm => {
        realm.write(() => {
          realm.create(DATA_SCHEMA, newDataList);
          resolve(newDataList);
        });
    }).catch((error) => reject(error));
});

// DataListスキーマにアップデート
export const updateDataList = dataList => new Promise((resolve, reject) => {
    Realm.open(databaseOptions).then(realm => {
        realm.write(() => {
            let updatingDataList = realm.objectForPrimaryKey(DATA_SCHEMA,dataList.id);
            updatingDataList.name = dataList.name
            resolve();
        });
    }).catch((error) => reject(error));
});

// DataListスキーマにデリート
export const deleteDataList = dataListId => new Promise((resolve, reject) => {
    Realm.open(databaseOptions).then(realm => {
        realm.write(() => {
            let deletingDataList = realm.objectForPrimaryKey(DATA_SCHEMA,dataListId);
            realm.delete(deletingDataList)
            resolve();
        });
    }).catch((error) => reject(error));
});

// DataListスキーマを全デリート
export const deleteAllDataList = () => new Promise((resolve, reject) => {
    Realm.open(databaseOptions).then(realm => {
        realm.write(() => {
            let allTodoLists = realm.objects(DATA_SCHEMA);
            realm.delete(allTodoLists)
            resolve();
        });
    }).catch((error) => reject(error));
});

// DataListスキーマからセレクト
export const queryAllDataList = () => new Promise((resolve, reject) => {
    Realm.open(databaseOptions).then(realm => {
        let allTodoLists = realm.objects(DATA_SCHEMA);
        resolve(allTodoLists);
    }).catch((error) => reject(error));
});
export default new Realm(databaseOptions);

呼び出す側の書き方

上記でRealmの設定を定義しておけば、呼びたいときに各コンポーネントで以下のように書くだけでデータのやり取りができます。

import React, { Component } from 'react';
import {insertNewDataList,
  queryAllDataList,
  deleteAllDataList
} from '../../../databases/allSchemas';

class *** extends Component {
// インサートの処理
const data = this.state.data
data.id = uuid()
insertNewDataList(data).then().catch((error) => {
  return alert(`Insert error ${error}`)
})

// セレクトの処理
queryAllDataList().then((dataLists) => {
  this.setState({ dataLists: dataLists})
}).catch((error) => {
  this.setState({ dataLists: [] })
})

// 全デリートの処理
deleteAllDataList().then().catch((error) => {
  alert(`Insert error ${error}`)
})
}

クローズの必要性

Realm はデータベースをオープンしたら、自分で必ずクローズする必要があるらしいのですが、しなくても動きはするのでつい付け忘れてしまうことが多いようです。

私が参考にした動画の方も特にRealm.close();は記述しておりませんでした。

試しに自分の環境でRealm.close();を追加しましたが、そうするとデータの取得ができなくなってしまいます。

書いても書いてなくても特にエラーを吐かないため判断が付きにくいので、この辺りはもう少し理解が深まれば追記しようと思います。

最後に

このまで形ができれば、あとはクエリの絞り込みやソートなどは公式ページを観ればいろいろと書いてあります。
https://realm.io/docs/javascript/latest/

また、冒頭で書いた「海外の方の参考動画」は以下になります。
英語ですが動画でわかりやすいので、この記事でしっくりこなかった方はぜひみてみてください。
https://www.youtube.com/watch?v=2sI64vaHF98