【Unity】Firebase Cloud Firestoreを実装してみた

はじめに

今回はUnityでFirebase Cloud Firestoreを実装してみたいと思います

公式ドキュメントはこちらです。

Cloud Firestore  |  Firebase

Firebase Console上での画面はここです。

f:id:dasuko:20200802140114p:plain

Firebase Cloud Firestoreとは

Google の柔軟でスケーラブルな NoSQL クラウド データベースを使用して、クライアント側開発とサーバー側開発のデータを保存、同期します。

Cloud Firestore は、Firebase と Google Cloud Platform からのモバイル、ウェブ、サーバー開発に対応した、柔軟でスケーラブルなデータベースです。Firebase Realtime Database と同様に、リアルタイム リスナーを介してクライアント アプリ間でデータを同期し、モバイルとウェブのオフライン サポートを提供します。これにより、ネットワークの遅延やインターネット接続に関係なく機能するレスポンシブ アプリを構築できます。Cloud Firestore は、その他の Firebase および Google Cloud Platform プロダクト(Cloud Functions など)とのシームレスな統合も実現します。

Cloud Firestore  |  Firebaseより引用

Firebase Cloud Firestoreはデータベースを使ってユーザのデータを保存・管理したい場合に使うと便利です。

FirebaseのデータベースにはCloud Firestoreの他にRealtime Databaseもあります。

どちらがいいかはこちらをご参考ください

データベースの選択: Cloud Firestore または Realtime Database  |  Firebase

データベースの作成

f:id:dasuko:20200804003254p:plain

Firebase Consoleからデータベースの作成を選択します。

すると、以下のような画面になります。

f:id:dasuko:20200804003423p:plain

こちらでデータのセキュリティを保護するルールを作成します。

ルールには本番環境とテストモードがあります。

本番環境・・・データはデフォルトで非公開になります。セキュリティルールで非公開になります。セキュリティルールで指定されている通りに、クライアントの読み取り/書き込み権限のみ付与されます。

テストモード・・デフォルトでデータが開き、クイックセットアップが有効になります。セキュリティルールが更新されない場合、クライアントの読み取り/書き込みアクセスは30日後に拒否されます。

今回はテストモードで作成してみます。

  

 

次にロケーションを設定します。

f:id:dasuko:20200804003939p:plain

ロケーションを選択したら、完了ボタンを押します。

  

データベースを作成すると、以下のような画面になります。

f:id:dasuko:20200804004352p:plain

ルールタブを選択すればいつでもセキュリティ保護のルールを変更できます。

f:id:dasuko:20200804004502p:plain

もし、Realtime Databaseが選択されていて、FireStoreに変更したい場合は以下をクリックします。

f:id:dasuko:20200804011624p:plain

f:id:dasuko:20200804013808p:plain

UnityPackageをインポート

FirebaseFirestore.unitypackageをUnityプロジェクトにインポートします。

実装

実装してみたいと思います。

データの追加

まずはデータを追加してみます。

        var docRef = Firebase.Firestore.FirebaseFirestore.DefaultInstance.Collection("users").Document("user");
        var user = new System.Collections.Generic.Dictionary<string, object>
        {
            { "Name", "Hogehoge" }
        };

        docRef.SetAsync(user).ContinueWithOnMainThread(task => {
            Debug.Log("Completed");
        });

これを実行すると、Firebase Console上でデータが追加されたことが確認できます。

f:id:dasuko:20200804013739p:plain

ドキュメントが存在しない場合には、ドキュメントが新規で作成され、ドキュメントが存在する場合には、上書きします。

ただ、以下のようにSetOptionsを指定することにより、データを統合するように指定することも可能です。

        docRef.SetAsync(user, Firebase.Firestore.SetOptions.MergeAll).ContinueWithOnMainThread(task => {
            Debug.Log("Completed");
        });

