アレアレ

お役立ち情報、お悩み解決情報を発信!

visual studio for mac

Visual Studio for Macを利用したiOSアプリのAndroidへの移行手順

Visual Studio for Macを利用したiOSアプリのAndroidへの移行手順

今回は、iOSアプリのAndroid版への移植開発にあたり、「Visual Studio for Macってどうなの?」と興味がある人に伝えたいお役立ち情報です。

私は、Visual Studio for Macを利用して、iOSのネイティブアプリをAndroid版へ移植し、そのアプリをGoogle Playへの公開するまでを実際に試しました。

その手順をご紹介することで、「Visual Studio for MacでのAndroidアプリ開発はこんな感じなんだな」という雰囲気を掴んでいただきたいと考えています。

また、今回の記事の対象読者ですが、iOSとAndroidでのネイティブアプリの開発知識がある一方で、Visual Studioを触った経験がない人をイメージしています。

つまり、iOSとAndroidアプリの開発知識がないと、理解しにくい内容となります。ただ、具体的なソースコードを示しながら、わかりやすく説明することを心掛けますので、そんなに詳しくなくても、雰囲気は掴めるはずです。

開発環境

はじめに、私が今回の検証を行った開発環境は次の通りです。

  • OS: macOS Sierra (バージョン 10.12.1)
  • Visual Studio for Mac: Preview 1 (7.0 build 347)
  • Android Studio: 2.2.2 (Java SE Runtime Environment build 1.8.0_77-b03)
  • Xcode: Version 8.1 (8B62)

で、ご覧いただくとわかる通り、この記事の開発環境は、Visual Studio for Macのプレビュー版を利用しています。

そのため、以下で紹介する内容のうち、Visual Studio for Macの機能を使った箇所の説明に関しては、正式版とは齟齬が出る可能性があります。

一方で、「Visual Studio for Macを利用したiOSアプリのAndroidへの移行手順」という大局的に見ると、その手順は大きく変わらないはずです。

移行手順の概要

では、Visual Studio for Macを利用したiOSアプリのAndroidへの移行手順の概要から、ご紹介します。次の通りです。

  1. iOSアプリのロジックをVisual Studio for Macのクラスライブラリとして実装する
  2. そのクラスライブラリ向けに単体テストプロジェクトを作り単体テストを書く
  3. Android Appプロジェクトを追加しUIを作る(ロジックは1のクラスライブラリのものを利用する)
  4. Android AppプロジェクトのUIの単体テストを書く(今回は省略)

と、ざっくりこのような手順で行いました。では、それぞれの手順について、具体的なサンプルアプリのソースを踏まえつつご紹介します。

移行対象のサンプルiOSアプリ

今回、説明を具体的にわかりやすくするために、本当に簡単なiOSのサンプルアプリを定義します。具体的には、次の画像を見てください。

ios-sample-app

このように、入力のテキストボックスが2つあり、そこに数値を入れたらその足し算結果が、右のラベルに表示される、というだけのアプリです。

で、このアプリのソースコードですが、次のようになります。

ViewController.swift

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var left: UITextField!
    @IBOutlet weak var right: UITextField!
    @IBOutlet weak var result: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func textFieldEditingChanged(_ sender: Any) {
        
        let leftValue = (left.text! as NSString).doubleValue
        let rightValue = (right.text! as NSString).doubleValue
        
        result.text = "\(leftValue+rightValue)"
    }
}

このソースを簡単に解説すると、textFieldEditingChangedメソッドは、2つのテキストボックスのどちらかに、文字が入力されると呼び出されます。

その際に、テキストボックスのそれぞれの値を数値で取得して、足してラベルに設定する、という極めて簡単なプログラムです。

ロジックをクラスライブラリに実装する理由

では、いよいよVisual Studio for Macを利用していきます。で、まずは、上の足し算アプリのロジック部分をクラスライブラリとして実装します。

このロジック部分とは、このサンプルでは、「2つの数値を足して、1つの数値として返す」と言う部分です。

たったこれだけであれば、わざわざクラスライブラリにする意味を見出せない方も多いはずです。ですが、実際には、ViewControllerには、もっと複雑な処理を書いていますよね。

