Xamarin.iOSで音声の再生が途中で止まる

効果音を実装していたのだが、なぜか再生が途中で止まる。

バックグラウンドで再生を続けているBGMはちゃんと動くのに効果音だけが止まる。はまった。

最初にしていた実装はこれ。

         public void StartPlayEffect(string title)
        {
            var url = NSUrl.FromFilename(title + ".mp3");

            NSError _err = null;

            var effect = new AVAudioPlayer(
                        url,
                        AVFileType.MpegLayer3,
                        out _err
                     );

            effect.NumberOfLoops = 0;
            effect.PrepareToPlay();
            effect.Play();
        }

効果音なので使い捨てで良いだろうと思ってローカル変数として実装してたのが間違い。

インスタンス変数に変えたら動いた。

おそらく参照がなくなってGCで再生中に消されちゃったものと推測。

Xamarin.iOSでオレオレ証明書の許可とクライアント証明書の送信

iOSのアプリでクライアント認証が必要だったので四苦八苦。

なんとか共通部で実装を試みるも、WebRequsetHandlerは存在せず、Certificate周りのメソッドを無理矢理上書きしたハンドラを作るも動いてくれず。

結局、iOS側の実装でNSUrlSessionの実装をゴリゴリ書くことに。

以下の処理をDependencyServiceとして実装。

 using System;
using Xamarin.Forms;
using Hoge;
using Hoge.iOS;
using Foundation;
using Security;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;

[assembly: Dependency(typeof(DeviceService))]
namespace Hoge.iOS
{
    public class DeviceService : Hoge.IDeviceService
    {
// HttpResultは適当な自作クラスです
        public async Task<HttpResult> Get(string urlString)
        {
            var url = new NSUrl(urlString);
            var request = new NSUrlRequest(url);
            var myConfig = NSUrlSessionConfiguration.DefaultSessionConfiguration;
            var session = NSUrlSession.FromConfiguration(myConfig, new MySessionDelegate(), new NSOperationQueue());
            var task = await session.CreateDataTaskAsync(request);

            if (task.Response == null)
            {
                return null;
            }
            else
            {
                return new HttpResult()
                {
                    StatusCode = (int)((NSHttpUrlResponse)task.Response).StatusCode,
                    Body = task.Data.ToString(NSStringEncoding.UTF8).ToString()
                };
            }
        }


        public class MySessionDelegate : NSUrlSessionDelegate
        {
            public override void DidReceiveChallenge(
                NSUrlSession session,
                NSUrlAuthenticationChallenge challenge,
                Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
            {
                if (challenge.PreviousFailureCount > 0)
                {
                    completionHandler(NSUrlSessionAuthChallengeDisposition.CancelAuthenticationChallenge, null);
                }
                else if (challenge.ProtectionSpace.AuthenticationMethod == "NSURLAuthenticationMethodServerTrust")
                {
// 決められたホストにしか繋がない前提
                    completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, CreateCredential());
                }
                else if (challenge.ProtectionSpace.AuthenticationMethod == "NSURLAuthenticationMethodClientCertificate")
                {
// 決められたホストにしか繋がない前提
                    completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, CreateCredential());
                }
            }

            private NSUrlCredential CreateCredential()
            {
                var identityRef = SecIdentity.Import(App.GetApp().GetCert());
                //SecCertificate cert = new SecCertificate(App.GetApp().GetCert());
                var credential = new NSUrlCredential(identityRef, null, NSUrlCredentialPersistence.None);
                return credential;
            }
        }

    }
}

アプリ埋め込みの証明書をロードする処理をAppに実装
証明書は秘密鍵付きで、ビルドタイプをEmbeddedResouceとして設定。

         public X509Certificate2 GetCert()
        {
            X509Certificate2 cert = new X509Certificate2();
            using (Stream CertStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "証明書ファイル.p12"))
            {
                byte[] RawBytes = new byte[CertStream.Length];
                for (int Index = 0; Index < CertStream.Length; Index++)
                {
                    RawBytes[Index] = (byte)CertStream.ReadByte();
                }

                cert.Import(RawBytes, "パスワード", X509KeyStorageFlags.DefaultKeySet);
            }
            return cert;
        }

Xamarinの自動プロビジョニング

これ今ちゃんと動くのかな??

バンドル識別子「*」はダメだよってエラーになってできない

調べると特定の権限がONになってるとできないらしく、デフォル設定と見比べると、アプリ内購入がONになってるのが原因にも見える。

だけど、OFFにしてみてもやっぱりダメ。

なんでなん?

二つ名アプリの売り上げ動向

Qiitaに投稿して意外と好評だった二つ名のアプリですが、ではどれぐらい効果があったのか公開します。

iOSのダウンロード数です。「いいね」をいっぱい頂いていた期間でダウンロードが伸びてますが、それでも60件程度。

Androidはこんな感じでした。

インタースティシャル広告を入れてたのですが、その売り上げはというと・・・

100円程度でした。

ちゃんちゃん。

昔作ったFlashゲーム

http://megyo.jp/games/summer/adv2.html

htdocs配下を見てたら昔作りかけていたFlashゲームの残骸を見つけた。

今見ると意外と頑張ってた。

当時、ブラウザゲームに適したジャンルは何かを考えた結果、アドベンチャーゲームという結論に達したのでFlashでゲームを作ってみてた。

シナリオ、シナリオ分岐やBGM、選択肢などをXMLファイルとして外出しして、XML(シナリオ)と絵、BGMを用意したらゲームが完成するようにフレームワークを作っていた。
#開発中だったのでBGMはペルソナのBGMを仮で流してたっぽい

スマホアプリのナビゲーション・ボタンの配置

こんなアプリ出してます。

https://itunes.apple.com/jp/app/id460679511?l=ja&ls=1&mt=8

元々は、5年以上前に自分が欲しかったものをFlashBuilderで作って、今年Xamarinで焼き直したものです。

#ちなみに初回リリースの当初は結構レビュー評価良かったのですが、途中からエロ漫画の広告が出てきてしまってすごいレビュー下がりました。。。

こだわりのポイント

私個人としてはスマホのボタン等の配置は画面下部に集約したいのです。片手で操作できるように。

結果、こんな画面に。

下の方に色々集約されてます。

文字が切れてるボタンがある部分は、スライドすると後続のボタンが見れるようなってます。

ちなみに文字列の入力フォームにフォーカスがあたると、上側に表示されるようになります。

仮想キーボードが邪魔にならないようにするためです。


ボタン群が3行に渡るのはいけてないのですが、個人的には2行ぐらいなら良いのではと思ってます。

仕事で同じような設計して、「押し間違いそう」といった指摘を受けましたが、「しそう」なだけで実際にはそんなに間違わないのではと思ってます。

なぜこんな記事を書くかと言うと、やりすぎて「他にあまりみない画面はいくない」と言う指摘があったので、「他にもあるよ」と言う状態になって欲しくて情報発信しました。

アドラーの心理学

以前、アドラーの心理学を読みながらマインドマップを書いてたもの。

色々あるけど三行で言うとこんな感じ。

・人は人の役に立ったと思う時に幸せを感じる

・実際に役に立ったかどうかは関係なくて、自分がどう思うかが重要

・見返りの要求や承認欲求があるとそれは自分のための行動となる

悪く言えば「独りよがり」。だから「嫌われる勇気」と言うタイトルなのかな。