iPhoneアプリのprovisioning profileの指定場所

iPhoneアプリを作成する場合、provisioning profileを作成する必要があるが、Visual Studioで開発を行う場合にも、XCode側でセットする必要がある。

XcodeメニューのPreverencesを開き、Accountsタグから、右下のView DetailsでProvisioning Profilesをダウンロードしておく必要がある。

 

ここまでやると、Visual StudioiOSプロジェクトのプロパティで選択できるようになります。

 

ちなみに、ad-hocでudidを追加した場合でも、XCodeでダウンロードしなおす必要があります。
その際、Visual Studio側も再起動して、iOSプロジェクトのプロパティを再選択しないとダメっぽいです。

 

最後に、ad-hocのudid指定が正しくされているかの確認は、ipaファイルの拡張子をzipに変更して中を参照し、embedded.mobileprovisionというファイルを強引にテキストエディタで開いて、
<key>ProvisionedDevices</key>
の部分を参照すれば、OK

Xamarin.Forms Androidのストアへの公開方法

Xamarin.FormsでAndroidアプリのストアへの公開方法のメモです。

 

新しいキーストアを作ります。
Visual Studioのツールメニューから、[Android]-[Android Adb Command Prompt...]を選択。

f:id:shinya_dog:20160708164146j:plain

 

コマンドプロンプトが起動するので、

keytool -genkey -v -keystore <パスを含んだファイル名> -alias <キーストア内のキーの名前> -keyalg RSA -keysize 2048 -validity 10000

を入力し、必要事項にこたえていきます。

f:id:shinya_dog:20160708165956j:plain

 

Visual Studioのツールメニューから、[Android]-[Publish Android App...]を選択。

f:id:shinya_dog:20160708170417j:plain

 

先ほど作成したKeyStore情報を入力します。

f:id:shinya_dog:20160708170928j:plain

 

次の画面で、APKファイルの出力場所と名前を指定します。
基本的に初期値で良いでしょう。但し、APK パッケージ名は「com.example.myapp」形式で指定する必要があります。
ファイル名がこれに準拠していない場合は、DroidプロジェクトのプロパティでPackage nameを変更しましょう。

f:id:shinya_dog:20160708171422j:plain

 

Xamarin.Formsでとりあえずスプラッシュ(起動)画面を表示

Xamarin.Formsのテンプレートを使用すると、iOSのResourcesにデフォルトのアイコンやスプラッシュ画面と思える画像が用意されている。当然、ファイル名がどこかに記述されているんだろうなとは思うものの、とりあえず、そのまま画像を変更しちゃえば変わるだろうなと試したところ変わらない・・・

 

僕だけじゃないよね?

 

いろいろ方法はありそうだけど、スプラッシュ画面なんてなんでも良いけど、デフォルトのXamarinロゴはさすがにってレベルだと、画像差し替えだけでなんとかしたいですよね(笑)

 

そんな人は、iOSプロジェクトのプロパティで、iOS Applicationタブにある、Launch Storyboard:という部分に入っている、LaunchScreenという文字を消してみましょう。

f:id:shinya_dog:20160707110716j:plain

 

これで、シミュレータで確認する限り、だいたいのプラットフォームで変わるはず。

アイコン等のリソース追加で気を付けること

新たにリソースを追加した際、iOSは問題なかったが、Droid側で、

「aapt.exe" はコード 1 を伴って終了しました。」

と表示された場合は、ファイル名に空白やハイフン等、JAVAで変数に使用できない文字が使用されていないか確認しましょう。

 

また、

「"CopyIfChanged" タスクが予期せずに失敗しました。」

が表示される場合は、TFS等のソース管理を使用していて、Resource.Designer.csが読み取り専用になっていないか確認しましょう。

Xamarin.Formsで設定の読み書きをDependency Serviceを使用しないで行ってみる