以下のようにカスタムクラスを使ってデータを定義することもできます。

        var docRef = Firebase.Firestore.FirebaseFirestore.DefaultInstance.Collection("users").Document("user");
        var user = new User
        {
            Name = "Hugahuga"
        };

        docRef.SetAsync(user).ContinueWithOnMainThread(task => {
            Debug.Log("Completed");
        });

.....

[Firebase.Firestore.FirestoreData]
public class User
{
    [Firebase.Firestore.FirestoreProperty]
    public string Name { get; set; }
}

プロパティを使えるのはいいですね!

データを更新する場合は以下のようにUpdateAsyncを実行します。

        var docRef = Firebase.Firestore.FirebaseFirestore.DefaultInstance.Collection("users").Document("user");
        var user = new System.Collections.Generic.Dictionary<string, object>
        {
            { "Name", "Hogehoge" }
        };

        docRef.UpdateAsync(user).ContinueWithOnMainThread(task => {
            Debug.Log("Completed");
        });

最後に

今回はFirebaseのCloud Firestoreを実装してみました。

機会があれば、Realtime Databaseも記事にしてみたいと思います。(どちらも過去に実装したことはあります)

参考

Cloud Firestore  |  Firebase

【C++】C++でHTTPサーバを実装してみた

はじめに

サーバの理解を深めるためになんとしてもC++でサーバを実装してみたい!と思い、実装してみました。

今回のゴール

ブラウザに"Hello World!"と出力する。

エンドポイントの作成

各説明は以下より引用

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/socket.2.html

#include <sys/types.h>
#include <sys/socket.h>

....

int socket(int domain, int type, int protocol);  

socketは通信のためのエンドポイントを作成し、ディスクリプタを返します。

domain

domainはどのプロトコルファミリを通信に使用するか指定するためのドメインを指定します。

以下のようなものがあります。

名前 説明
AF_UNIX, AF_LOCAL ローカル通信
AF_INET IPv4 インターネットプロトコルを使用しての通信
AF_INET6 IPv6 インターネットプロトコルを使用しての通信
AF_PACKET 低レベルのパケットインターフェース
AF_ALG カーネルの暗号APIへのインターフェース

type

typeは通信方式を指定します。

(ほとんどのサンプルがSOCK_STREAMを使ってた)

名前 説明
SOCK_STREAM 順序性と信頼性があり、双方向の、接続された バイトストリーム (byte stream) を提供する
SOCK_DGRAM データグラム (コネクションレス、信頼性無し、固定最大長メッセージ) をサポートする。
SOCK_SEQPACKET 固定最大長のデータグラム転送パスに基づいた順序性、信頼性のある 双方向の接続に基づいた通信を提供する。受け取り側ではそれぞれの入力 システムコールでパケット全体を読み取ることが要求される。
SOCK_RAW 生のネットワークプロトコルへのアクセスを提供する。
SOCK_RDM 信頼性はあるが、順序は保証しないデータグラム層を提供する。
SOCK_NONBLOCK 新しく生成されるオープンファイル記述 (open file description) の O_NONBLOCK ファイルステータスフラグをセットする。
SOCK_CLOEXEC 新しいファイルディスクリプターに対して close-on-exec (FD_CLOEXEC) フラグをセットする。

sockaddr_in構造体

struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

sin_familyにはAF_INETをセットする。

sin_portはポート番号

sin_addrはIPホストアドレス 詳しくは以下を参照

https://linuxjm.osdn.jp/html/LDP_man-pages/man7/ip.7.html

bind

ソケットに名前をつける

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr,
         socklen_t addrlen);

bindはsockfd(ファイルディスクリプタ)参照されるソケットにaddrで指定されたアドレスを割り当てます。

詳しくは以下参照

https://linuxjm.osdn.jp/html/LDP_man-pages/man7/ip.7.html

listen

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);

listenはsockfdが参照するソケットを接続待ちソケットしてマークする。

sockfd についての保留中の接続のキューの最大長を指定する。

詳しくは以下参照

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/listen.2.html

accept

#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

ソケットへの接続を受けます。

