スタッフブログ Staff Blog

MTのData APIを活用してオフライン対応のiOSアプリを作成する

現在ネットワーク接続がオフラインの場合でも利用できるiOSのネイティブアプリを試作しています。コンテンツはMovable Typeに格納されているので、Data APIを利用してデータのやりとりをすることを検討中です。目下ある程度データをやりとりする仕組みができあがったので、Data APIの利用例としてご紹介してみたいと思います。

iOSアプリでデータを蓄積する方法

前提知識として少しご紹介しますが、iOSアプリでオフライン対応を行うには、何らかのデータベースを利用してコンテンツを蓄積する方法が考えられます。

データベースの仕組みにはCore Data、SQLite、Realmなどがありますが、今回はRealmを使用することにしました。

データの取得

iOSアプリをオフラインでも利用可能にするために、まずアプリ起動時にすべてのコンテンツをダウンロードする必要があります。これは、標準で用意されている記事を取得するエンドポイントを利用することで実現できます。

https://yourdomain/path/to/mt/mt-data-api.cgi/v3/sites/[blog_id]/entries?limit=10000

MTEntriesタグでlastn="0"を指定して全ての記事を取得するといったことができませんので、limitに大きな値を指定する必要がありました。(limit=0にすると50件しか取得できません。全記事を取得するパラメーターの要望をFogBugzに書こうかなとも...。)

エンドポイントにアクセスして返ってきた結果をRealmに保存します。Realm Browserでデータベースを確認すると、以下のように記事データが入っていることが確認できます。
Realm Browserでデータベースの内容を表示した画面

全記事の取得は負荷が高い

今回のブログには1,000件強の記事が入っていたため、先のエンドポイントにアクセスしても結果が返ってくるまでにしばらく時間を要しました。このままではアプリの起動時間が長くなりますし(ちなみに、予めデータベースをアプリに入れておく手法もあります)、アプリのユーザー数が増えアクセス数が増えた場合が不安です。

そこで、アルファサード株式会社が提供している「ResourceObjectプラグイン」で、予め全記事のデータが入ったJSONをサーバーに出力しておきます。以下のような簡単なテンプレートを書くだけで、mt-data-api.cgi/v3/sites/[blog_id]/entriesにアクセスした時と同じ内容のJSONが出力されます。

<mt:setVars>
    fields=title,id,body,more,keywords,basename,tags,categories,date,permalink
</mt:setVars>
<mt:Entries lastn="0"><mt:If name="__first__"><$mt:EntriesCount setvar="total_results"$></mt:If></mt:Entries>

{"totalResults":<$mt:Var name="total_results"$>,"items": [<MTEntries glue="," lastn="0"><mt:ResourceObject stash="entry" fields="$fields"></mt:Entries>]}

静的なJSONを取得するようにした結果、アプリの起動時間を大幅に短縮することができました。

記事データの更新

前項でアプリ内にブログのコンテンツを全て保存しましたが、その後もブログが更新されていくため、定期的にアプリのデータベースを更新する必要があります。そこで、記事を取得した日を記録しておき、以下のようなパラメーターで記事を取得するエンドポイントにアクセスすることで、2017年7月30日以降に編集が行われた記事が取得できます。これで得たレスポンスを元にデータベースを更新すれば完了です。

mt-data-api.cgi/v3/sites/[blog_id]/entries?limit=10000&dateFrom=2017-07-30&dateField=modified_on

記事データの削除

頻度は少ないかもしれませんが、記事の新規追加・既存記事の編集の他に記事のステータスが「非公開」に変更される、また記事が削除されることも予想されます。

ステータスが「非公開」に変更された記事データの取得

記事を取得するエンドポイントを利用してステータスが「非公開」の記事も取得することはできるのですが、Movable Typeの認証を通過させる必要があります。認証情報をアプリ内に隠し持つこともできるかもしれませんが、Data APIのエンドポイントを拡張し、認証なしで指定日以降にステータスが「非公開」になった記事IDの配列を取得できるように開発を行いました。

ステータスが「非公開」の記事を取得する部分のコードは以下のようになりました。(余談ですが、MT::Objectのloadメソッドを覚えると様々なことができるようなります。)