Xamarin.Formsで設定の保存をGoogle先生に聞いてみると、一般的な手法(色々な人が解説してくれている)では、Dependency Serviceを使用するのが良さそうだと教えてくれます。

ただ、これはこれで良いけど、データとメソッドを一つのクラスにして、プラットフォーム別の差異を継承で処理しちゃっても良いんじゃね?
と思っちゃったので、ちょっと試してみました。

良い例題がパッと思い浮かばないので、そのままデータの保存で試してみます。


まずは、共通プロジェクトに設定情報を管理するクラスの抽象クラスを定義する。
[XFApp1\ASettingInfo.cs]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XFApp1
{
	public abstract class ASettingInfo
	{
		/// <summary>ユーザーID</summary>
		public string UserId { get; set; }

		/// <summary>ユーザー名</summary>
		public string UserName { get; set; }

		/// <summary>保存</summary>
		public abstract void Save();

		/// <summary>読込</summary>
		public abstract void Read();
	}
}

そして、とりあえず共通プロジェクトのApp.csにstaticに宣言しちゃう!!
(なんでもかんでも、staticにしちゃダメよって言われそうですが・・・)
ついでに、テストに使用するテキストボックス(Entry)を2つと保存ボタンも定義して、
保存ボタンで入力値を保存して、プログラムの起動時に読み込む処理を記述しておきます。
[XFApp1\App.cs]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Xamarin.Forms;

namespace XFApp1
{
    public class App : Application
    {
	//設定情報保持用
	public static ASettingInfo	MySettingInfo		= null;

	//コントロール
	private Entry	txtUserId	= new Entry();
	private Entry	txtUserName	= new Entry();
	private Button	btSave		= new Button();

        public App()
        {
            // The root page of your application
            MainPage = new ContentPage
            {
                Content = new StackLayout
                {
                    VerticalOptions = LayoutOptions.Center,
                    Children = {
                         new Label {
                             HorizontalTextAlignment = TextAlignment.Center,
                             Text = "Welcome to Xamarin Forms!"
                         },
			 txtUserId,
			 txtUserName,
			 btSave
                     }
                }
            };

            //コントロールのプロパティを適当に・・・
            txtUserId.Placeholder = "ユーザーID";
            txtUserName.Placeholder = "ユーザー名";
            btSave.Text = "保存";
            btSave.Clicked += btSave_Clicked;
	}

	protected override void OnStart()
        {
            //設定を読み込む
            App.MySettingInfo.Read();

            //読み込んだ情報をセット
            txtUserId.Text = App.MySettingInfo.UserId;
            txtUserName.Text = App.MySettingInfo.UserName;
	}

	private void btSave_Clicked(object sender, EventArgs e)
	{
            //入力情報を保存
            App.MySettingInfo.UserId = txtUserId.Text;
            App.MySettingInfo.UserName = txtUserName.Text;
            App.MySettingInfo.Save();
	}
    }
}

次に、Droidプロジェクト側で、実際にインスタンスを作成するクラスを作成します。
内容としては、先ほどのASettingInfoを継承して、先ほどは抽象メソッドとしていた部分の実態を記述します。
※NuGetで、Newtonsoft.Jsonをインストールしておいて下さい。
[XFApp1.Droid\DependeSettingInfo.cs]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Newtonsoft.Json;

namespace XFApp1.Droid
{
	public class DependeSettingInfo : XFApp1.ASettingInfo
	{
		private const string	cFILE_NAME	= "SettingInfo.json";

		/// <summary>保存</summary>
		public override void Save()
		{
			var json = JsonConvert.SerializeObject(this);
			string FilePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + "/" + cFILE_NAME;

			System.IO.File.WriteAllText(FilePath, json);
		}