引数のaddrはsockaddr構造体へのポインタ。接続相手のソケットのアドレス。

addrlenは接続相手のアドレスの大きさが入る。

詳しくは以下参照

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/accept.2.html

recv

接続されたソケットまたはバインドされた非接続ソケットからデータを受診します。

int recv(int sockfd,  char* buf, int len, int flags
);

bufは受診データを格納するバッファへのポインタ

lenはbufのバッファの長さ

詳しくは以下参照

recv

send

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ソケットへメッセージを送信します。

送信するメッセージをbufに格納し、その長さをlenで指定します。

lflagsに指定するフラグは以下のフラグの論理和をとったものを指定します。

名前 説明
MSG_CONFIRM 転送処理に進展があった、つまり相手側から成功の応答を受けたことをリンク層に知らせる
MSG_DONTROUTE パケットを送り出すのにゲートウェイを使用せず、 直接接続されているネットワーク上のホストだけに送る。
MSG_DONTWAIT 非停止 (nonblocking) 操作を有効にする。
MSG_EOR レコードの終了を指示する (SOCK_SEQPACKET のようにこの概念に対応しているソケット種別のときに有効)。
MSG_MORE 呼び出し元にさらに送るデータがあることを示す。 このフラグは TCP ソケットとともに使用され、 TCP_CORK ソケットオプションと同じ効果が得られる
MSG_NOSIGNAL ストリーム指向のソケットで相手側が接続を切断した時に、エラーとして SIGPIPE を送信しないように要求する。
MSG_OOB 帯域外 (out-of-band) データをサポートするソケット (例えば SOCK_STREAM) で 帯域外 データを送る。

詳しくは以下参照

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/send.2.html

最終的なコード

今回書いたコードは以下のようになりました。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>


int main()
{
    auto sock = socket(AF_INET, SOCK_STREAM, 0);

    if (!sock) {
        std::cout << "Failed to initialize a socket." << std::endl;
        return 1;
    }

    struct sockaddr_in server_addr;

    memset(&server_addr, 0, sizeof(server_addr));

    int port = 5000;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);

    int optval = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
        std::cout << "Failed to setsocket" << std::endl;
        close(sock);
        return 1;
    }

    if (bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) {
        std::cout << "Failed to bind. error:" << strerror(errno) << std::endl;
        close(sock);
        return 1;
    }

    if (listen(sock, 5) != 0) {
        std::cout << "Failed to listen. error:" << errno << std::endl;
        close(sock);
        return 1;
    }

    auto body = std::string("Hello World!");
    auto response = std::string("");
    std::ostringstream oss;

    oss << "Content-Length: " << 20 << "\r\n";

    response.append("HTTP/1.1 200 OK\r\n");
    response.append("Content-Type: text/html; charset=UTF-8\r\n");
    response.append(oss.str());
    response.append("Connection: Keep-Alive\r\n");
    response.append("\r\n");
    response.append(body);

    std::cout << "response:" << response << std::endl;

    char inbuf[2048];

    while (true) {
        auto connfd = accept(sock, nullptr, nullptr);
        if (connfd < 0) {
            std::cout << "Failed to accept." << std::endl;
            break;
        }

        memset(inbuf, 0, sizeof(inbuf));
        recv(connfd, inbuf, sizeof(inbuf), 0);

        if (send(connfd, response.c_str(), response.length(), 0) == -1) {
            std::cout << "Failed to send." << std::endl;
        }
    }

    close(sock);

    return 0;
}

実行結果

portを5000に指定したので

http://127.0.0.1:5000/にアクセスしてみます

無事Hello Worldと出力されました!

f:id:dasuko:20200731202908p:plain

まとめ

今回はIPv4TCPのサーバを実装してみましたが、

IPv6 サーバの実装はこの辺りを参考にするといいかと思います。

IPv6 server & client in C · GitHub

参考

HTTPサーバの作成(TCPサーバサンプル):Geekなぺーじ