その複雑な処理を、将来的に共通利用するために、ロジックとして、クラスライブラリにします。ちなみに「将来的」と言うのは、Android版の開発後に行う、iOSアプリのC#化です。

Android版の開発をきっかけに、これまでXcodeで作ってきたiOSアプリも、Visual Studio for Mac側に取り込みたいわけですね。そうした方がソースを共通化でき、将来的な生産性が高まるからです。

その布石としてロジック部分をクラスライブラリとして実装します。

Visual Studio for Macでのクラスライブラリの実装手順

では、ロジック部分をクラスライブラリとして実装する手順を、足し算アプリの移植を例にご紹介します。

まず、Visual Studio for Macを起動したら、空のソリューションファイルを「MyGreatApp」と言うソリューション名で作成します。

empty-solution1

empty-solution2

続いて作成した、そのソリューションに対し、新しいプロジェクトとして、「MyGreatApp.Core」と言う名前のポータブルライブラリを追加します。

add-portable-lib1

add-portable-lib2

ポータブルライブラリのプロジェクトの追加が完了すると、そのプロジェクト内に、MyClass.csという初期ファイルがあるので、それは削除します。

そして、新たにCalcModel.csという空のクラスファイルを追加してください。

add-calcmodel

で、このCalcModelに、足し算アプリのロジック部分となる、「2つの数値を足して、1つの数値として返す」処理を実装します。具体的には、次の通りです。

CalcModel.cs

namespace MyGreatApp.Core
{
    public class CalcModel
    {
        public double Left
        {
            get;
            set;
        }

        public double Right
        {
            get;
            set;
        }

        public double Result
        {
            get { return Left + Right; }
        }
    }
}

以上で、足し算アプリのロジックをクラスライブラリとして実装することができました。

クラスライブラリの単体テストを書く

では、ロジックの実装ができたので、そのテストを行う単体テストを書きます。具体的には、ソリューションに対して、単体テストのプロジェクトを「MyGreatApp.Core.Test」というプロジェクト名で追加します。

add-unit-test-project1

add-unit-test-project2

単体テストのプロジェクトを選ぶ時ですが、UI Testのプロジェクトを選ばないように気をつけてください。その他→.Net内にある「NUnit ライブラリ プロジェクト」を選択してください。

単体テストのプロジェクトが追加できたら、次にそのプロジェクトから、MyGreatApp.Coreライブラリへの参照設定を行います。そうしないと、このプロジェクトから、先ほど実装したロジックが呼べないからです。

その参照設定を行うためには、Visual Studio for Macの画面右にあるソリューションから、MyGreatApp.Core.Testのプロジェックトを展開し、「参照」でコンテキストメニューを表示し、「参照の編集」を行います。

add-reference1

すると、次の画面がポップアップ表示されるので、「プロジェクト」タブを選択し、MyGreatApp.Coreのチェックボックスにチェックを入れます。

add-reference3

以上で、ロジックへの単体テストを書く準備ができました。

追加した単体テストのプロジェクトには、Test.csというファイルが初めから存在するはずです。今回は、そのファイルを編集して、単体テストを書きます。具体的には、次のコードです。

Test.cs

using NUnit.Framework;
namespace MyGreatApp.Core.Test
{
    [TestFixture()]
    public class Test
    {
        private CalcModel Model = new CalcModel();
        
        [Test()]
        public void 足し算の結果が正しい()
        {
            Model.Left = 3.9;
            Model.Right = 5.1;

            Assert.AreEqual(Model.Result, 9.0);
        }
    }
}

ロジックがシンプルなので、テストもこのようにとてもシンプルなものとなりました。実際には、もっと複雑なロジックに対しての単体テストなるので、テストのコードはもっと複雑になります。

ちなみに上のソースでは、テストケースのメソッド名を「足し算の結果が正しい」と日本語で書いていますが、日本人にとっては、この方がわかりやすいです。

試しにこの単体テストをVisual Studio for Macで実行すると、次の画面の結果が得られます。

test-result

ご覧の通り、日本語だと、何のテストを行ったのかがすぐにわかりますよね。

Android Appプロジェクトを追加し、UIの実装を行う

以上でクラスライブラリのロジックが正しく実装されていることが確認できました。なので、あとは、このクラスライブラリを利用した、AndroidアプリのUIを作るだけです。