		/// <summary>読込</summary>
		public override void Read()
		{
			string FilePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + "/" + cFILE_NAME;

			string				json = null;
			DependeSettingInfo	RecvObj = null;

			if (System.IO.File.Exists(FilePath))
			{
				json = System.IO.File.ReadAllText(FilePath);
				RecvObj = JsonConvert.DeserializeObject<DependeSettingInfo>(json);
				this.UserId = RecvObj.UserId;
				this.UserName = RecvObj.UserName;
			}
		}

	}
}

そして、プログラムの開始時に、冒頭で宣言していたstatic変数にインスタンスをセットするようにしておきます。
[XFApp1.Droid\MainActivity.cs]

using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace XFApp1.Droid
{
	[Activity(Label = "XFApp1.Droid", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
	public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
	{
		protected override void OnCreate(Bundle bundle)
		{
			base.OnCreate(bundle);

			global::Xamarin.Forms.Forms.Init(this, bundle);

			//設定操作クラスのインスタンスを生成
			App.MySettingInfo = new DependeSettingInfo();

			LoadApplication(new App());
		}
	}
}

これで、Droid側で設定の読み書きと参照を行う準備ができました。
早速試してみましょう(^^)/

初回起動時は、当然テキストボックスには何も表示されません。
f:id:shinya_dog:20160628160046j:plain

ユーザーIDとユーザー名を入力して、保存ボタンをクリックしてから、プログラムを再起動すると、
f:id:shinya_dog:20160628160308j:plain

期待していた通りに表示されました(*^o^*)


次は、iOS側の処理になりますが、先ほどのDependeSettingInfo.csはiOS側のプロジェクトでもそのまま動きます。
なので、Visual Studioの機能でリンクを貼ります。

iOSプロジェクトを右クリックして[追加]→[既存の項目]を選択
f:id:shinya_dog:20160628160748j:plain

先ほどDroid側に追加したDependeSettingInfo.csを、リンクとして追加します。
※もちろんリンクとして追加しなければならないということはありませんよ。
f:id:shinya_dog:20160628161038j:plain

そして、Droidの時と同じように、プログラムの開始時に、冒頭で宣言していたstatic変数にインスタンスをセットするようにしておきます。
[XFApp1.iOS\AppDelegate.cs]

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

using Foundation;
using UIKit;
using XFApp1.Droid;

namespace XFApp1.iOS
{
	// The UIApplicationDelegate for the application. This class is responsible for launching the 
	// User Interface of the application, as well as listening (and optionally responding) to 
	// application events from iOS.
	[Register("AppDelegate")]
	public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
	{
		//
		// This method is invoked when the application has loaded and is ready to run. In this 
		// method you should instantiate the window, load the UI into it and then make the window
		// visible.
		//
		// You have 17 seconds to return from this method, or iOS will terminate your application.
		//
		public override bool FinishedLaunching(UIApplication app, NSDictionary options)
		{
			global::Xamarin.Forms.Forms.Init();

			//設定操作クラスのインスタンスを生成
			App.MySettingInfo = new DependeSettingInfo();

			LoadApplication(new App());

			return base.FinishedLaunching(app, options);
		}
	}
}


同じようにテストしてみると、期待通りに動作しますよね?

UWPの場合は、Droidで作成したクラスをそのまま使う訳にはいきませんが、同じように抽象クラスを継承して作って、UWPで動作するように実装して、プログラム起動時にインスタンスがセットされるようにすれば、動作するはずです。



ここに書いた方法は、Xamarinの技術ではないのであえて書かなくても皆わかっていることかもしれませんが、Xamarinをキーワードに探すとあまり出てこない気がしますし、Xamarin初心者にとっては、動きそうだけど動くのかな?という不安があったのも事実なので、後に続く人の参考になればと書いておきました。

汎用的に使用するメソッドとかであれば、もちろんDependency Serviceを使用した方が良いと思いますが、方法論の一つとして、これも検討の価値はあるのではないかと思います。
もしも問題等あればご指摘下さい。

AndroidのMasterDetailPageから、アプリケーションアイコンを消すには


MasterDetailPageを使うと、Androidの時にはアプリケーションアイコンが表示される。f:id:shinya_dog:20160627162727j:plain

これが、iOSWPFの時には出てこないし、その左側にメニューのアイコンが表示されることもあり、消したいと思うケースがあると思う。
Androidなら出すべきとかは、とりあえずおいておいて・・・)

その場合は、DroidプロジェクトのResourcesフォルダに、valuesフォルダを追加し、スタイル定義用のxmlファイル(ここでは、MyStyle.xml)を追加する。
f:id:shinya_dog:20160627163208j:plain

<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <!-- <style name="CustomTheme" parent="android:Theme.Holo.Light"> -->
  <style name="CustomTheme">
    <item name="android:icon">@android:color/transparent</item>
  </style>
</resources>


あとは、MainActivity.csを開いて、ActivityAttributeにThemeを追加してあげることにより、表示されなくなります。

using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace XFApp1.Droid
{
	[Activity(Label = "XFApp1.Droid", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
		Theme = "@style/CustomTheme")]
	public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
	{
		protected override void OnCreate(Bundle bundle)
		{
			base.OnCreate(bundle);

			global::Xamarin.Forms.Forms.Init(this, bundle);

			Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();

			LoadApplication(new App());
		}
	}
}

f:id:shinya_dog:20160627165355j:plain

Microsoft.Azure.Mobile.Clientを追加すると

Xamarin.FormsでMicrosoft.Azure.Mobile.Clientを使用したくて現在はまり中(--)

まず、テンプレートで、Xamarin.Forms(UWP/Android/iOS)を選択して、
何度かリビルドしたりソリューションを再起動したりして、エラー一覧に警告も何も出ない状態にして、UWP/Android/iOS、それぞれデバッガー上で実行して正常に起動することを確認。

f:id:shinya_dog:20160624172340j:plain

 

その後、ソリューションを右クリックして「ソリューションの NuGet パッケージの管理」を選択して、

f:id:shinya_dog:20160624172629j:plain

 

全てのプロジェクトに「Microsoft.Azure.Mobile.Client」をインストールしてリビルドすると、

f:id:shinya_dog:20160624172907j:plain

 

この時点でのエラーと警告はこんな感じ。

f:id:shinya_dog:20160624173445j:plain

 

とりあえずこの時点で、AndroidiOSデバッグ実行すると、起動することは確認。

f:id:shinya_dog:20160624173957j:plain

 

そして、Azureのポータルのクイックスタートで表示されたとおりに、MobileServiceClientのインスタンス作成コード(using も当然追加)を、App.csに追記する。

f:id:shinya_dog:20160624174647j:plain

 

この状態で、再度AndroidiOSデバッグ実行すると、Androidは問題なく起動するが、iOS側はエラーが発生してしまう。

f:id:shinya_dog:20160624175021j:plain

 

もちろん、警告は出ているのでここらにヒントがあるような気もしますが、単純に支持されたとおりに、AutoGenerateBindingRedirectsをtrueにしただけではダメそうですね。

 

2016/06/27追記
どなたか、解決方法ご存じの方いらっしゃいませんか?
facebook「Japan Xamarin User Group(JXUG)」で下記のアドバイスをいただきました。ありがごうございます!!

 

Azureポータルのクイックスタートには、特にそのような記述はなさそうですが、どうやら、iOSのプロジェクト側では、MobileServiceClientのインスタンス作成前に、初期化処理が必要みたいです。

f:id:shinya_dog:20160627103252j:plain

 

iOS側のプロジェクトのAppDelegate.csでAppクラスのインスタンス作成前に、

 Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();

の一行を追加したらとりあえず起動はできました。
無事、動作するのかは別問題ですが(笑)

 

ちなみに、起動は初期化処理を入れなくてもできますが、どうやらAndroid側にも同様の初期化処理が必要みたいです。

f:id:shinya_dog:20160627110022j:plain