Webサーバの動作を理解するための事前知識とC++によるフルスクラッチで実装して理解を深めよう - Qiita

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/socket.2.html

https://linuxjm.osdn.jp/html/LDP_man-pages/man7/ip.7.html

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/bind.2.html

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/listen.2.html

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/listen.2.html

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/accept.2.html

https://linuxjm.osdn.jp/html/LDP_man-pages/man2/send.2.html

recv

【Unity】Componentの操作まとめ

はじめに

UnityではGameObjectに対して、必要なComponentをアタッチすることにより

オブジェクト毎に異なる振る舞いをさせることができます。

今回はこのComponentの操作についてまとめたいと思います

前提条件

GameObjectにアタッチできるComponentは、Componentクラスを継承しているクラスのみです。

Componentの追加

EditorでGameObjectに対してComponentを追加

EditorからGameObjectに対してComponentを追加する場合は、Componentを追加したいGameObjectを選択した状態で

InspectorのAddComponentをクリックします。

f:id:dasuko:20200729024227p:plain

ScriptからGameObjectに対してComponentを追加

ジェネリックを使う場合は以下のようになります。

Sampleという名前のGameObjectを生成し、Rigidbody Componentをアタッチしています。

var obj = new GameObject("Sample");
obj.AddComponent<Rigidbody>();

Type指定でもAddComponentできます。

var obj = new GameObject("Sample");
obj.AddComponent(typeof(Rigidbody));

Componentの取得

Componentを取得するにはGetComponentを実行します。

var rigidbody = GetComponent<Rigidbody>();

Componentを複数取得することも可能です。

(Baseクラスを指定して、Componentを取得したい場合とか。

例えば、ゲームで状態異常を表すComponentを別々に定義して、キャラクターに対して複数アタッチすることがある場合など)

var components = GetComponents<ComponentBase>();

これもジェネリックではなく、Typeで取得することもできます。

var rigidbody = GetComponent(typeof(Rigidbody));

そのGameObjectの子要素から取得することも可能です。(再帰的に取得します)

var rigidbody = GetComponentInChildren<Rigidbody>();

もちろん親要素から取得することも可能です。

var rigidbody = GetComponentInParent<Rigidbody>();

Componentの削除

EditorからGameObjectのComponentを削除

Componentを追加する時と同様に、Componentを削除したいGameObjectを選択した状態で

Inspectorを開きます。

削除したいComponentの歯車アイコンをクリックします。

"Remove Component"を選択します。

f:id:dasuko:20200729025138p:plain

ScriptからGameObjectのComponentを削除

基本的にはComponentを取得して、そのComponentに対してDestoryを実行します。

var obj = new GameObject("Sample");
var rigidbody = obj.AddComponent(typeof(Rigidbody));
Destroy(rigidbody);

AddComponentは追加したComponentを返します。

Componentを定義

先述した通り、ComponentとしてGameObjectにアタッチするためには、

Componentクラスを継承したクラスである必要があります。

Create > C# Scriptで生成されるC#ファイルではMonoBehaviourを継承したクラスが生成されます。

MonoBehaviourはComponentを継承している(正確にはBehaviourを継承しており、BehaviourがComponentを継承している)ため、

特に気にする必要はありません。

自作したクラスがMonoBehaviourを継承しており、コンパイルエラーが出ていなければ、GameObjectにアタッチすることができます!

まとめ

今回は、Componentの操作だけをまとめてみました。

各クラスの役割や操作について今後時間があればまとめていきたいと思います。

ご意見あればいつでもコメントください!

【iOS】Objective-Cのプロパティ属性のまとめ

はじめに

Objective-Cのプロパティを自分のためのメモの意味を込めてまとめてみたいと思います。

atomicとnonatomic

atomic (default)

atomicは、スレッドの安全性を保証するためにロックを取得します。そのため、指定された値が常にGetterから設定し、Setterにより設定されます。

また、自動解放するための参照のカウントも保証し、呼び出し元に対して、オブジェクトが存在することを保証します。