package DataAPIExtendEntries::DataAPI;
use strict;

use MT::DataAPI::Endpoint::Common;
use MT::DataAPI::Resource;
use MT::Entry;

sub get_unpublished_entries {
    my ($app, $endpoint) = @_;
    my ($blog) = context_objects(@_) or return;
    my $terms;
    my @ret;

    $terms->{'blog_id'} = $blog->id;
    $terms->{'class'} = 'entry';
    $terms->{'status'} = 1;

    if (my $dateFrom = $app->param('dateFrom')) {
        if ($dateFrom =~ m/\-/) {
            $dateFrom =~ s/\-//g;
        }
        $terms->{'modified_on'} = {'>=' => $dateFrom . '000000'};
    }

    my @entries = MT::Entry->load($terms);
    foreach my $entry (@entries) {
        my $id = $entry->id;
        push(@ret, $id);
    }

    my $length = @ret;

    return {
        totalResults => $length,
        ids => \@ret
    };
}

Data APIのエンドポイントの拡張は、上野様の「Movable Type DataAPI拡張プラグイン作成の第一歩 - uehatsu's tech blog」が参考になりました。ありがとうございます。

削除された記事データの取得

削除された記事の情報を取得するのは厄介です。なぜならMovable Typeでは記事を削除すると、記事を保存しているmt_entryテーブルから記事情報が削除されてしまうためです。標準ではどのようにしても削除した記事情報を得ることはできません。

そのため、公式の「Movable Type オブジェクト・リファレンス - MT::Object」を参考にMT::ObjectのサブクラスであるDeletedObjectクラスを作成し、記事を削除した際にDeletedObjectクラスに記事IDと削除日時を記録することを考えました。DeletedObjectクラスのプロパティは以下のように定義し、データベースにmt_deletedobjectテーブルが作成されました。


package RecordDeletedObject::DeletedObject;
use strict;

use base qw( MT::Object );
__PACKAGE__->install_properties( {
    column_defs => {
        'blog_id' => {
            'label' => 'BlogID',
            'type' => 'integer'
        },
        'class' => {
            'label' => 'Class',
            'size' => 64,
            'type' => 'string'
        },
        'deleted_object_id' => {
            'label' => 'DeletedObjectID',
            'type' => 'integer'
        },
        'id' => 'integer not null auto_increment',
    },
    indexes => {
        'blog_id' => 1,
        'class' => 1,
    },
    datasource => 'deletedobject',
    primary_key => 'id',
    class_type  => 'deletedobject',
    audit => 1,
} );

その後MT::App::CMS::cms_post_delete.entryフックポイントを利用して記事情報の登録する関数、また先に紹介した『ステータスが「非公開」に変更された記事データの取得』と同じようにMT::Objectのloadメソッドを利用してテーブルのデータを取得する関数を作成しました。結果、mt-data-api.cgi/v3/sites/[blog_id]/deletedentriesにアクセスすると以下のキャプチャ画像のように指定日以降に削除した記事のIDが取得できるようになりました。
自作のdeletedentriesエンドポイントにアクセスし、指定日以降に削除した記事のIDを取得した画面

まとめ

標準で用意されているData APIのエンドポイントに加え、削除した記事などを取得できるエンドポイントを追加することで、Data APIを活用してオフラインに対応したiOSのネイティブアプリが作成できることが分かりました。コンテンツを一覧表示したり記事を表示したりする際、サーバーへのアクセスがないため非常に高速に表示されます。(画像がある場合は別になってしまいますが...。)

本稿がMovable TypeのData API活用例、Data APIを活用したネイティブアプリの作成例として参考になりましたら幸いです。


お知らせ(PR)

当社関連会社であるファサード株式会社では、Movable Type / PowerCMSで運用中のウェブサイトを10分でアプリ化できるパッケージ「Apliko」(モバイルアプリパック)を販売しております。詳しくはAplikoのウェブサイトをご覧ください。

お問い合わせ Contact

制作のご依頼、ご相談などは下記のフォームからご連絡ください。