ワード駆動開発

https://qiita.com/megumu-u/items/576e0967bc357314df1b

Qiitaに記事を投稿したのですが、「二つ名オンライン」という名前だけで、ネタを膨らませて作ってみました。

ここで私は提唱します(提唱はしない)

ワード駆動開発

あるキーワードだけでプロダクトを作る手法です。

自分のインスピレーションに引っかかるものがあるのなら、それはきっと意味のあること、人を惹きつける何かがあるということ、という前提に立ち、そのワードから内容を発展させていきます。

二つ名オンライン
オンラインゲーム
ソシャゲー
ソシャゲーといえばコレクション
二つ名をコレクション

で、なんとかゲームっぽくしてみました。

ほら、キーワードだけで何か作れそうな気がしませんか?

最近思い付かんだワードとして「全は一、一は全」があります。これは鋼錬で出てくるワードです。

サブタイトルもつけて「全は一、一は全〜みんな私になっていく〜」としたら、ほらなんか楽しそう。

他人を侵食していくソシャゲーにできそう。

「昨日まで田中というアカウントなのに今日は吉田になってるー」みたいな。

これがワード駆動開発。

※本気にしないでね

Xamarin.FormsでLottieを使ってみようとしたらビルドでエラーが出るようになった

resource fork, Finder information, or similar detritus not allowed

ビルドするとこんなエラーが出るようになった。

これ自体はググれば対処法は出てきて、ファイルに設定されている属性の問題でxattrと言うコマンドをbin配下に適用したら解決できる。

が、何度解決させてもビルドしなおすとまた発生する・・・

原因はAdobe Affter Effectsから出力したjsonでした。

BodyMovinから出力したjsonファイルがダメだったらしく、jsonファイルにxattrコマンドを使えば問題は起きなくなりました。

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円程度でした。

ちゃんちゃん。