(別スレッドが値を設定して、参照カウントが0になった場合に競合することがあります)

変数にアクセスする場合、別のスレッドでのアクセスが完了するまで、待機するため高速ではない。

nonatomic

スレッドセーフではないが、atomicより高速。

しかし、@synchronizedNSLockを使うことによりスレッドセーフを実現することも可能。

strongとassign

オブジェクト型プロパティはstrong、それ以外はassignがデフォルト。

strongはretainと同じでオブジェクトの参照を保持し続ける。オブジェクトへの強い参照を表す。

assignは値を保持する。

weak

渡された値を弱い参照で保持する。

参照先のオブジェクトが消えた場合はnilになる。

Outletやdelegateで使われることが多い。

(delegateの場合はstrongにすると、循環参照になり、対象のオブジェクトが解放されない)

copy

代入されたときにオブジェクトのcopyWithZoneメソッドを実行する。

結果はstrong(強い参照)で保持する。

Mutableなオブジェクトにはcopyを指定することにより、immutableオブジェクト返すことができるので、

プロパティに設定される値がimmutableであることを保証することができる。

また、NSStringにもcopyを指定する。

NSStringクラスのインスタンスにcopyを送っても同じアドレスを指すようになっています。

なぜNSStringをプロパティにする際にcopyオプションを選択すべきなのか - Qiita より引用

copyの振る舞いはcopyWithZoneの実装に依存します。

同じアドレスを返すということは、振る舞いとしてはstrongと同じ感じですかね

まとめ

Objective-Cのプロパティ属性についてまとめてみました。

結構自分でもわからなくなることがあるので、今回はメモ的な意味合いが強いです。

何か意見あれば、コメントください!

参考

Objective-C のプロパティ属性のガイドライン - Qiita

ios - What's the difference between the atomic and nonatomic attributes? - Stack Overflow

ios - Objective-C ARC: strong vs retain and weak vs assign - Stack Overflow

Objective C のプロパティ属性まとめ - Qiita

なぜNSStringをプロパティにする際にcopyオプションを選択すべきなのか - Qiita

【Unity】メインスレッドで処理を実行する方法

はじめに

Unityではメインスレッドでしか実行できないAPIが多く存在します。(というかほとんどそう)

しかし、パフォーマンスを向上させるためには、マルチスレッドプログラミングが必須と言ってもいいでしょう。

マルチスレッドプログラミングについては以下の記事にまとめてあります。

dasuko.hatenadiary.jp

TaskやThreadでバックグラウンドスレッドで処理を実行した後に、

メインスレッドで処理を継続したい!ということがあるかと思います。

(UIの更新とか)

今回は特定のアクション(処理)をメインスレッドで実行する方法を紹介します。

メインスレッドで処理を実行する方法

UniRx

GitHub - neuecc/UniRx: Reactive Extensions for Unity

UniRxは.NET Reactive ExtensionsがiOS IL2CPPの互換性に問題があるために、Unity向けに再実装されたものです。

Reactive Extensionsの機能がメインですが、他にも便利な機能が多くあります。

メインスレッドに処理を委譲したい場合は、MainThreadDispatcherを使います。

以下にサンプルがあるので、確認してみてください!

UniRx/Sample10_MainThreadDispatcher.cs at master · neuecc/UniRx · GitHub

具体的な使い方としては、以下のように使います。

// background thread.

MainThreadDispatcher.Post(_ => Debug.Log("test"), null);

Debug.Log("test")の部分がメインスレッドで実行されます。

自作する

MainThreadDispatcherを実装するのは、そんなに難しいことではありません。

UnityのAPIのほとんどはメインスレッドでしか実行できず、

Unityのイベントもまたメインスレッドで実行されます。

つまり、メインスレッドで実行したい処理を変数に入れて、 Updateイベントで処理すれば、メインスレッドで実行されるということです。

よって、以下のように実装することができます。

(もちろん改善の余地はあります)

  
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;