そのために、ソリューションに対し、新しいプロジェクトとして、Android Appプロジェクトを「MyGreatApp.Android」という名前で追加します。

add-android-app-project1

add-android-app-project2

add-android-app-project3

Android Appプロジェクトの追加ができたら、次は、MyGreatApp.Core.Testに対して行った同じ手順で、MyGreatApp.Coreライブラリの参照設定を行ってください。

続いて、このプロジェクトをデバッグ起動できるようにするため、「スタートアッププロジェクト」に設定します。

set-startupproject

「スタートアッププロジェクト」の設定ができたら、試しにAndroid Appプロジェクトをこの時点でデバッグ起動してみましょう。運が良い人は、ちゃんとシミュレーターが起動し、「Hello World, Click Me!」というボタンが表示されるはずです。

一方で、その人の環境次第では、ここで一向にシミュレーターが起動しない状況に出くわします。そうなった人は、次の記事の手順を試してください。

Visual Studio for Macからのデバッグ時にAndroidシミュレーターがうまく動かない場合の対応方法

では、AndroidアプリのUIの実装を始めて行きます。まずその画面を作ります。すでにResources/Layout/Main.axmlに画面の定義がありますので、それを編集し次の画面を作成します。

android-app-ui

Main.axml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:minWidth="25px"
    android:minHeight="25px">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/linearLayout1">
        <EditText
            android:inputType="numberDecimal"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:id="@+id/editTextLeft"
            android:width="88dp"
            android:hint="0"
            android:gravity="right" />
        <TextView
            android:text="+"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center" />
        <EditText
            android:inputType="numberDecimal"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:id="@+id/editTextRight"
            android:width="88dp"
            android:hint="0"
            android:gravity="right" />
        <TextView
            android:text="="
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center" />
        <TextView
            android:text="0"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center_vertical|right"
            android:id="@+id/textViewResult"
            android:width="88dp" />
    </LinearLayout>
</LinearLayout>

以上のようにUIを定義したら、続いて、その画面の処理を記述するため、MainActivity.csを編集します。具体的には次のコードの通りです。

MainActivity.cs

using System;
using Android.App;
using Android.Widget;
using Android.OS;
using MyGreatApp.Core;
namespace MyGreatApp.Android
{
    [Activity(Label = "MyGreatApp", MainLauncher = true, Icon = "@mipmap/icon")]
    public class MainActivity : Activity
    {
        private CalcModel Model = new CalcModel();

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            SetContentView(Resource.Layout.Main);

            //EditTextに入力があった場合の処理を定義する
            FindViewById<EditText>(Resource.Id.editTextLeft).TextChanged += (sender, e) =>
            {
                Calc();
            };
            FindViewById<EditText>(Resource.Id.editTextRight).TextChanged += (sender, e) =>
            {
                Calc();
            };
        }

        //計算結果をラベルに表示する
        private void Calc()
        {
            Model.Left = GetValue(Resource.Id.editTextLeft);
            Model.Right = GetValue(Resource.Id.editTextRight);

            FindViewById<TextView>(Resource.Id.textViewResult).Text = $"{Model.Result}";
        }

        //指定したIDのEditTextの入力値を数値として取得する
        private double GetValue(int editTextId)
        {
            var editText = FindViewById<EditText>(editTextId);
            double result = 0;
            if (double.TryParse(editText.Text, out result))
            {
                return result;
            }

            return 0;
        }
    }
}

以上のコードのように編集し、デバッグ実行すると、シミュレーター上でこのサンプルが起動します。

そのシミュレーターに起動したアプリに、2つの数値を試しに入れてみた様子が次の画像です。

android-app

これでiOSサンプルアプリと同じAndroidアプリができたことになります。

で、本来であれば、ここでできたAndroidアプリに対し、UIの単体テストを書くところですが、この記事では省略します。

この記事の目的は、Visual Studio for Macを使って、iOSアプリをAndroidアプリに移植する手順の雰囲気を、お伝えすることにあるからです。ここまでの内容で、それがお伝えできていますよね?

というわけで、以上、Visual Studio for Macを利用したiOSアプリのAndroidへの移行手順でした。

Return Top