public class MainThreadExecutor : MonoBehaviour
{
    private Queue<Action> actionQueue = new Queue<Action>();

    private static readonly object syncObject = new object();
    private static MainThreadExecutor instance;

    public static MainThreadExecutor Instance
    {
        get
        {
            lock (syncObject)
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<MainThreadExecutor>();
                }

                if (instance == null)
                {
                    instance = new GameObject(typeof(MainThreadExecutor).ToString()).AddComponent<MainThreadExecutor>();
                }
            }

            return instance;
        }
    }

    public static void Initialize()
    {
        var executor = MainThreadExecutor.Instance;
        Debug.Log("initialized :" + executor ?? executor.name);
    }

    public void Post(Action action)
    {
        lock (syncObject)
        {
            actionQueue.Enqueue(action);
        }
    }

    private void Update()
    {
        while (actionQueue.Any())
        {
            Action action = null;
            lock (syncObject)
            {
                action = actionQueue.Dequeue();
            }

            if (action != null)
            {
                action.Invoke();
            }
        }
    }
}

まとめ

今回はUnityで特定の処理をメインスレッドで実行するための方法をご紹介しました。

今回のサンプルはGithubで公開しておきました。

GitHub - dasuko10/MainThreadExecutor: Main Thread Executor for Unity

ぜひご意見ください!!!

参考

GitHub - neuecc/UniRx: Reactive Extensions for Unity

【プログラミング】初心者向け学習法

はじめに

以前は、プログラミング初学者向けに最初にするべきことや

dasuko.hatenadiary.jp

プログラミング初学者向けの考え方についての記事を書きましたが、

dasuko.hatenadiary.jp

今回は学習法を中心に紹介していきます。

基本的には何か作って、わからないことがあればググる!というのがいいと思っています。

ただ、ググる時に参考にするべきサイトって何?とか

本当の基礎を勉強したい!っていう方もいると思うのでそういう人に向けた記事となっています。

(基本的に有名なのしか紹介しません)

ってことで今回紹介するのは大きく分けて以下の二つ

  • 基礎を学びたい時に参考にすべきサイトは?
  • わからないことがあってググったけど、どのサイトを参考にすべき?

では順番に紹介していきます。

基礎を学びたい時に参考にすべきサイト

どこかの大学の講義資料

個人的におすすめなのは、大学の講義資料とかを覗いてみることです。

意外と有名な大学の講義資料がインターネット上に転がっていたりします。

(もちろん国内外問わず)

なので「[有名な大学の大学名] [キーワード]」とかで調べると出てきたりしますよ

筆者は暇な時に、こういうのを漁ってたりします

キーワードは、"プログラミング"とかでもいいかも

あとは言語名とか技術とか、、、

Coursera

Top Online Courses and Specializations | Coursera

Courseraはいろんな大学や企業が公開している講義を受けられます。

(合間合間にテストもあります)

全ての講義が終わって、テストにも合格すれば、認定証がもらえたりします。(有料だったりもします)

edx

edX | Free Online Courses by Harvard, MIT, & more | edX

edxもCourseraと似ていて、海外の大学の講義が受けられたりします。

様々なカテゴリがあって、学びたいものを選んですぐに勉強が始められます。

学びたいこと、興味があることはあるけど、何をすればいいかわからない!という方にはおすすめです

参考にすべきサイト

StackOverflow

まずはStackOverflowですね

Newest Questions - Stack Overflow

世界中のいろんな開発者がここで質問したり、回答したり、そのやりとりを参考にしたりしています。

わからないことがあって、ググってStackOverflowが引っ掛かったら、まず見てみることをおすすめします。

Qiita

Qiitaも有名ですね

Qiita

日本語なので、こっちの方がハードルが低いかもしれません。

いろんな技術記事が日々更新されているので、自分の興味のあるトピックについては

常にチェックしてみてもいいかもしれません。

まとめ

ということで、何を勉強したらいいかわからない!という方は

大学の講義資料を漁ったり、Courseraやedxがおすすめです!!

やりたいこと、やってることがあって、わからないことをググったときにどんなサイトを信用したらいいのって方は

QiitaとかStackOverflowとかおすすめです。(後者はおすすめしなくても、ググったらまず出てくると思いますが)

全部、個人的に使ってるものなので、わからないこととかあったらいつでも聞いてください!

参考

Top Online Courses and Specializations | Coursera

edX | Free Online Courses by Harvard, MIT, & more | edX

Newest Questions - Stack Overflow

Qiita

MVVM (Model-View-View Model)

はじめに

今回はMVVM (Model-View-View Mode)について簡単にまとめてみたいと思います。

MVVMとは

Model-View-ViewModel (MVVM、モデル・ビュー・ビューモデル) は、ソフトウェアアーキテクチャパターンのひとつ。独自のグラフィカルユーザインタフェース (GUI) を持つアプリケーションを、以下に述べるようなModel-View-ViewModelの3つの部分に分割して設計・実装する方法。Model-View-Controller (MVC) の派生パターンであり、特にPresentation Modelパターンを直接の祖先に持つ。MVVMを考慮してアプリケーションを開発する目的は、他のMVC系のパターンと同様にアプリケーションの「プレゼンテーションとドメインを分離」することで、アプリケーション開発における保守性・開発生産性に寄与することである。

Model View ViewModel - Wikipediaより引用

MVVMはModel、View、ViewModelという3つの要素に分割して設計する方法です。

背景

2005年に、マイクロソフトWPFおよびSilverlightアーキテクトであったJohn Gossmanが自身のブログでModel-View-ViewModel (MVVM) パターンを発表しました。MVVMとFowlerのプレゼンテーションモデルは、両方ともビューの状態と動作を含んだ形でビューを抽象化しています。Gossmanはユーザインタフェースの作成を簡素化するために、WPFの活用コア機能への標準化された方法としてMVVMを導入し、一方、FowlerはビューのUIプラットフォームに依存しない抽象化を作成するための手段としてプレゼンテーションモデルを導入しました。その意味で、一般的なPMパターンをWPFSilverlightのプラットフォームにより特化したものになるようにMVVMを検討しています。

Model View ViewModel - Wikipediaより引用

MVVMパターンでは、ViewはUIと全てのUIに関するロジックをカプセル化し、ViewModelはプレゼンテーションロジックと状態をカプセル化し、モデルはビジネスロジックとデータをカプセル化します。

それぞれの詳しい特徴については後述します。

MVVMパターン.NET Framework 3.0に実装されたWPFSilverlightの両方をサポートするために考案されました。

しかし、今では幅広い開発で適用されています。

モバイルのアプリ開発ではよく見かけます。

MVVMの各要素について

MVVMには Model、View、ViewModelという三つの要素があります。

Model

アプリケーションのドメイン(問題領域)を担う、そのアプリケーションが扱う領域のデータと手続き(ビジネスロジック - ショッピングの合計額や送料を計算するなど)を表現する要素である。

Model View ViewModel - Wikipediaより引用

Modelは主にデータの格納やサーバ間との通信などの手続き部分を担っています。

ModelはViewの描画については何も知りません。

描画に関するロジックはView、ViewModelが担う。その他のロジックについてはModelが担うと考えるのが妥当です。

View

アプリケーションの扱うデータをユーザーが見るのに適した形で表示し、ユーザーからの入力を受け取る要素である。すなわちUIへの入力とUIからの出力を担当する。

Model View ViewModel - Wikipediaより引用

ViewはViewModelに含まれたデータをバインディングして自動的に描画するだけです。

Viewそのものに状態やロジックを持たないのが特徴です。

Viewはウインドウやページやボタンなどの視覚的な要素です。

Viewのコントロールやレイアウト、スタイルなどを定義します。

ViewModel

Viewを描画するための状態の保持と、Viewから受け取った入力を適切な形に変換してModelに伝達する役目を持つ。すなわちViewとModelの間の情報の伝達と、Viewのための状態保持のみを役割とする要素である。

Model View ViewModel - Wikipediaより引用

ViewModelはViewのプレゼンテーションロジックとデータをカプセル化します。

Modelが具体的な手続きを定義し、ViewModelがViewとModel間のデータの受け渡しを担当し、Viewが実際の描画を担当します。

MVVMパターンのメリット

  • 既存のビジネスロジックカプセル化する既存のモデルの実装がある場合は、それを変更することは困難であるか、または危険である可能性があります。 このシナリオでは、ビューモデルはモデルクラスのアダプターとして機能し、モデルコードに大きな変更を加えないようにします。
  • 開発者はビューを使用せずに、ビューモデルとモデルの単体テストを作成できます。 ビューモデルの単体テストでは、ビューで使用されるのとまったく同じ機能を使用できます。
  • ビューが XAML で完全に実装されていれば、コードに触れることなくアプリの UI を再設計できます。 したがって、ビューの新しいバージョンは、既存のビューモデルで動作する必要があります。
  • デザイナーと開発者は、開発プロセス中に、コンポーネントに対して個別に、または同時に作業を行うことができます。 デザイナーはビューに専念できますが、開発者はビューモデルとモデルコンポーネントを操作できます。

モデルビュービューモデルパターン - Xamarin | Microsoft Docs より引用

これはXamarinのドキュメントからの引用になりますが、大枠こんな感じだと思います。

  

アプリケーションロジックととUIを明確に分離することで、開発・設計が容易になります。

また、コードの再利用性も工場し、開発者とUI設計者は変更するファイルが違うので、

競合が発生せず、同時に共同作業することができます。

Viewに実装するかViewModelに実装するか

画面上のUIの外観に関連していて、あとでスタイル変更できるものはViewに定義するのがベター。

DataBindingとは

データバインディング(データバインド、データ結合)は、データと対象を結びつけ、データあるいは対象の変更を暗示的にもう一方の変更へ反映すること、それを実現する仕組みのことである

データバインディング - Wikipediaより引用

  

DataBindingはMVVMパターンではとても重要な役割を担います。

DataBindingには一方向のDataBindingと双方向のDataBindingがあります。

一方向のDataBindingを使用すると、UIの操作をViewModelにBindして、Viewが更新される時に、

基のデータの値を反映させることができます。

双方向のDataBindingではこれに加え、ユーザがUIで変更をした時に基となるデータも更新します。

AndroidでMVVM

Android x Kotlin x MVVMならこの辺りを参考にするといいと思います。

MVVMで実装するためのライブラリや機能が結構揃ってるイメージがあります。

機会があれば、別の記事で紹介したいと思います。

Android MVVM + LiveData + DataBinding(初心者向け) - Qiita

iOSでMVVM

iOSでSwift x MVVMっていうのはあまり実装経験がないので、

関連しそうな記事だけご紹介します。

【iOS】MVVMについて - Qiita

UnityでMVVM

UnityならUniRxを使う感じ?

UniRxは他にも結構Unityの補助的な機能が充実してるので、

Unity開発者の方はぜひ使ってみるといいと思います。

Unityで学ぶMVPパターン ~ UniRxを使って体力Barを作成する ~ - Qiita

まとめ

今回はMVVMについて簡単にまとめてみました。

機会があれば、実装サンプルも紹介したいと思います。

MVVMを意識して実装するだけで、設計や運用、開発がとても楽になるので、

まだ、取り入れたことがないという方はぜひ試してみたください!

参考

5: Implementing the MVVM Pattern Using the Prism Library 5.0 for WPF | Microsoft Docs

モデルビュービューモデルパターン - Xamarin | Microsoft Docs

Model View ViewModel - Wikipedia

データバインディング - Wikipedia

Android MVVM + LiveData + DataBinding(初心者向け) - Qiita

【iOS】MVVMについて - Qiita