TIME HACKER Version 1.1.0 ウィジェット対応と新規アイコンを追加しました

いつもTIME HACKERをご利用いただきありがとうございます。

今回のバージョン1.1.0で以下の対応を行いました。

  • ウィジェットでも記録できるようになりました。
  • ご要望のあったアイコンを追加しました。

ウィジェットでも記録できるようになりました。

ウィジェットにTIME HACKERを表示していただく事で、アプリを起動しなくても行動の記録が取れるようになりました。

これにより、更に日々の行動が記録しやすくなりましたので、是非ともご利用ください!

f:id:project-unknown:20180821000617p:plain

ウィジェットに表示されるアクションは、アプリに登録してあるアクションの上から6番目までが表示されます。

また、記録を計測している場合は、以下のような画面となります。

f:id:project-unknown:20180821000820p:plain

このように2件までの記録中のアクションが表示されます。

また、ストップボタンを押すことで、計測を終了することも出来ます。

ご要望のあったアイコンを追加しました

f:id:project-unknown:20180821001325p:plain

今回は育児系のアイコンのご要望を戴いたので、6種類のアイコンをご用意致しました!
是非ご利用ください!


まだTIME HACKERをお使いでなければ、是非ともこの機会にTIME HACKERをご利用ください!

img


ヘルプページ
ご意見・ご要望
TIME HACKER関連記事

Firebaseのエラーへの対処 - Terminating app due to uncaught exception 'com.firebase.core', reason: 'Default app has already been configured.

はじめに

iOSのFirebaseでは、以下を二重に呼ぶ事は禁止されています(クラッシュします)

FirebaseApp.configure()

この時のクラッシュの内容は以下のようになります。

Terminating app due to uncaught exception 'com.firebase.core', reason: 'Default app has already been configured.

しかし、TodayExtension等、回避しづらいケースがあり、その解決法です。

FirebaseApp.app()を使う

要はFirebaseApp.configure()は、FirebaseAppのオブジェクトを生成し、設定を行う事ですので、
インスタンスが生成されていないかをチェックすれば回避出来ます。

import Firebase

class TodayViewController: UIViewController, NCWidgetProviding {

    override func viewDidLoad() {

        // 省略

        if FirebaseApp.app() == nil {
            FirebaseApp.configure()
        }

        // 省略
    }


}

TIME HACKER 伝わる不具合の報告方法とは

はじめに

どうも、どうまずです。
今回の新アプリのTIME HACKERでは、テストを中心にプロジェクトへの参加をさせてもらいました。

テストで一番大変だったのは・・・不具合の報告です。

  • メールで不具合を報告したのに、伝わらない。
  • 不具合一覧の内容が何を書いているのかわからない。

そんな場合に参考にして下さい。

不具合報告の大変さ

f:id:project-unknown:20180729194226p:plain

なんで、不具合報告って大変なんでしょうか・・・
今までの不具合報告の内容から、何個かパターンを出して見ました。

画面名、機能名を適当に書いている

例えば、TIME HACKERを例にしますと、


レポート画面でレポートが表示されない。


こんな感じの不具合報告です。
この報告で何が一番困るかというと、
「レポート画面ってどこのこといっているの?」
って話になります。

TIME HACKERをお使いの方はわかるかと思いますが、TIME HACKERには

  • レポート-行動履歴
  • レポート-アクション毎
  • レポート-時間管理

と3つのレポート画面があり、どの画面をさしているのかよくわからないのです。

簡単な話かもしれませんが、意外と出来ていない場合が多い失敗報告例です。

不確定要素が含まれる不具合報告

場合によっては、不具合解消までの時間が大幅に伸びるパターンです。
これは、過去私が会社で報告された内容です。


他に何も作業していないのに、A機能の処理が遅延した。


という報告ですが、ログを確認すると、裏でDBバックアップをやっており、遅くなっていたという問題でした。
このように確認していないけど、さも確認したかのような報告がされてしまうと、不具合の解析が大幅に遅れ、最悪迷宮入りです。

不具合を報告するときは、確実な情報を伝えましょう。

  • 理論的に動かない
  • 仕様的に動かない

という場合、ログなどで「動いていないこと」を確認しましょう。
だって、理論的に仕様的に完璧だと思って動かして、発生した不具合なのですから。

また、SEならば、

  • ユーザからの報告

これも不確定要素です。
ユーザはシステムの細部までわかっていない場合が多いので、誤った内容になることが多いです。
そのため、ユーザからの報告はヒアリングしたり、実際に動かして確認を取らないといけません。

文章の不具合報告

これは実際にTIME HACKERで私どうまずが報告した内容を例に見て見ましょう。


レポート時間管理の下部の日付(6/15)をタップして、
リスト形式で「確保したいアクション」を表示している画面に移動して、
レポート時間管理画面に戻ると、日付が6/16になっている。


・・・自分でも分かり難い。
不具合を発見した当初はこれでも分かりやすい報告をしたつもりなのですが・・・

この報告は複数の動作をした結果、日付が正しくなかったと言っています。
しかし、複数の動作を1つの文にしてしまったため、どんな動作をしたのか分かりにくいです。
また、1つの文の中に不具合の内容も書いているので、結局何が不具合なのか分かりません。

もちろん、この不具合報告は「何いっているかわからない」という結果になってしまいました。

そのため、下記のように書き換えました。


1.レポート時間管理画面で日付をタップして6/15に変更
2.レポート時間管理画面のグラフの下の日付6/15をタップ
3.時間がリスト形式で表示される画面に遷移
4.レポート時間管理画面に戻る
5.レポート時間管理画面の日付が今日日付(6/16)になってる。

5の日付は6/15が正しいと思います。


箇条書きにすることで、以下のメリットがあります。

  • 動作を1つずつ記載できる。
  • 不具合の再現がし易い。
  • 機械的に事実を淡々と記載することができる。

文章にすると、文章に含まれる意味や内容を読み解く必要があります。
箇条書きにすることで、事実のみを淡々と報告していることが伝わり、余計な労力は不要となります。

テキストハレーションを生まないようにする。

テキストのやりとりは相手の顔が見えません。
そのため、余計なハレーション(悪影響)を及ぼすことがあります。

これも実際の例です。

レポート時間管理のグラフが正しく表示されない不具合があり、その報告をslackで正しく表示されない画像を送付して、


こんな風になっちゃったよ


と、何気なく送ってしまったのです。
その後、


これは、煽ってるの?


と返信があり、全力で謝罪しました。

不具合報告は、嫌な言い方をすると、
相手の失敗を見つけて、指摘する、かなりネガティブな報告なのです。
かなり注意して報告しましょう。

あと、新人の時は、バグという言葉を使いたがる傾向にあるようですが、

「バグってる」

という報告は、悪く言うと「お前、ミスってんぞ」と同意義です。
軽々しく使うとテキストハレーション発生ですから要注意です。
(新人のときはバグと思っても自分が知らない仕様があると思って、「仕様か確認して下さい」と言った方がいいです。)

再テストの結果は早めに

不具合報告が終え、ソースを修正してもらったら、修正が正しく行われているか再テストをして、やっと不具合修正が完了です。

「ソース修正しました」

という報告を受けた側は、「修正終わったんだ〜」くらいに捉えてしまうことが多いです。

しかし、修正した側は

  • 他に想定外や考慮不足の点がないか
  • 修正した結果、他の不具合を発生しないか
  • そもそも、修正した箇所とは別のところで不具合が発生してるんじゃ

と、気が気ではありません。

速やかに再テストを実施し、結果を報告しましょう。

結論

f:id:project-unknown:20180729194352p:plain

不具合報告をするときは、次のことを気をつけましょう

  • 画面名・機能名は正確に記載する。
  • 不確定要素は記載しない。(確認してから記載する。)
  • 箇条書きで機械的に記載する。
  • 不具合報告はテキストハレーションを起こしやすいから要注意する。
  • 再テストの結果は速やかに報告する。

これだけで、かなりわかりやすい内容になると思います。
ぜひ参考にしてみて下さい。

最後に・・・

記載したようなハレーションや問題が数々ありましたが、
「いいものを作りたい」という目的を共有し、メンバー一丸となり完成させたTIME HACKERをぜひ使ってみてください!

www.project-unknown.jp

う〜のん小話

f:id:project-unknown:20180727021019p:plain

※このいちご大福は生きているので
間もなくいちごが取り込まれ、ただの大福になります

デザイン担当のぽぽたです。

プロジェクトのマスコット担当のう〜のんですが
実は...う〜のんはいちごオレでできていま、した。初期設定では。
当時の私の大好物だったもので...。

今現在その設定は(いちごオレの飲み過ぎにより満足してしまったため)なくなりましたが
絶対甘いんだろうな...と、う〜のんに対する食欲を抑えられません。

う〜のん語録

うなな:バナナ
うまいらる : おいしいもの、おやつ、食べること


いちご大福 6個入り


その他のデザイン・イラスト関連の記事

その他のう〜のんに関する記事は、以下のページから見れますよ!

www.project-unknown.jp

う〜のんがこれまで登場した作品

ぱたぱたう〜のん

f:id:project-unknown:20180510010406p:plain:w300

www.project-unknown.jp

TIME HACKER メイキング

TIME HACKERリリースしました!

img

TIME HACKERをめでたくリリースすることができました。
まだの人は是非この機会にお試しください!

今回は、TIME HACKERを作るにあたり、その制作過程について記載していこうと思います。
今回の記事では、

  • どんなUX思想で作ったのか?
  • 設計思想はどうしたのか?
  • アプリ実装面での設計は?
  • マネタイズについて

上記を、書ける範囲で載せましたので、 これからアプリを作ろうとしている人へは、制作過程の参考に、
同業者の方へは、苦難の共感が得られれば幸いです。

TIME HACKERを作ることとなった動機

時間が欲しい

世の中のサラリーマンの殆どがそうだと思いますが、とにかく時間が無い。
平日に、スキルアップのための勉強や、アプリ造りを行いたくても、家に着くのが日付をまたがる時間帯になる為、中々実施することができません。

休日にやろうと思うのですが、、いざ休日になったら平日の疲れを癒やすために、ガッツリ休んでしまう等、自分のやりたいことがやれない毎日でした。

もう社会人生活も10年を超えてきて、一向に改善しない毎日に諦めの気持ちすら抱いていた際に、一冊の本と出会いました。

ライフハック大全

ふと手にとった、以下の「ライフハック大全―人生と仕事を変える小さな習慣250」という本が私の中で革命的でした。

何個もあるライフハックは、殆どが私の血肉になる位に、良い知識を仕入れる事ができたのですが、その中でも、最も私の中でためになったのは、

自分の1日の行動ログを取って、無駄に過ごしているところは無いか?を見返すことで時間を捻出する術

これにものすごく感銘を受け、その時のツイートが以下。

実際にこのライフハックを実施してみました。
私は1日の中で、以下の時間をもっと取りたいと考えています。

  • 読書をして自己啓発と勉強
  • PCを使ってプログラムや新技術の勉強
  • 個人アプリ、ゲームの開発

この観点を元に、1日の行動ログを記録し、時間を生み出すことが出来ないか分析してみます。
(メモ帳に走り書きしていた為、時間が入っていたり入っていなかったりしてます。)

  1. 8:00 起床
  2. 出る準備 20分 8:20
  3. 家を出てからバスにならぶ 10分 8:31
  4. バス乗車時間 13分 8:45
  5. 電車待ち時間 16分 9:02
  6. 特急 30分 乗り換え後、○○から△△ 20分 計50分 9:50
  7. 以降会社
  8. エレベーター待ちと朝の準備 16分 10:06
  9. タバコ 5分
  10. インシデント対応 41分 10:53
  11. 今日のタスクプランニング 9分 11:00
  12. ○○ミーティング 24分 11:27
  13. トイレとタバコ 15分 11:45
  14. 今年度のプロジェクト目標について議論 15分 12:00
  15. 昼飯と移動 30分 12:30
  16. タバコ 15分 12:45
  17. 目標についての議論 15分 13:00
  18. 溜まっていたSlackやり取り 21分
  19. タバコ 15分 13:40
  20. 雑務 15分
  21. 新規企画のKPI試算 vol1 45分 14:40
  22. タバコ 10分
  23. 新規企画のKPI試算 vol2 48分 15:45
  24. タバコ10分
  25. 部下面談 36分
  26. タバコ 10分
  27. ○○チーム向けプレゼン資料作成 10分
  28. △△MTG 40分
  29. ☓☓MTG 1時間30分
  30. タバコと帰る準備 10分 19:30
  31. ○○まで移動 20分
  32. △△まで 35分
  33. ジムで運動 1時間
  34. ジムの風呂 30分
  35. 買い物 10分
  36. バス停まで移動 5分
  37. バス待ちとバス乗車 18分
  38. タバコ 5分
  39. 家移動2分
  40. 家に着いて落ち着くまで 7分
  41. ダラダラ過ごす 2時間
  42. 風呂とダラダラ 30分
  43. 就寝 1:17

この日を振り返って見ると、例えば、以下を改善できそうです。

タバコ時間を読書時間に企てる

タバコの時間をタバコ + スキルアップのための読書(Kindle)を行うとしたら、80分読書時間が確保出来ます。
全てのタバコの時間を企てるのは難しいのと、読書の開始時には思い返しの時間も必要な事を鑑みても、50分は読書時間が確保できます。

移動時間を読書・勉強時間に企てる

また、バスの時間がトータル31分。電車の時間は特急が65分、在来線が40分。
バスと在来線は基本立つので、ここを読書に当てると71分。

特急は座ることが出来ますが、片道30分程度なので開発するには時間が足りなく見えるので、PCを使った勉強時間に企てると、65分勉強時間が確保できます。

ここまで考えると、家に帰るまでに、以下の時間を確保することがわかります。

  • 読書時間を121分
  • 勉強に65分

ただ、この勉強・読書時間は断片化した時間なので、効率は悪いですが、それでも家に変えるまでに、これだけの事が行えるので、

家では、アプリ開発に集中することができる。いやアプリ開発だけしていていいのです。

これまでだと、「勉強もしないと」「本も読まないと…」と考えがよぎって中々集中出来なかったのですが、
一日の無駄な時間、スキマ時間に十分に勉強を行えているので、家でやる必要が無いのです。

そして、家でダラダラとする時間を2時間から半分削るだけで、1時間確保出来ます。
更にストイックに削れば1時間半〜2時間確保できるでしょう。

ここまで見てわかるように、

1日の時間を細かく記録する事と、分析する事は、忙しいビジネスマンに取って、ものすごく有益である

ことがわかります。

そして、有益で有ることがわかった次は、もっと楽に記録したい・分析したいと思い、今回の「TIME HACKER」アプリに繋がります。

私自身の生活の質を向上させるために、どうしてもこの手のアプリが欲しかったのと、私の中でしっくり来るアプリがStoreに並んでいなかったので、何としてでも手に入れたかった。

気がつけば、全ての開発や勉強を停止して、着手していました。

(なんだかんだで7ヶ月も造ってた…)


仕様検討

コアバリューを決める

いつもは凄い悩む所だったのですが、ここは作り出した動機が動機なので、すぐに固まっていました。

アプリ制作初期は

無駄な時間を管理し、自分のスキルアップに企てたい時間を確保する

で、最後までここは変わらなかったのですが、
もっと範囲を広げて、以下を最終的なコアバリューとしました。

自分の時間を管理し、生活改善のサポートをするアプリ

ターゲット層を考える

ターゲット層は、女性でも・男性でもありませんし、
特段ペルソナも考えていません。

TIME HACKERでは、以下のユーザーをターゲットとして捉えています。

  • 気づいたら時間ばかり過ぎている人
  • 忙しくて自分の時間が確保出来ない人
  • 自分のスキルアップの時間を確保したい人
  • 日々の生活をもっと有意義に送りたい人

UXを検討する

大体のアプリに言えることですが、
このアプリはUXが特に肝となります。

  • 何より、毎日、しかも小刻みにアプリを立ち上げて行動のログを計測しないといけない。
  • それには、ユーザーのアプリ上の動作だけではなく、生活のリズムを一瞬でも妨げてはいけない。
  • 特に、行動ログを記録忘れた場合は、一気に記録するモチベーションが下がります。なので記録忘れを防がなければならない。
    • (結局、ここのサポート部分は1stリリースからは見送りました。)

上記をいろいろ考えてUXを造っている際に、取っていたメモが以下です。

f:id:project-unknown:20180528223801p:plain

f:id:project-unknown:20180528223811p:plain

結局、当時考えていた機能の半分くらいしか実現できていないのですが…。

UIを検討する

ここもUX並にメチャクチャ苦労しました。
なんだかんだで、2ヶ月近く、あーでもないこーでもないをやっていたんじゃないかしら。

ちなみに、最初期の頃に考えていたUI

f:id:project-unknown:20180713010102p:plain:w400

全く違いますね…。
今見ると、個人的に結構しんどいUIですが、当時はこれでいける!!と信じて止みませんでした。
結局、2日位、寝かせてから再度この画像を見て、くっそだせぇって思って、やり直しました
その時の温度感だけで決めるのではなく、一度寝かせるのは大事ですね。

次に考えたのが、以下のUIです。
文字情報を見るのではなくて、行動をアイコン化し、なれたら無意識で記録ができる事を狙っています。
また、無意識で押せる事を狙って、アイコンも結構大きめに設定してます。

f:id:project-unknown:20180713010403p:plain:w400

だいぶ、今の原型が見え隠れしていますね。
ここから、紆余曲折を経て、以下の様な画面で落ち着きました。

f:id:project-unknown:20180713010500p:plain:w400

マネタイズを検討する

TIME HACKERで、現在実装済みのマネタイズをご紹介します。
今後のマネタイズは敢えて載せません。
お金の匂いがプンプンする機能が追加されたら、「あっ、マネタイズに走ってるな」とでも思ってください。

  • アプリトップにバナー広告
  • 一定の回数計測を行った際のインタースティシャル

バナー広告

バナー広告は、KeyHolderでも実装しているのと、KeyHolderでも操作の邪魔にならないところに設置していますが、結構馬鹿にならないくらいの収益を上げてくれます。
UXの所でも述べていますが、TIME HACKERは、UXがとにかく命です。ユーザの行動を阻害する事をしようものなら、記録が面倒になり一気に使わなくなります
なので、バナー広告を設置するにあたり、アプリの操作を阻害する事はしないような画面設計にしています。

インタースティシャル広告

KeyHolderでは、インタースティシャル広告を出さずに本当に公開しました。
というか、KeyHolderはシンプルを極めすぎてインタースティシャル広告を入れる余地が無く、入れようものならアプリとしての価値を大きく欠損しかねるので、断念しました。

なので、TIME HACKERではインタースティシャルは仕様検討の総初期から、なんとかして入れられないかを深く検討しています。
(書けば書くほど、金儲けが全面に出てしまうので、引かれるリスクはありますが、それでもこの記事を記載しています)

バナー広告の項でも記載しましたが、TIME HACKERは何よりUXが命なので、インタースティシャル広告を入れる箇所には最新の注意を払っています。

TIME HACKERでインタースティシャスは、一定の時間、一定の回数、行動ログを取った際に、行動ログを記録したタイミングで広告表示を行っています。
これは、ユーザーとして考えた際に、行動ログを計測する直前に広告が表示されたら、ユーザーの行動を阻害するリスクが非常に高まる為、記録を開始したタイミングで広告を表示することにしました。

行動ログを記録した後に広告を表示しているので、マネタイズとしての期待値は大きく下がると思いますが、私が考えている一番のリスクは、

眼の前の収益に目がくらんで、ユーザの行動を阻害する事で、アプリをアンインストールされる事を一番のリスクとして捉えています

また、上記で一定の回数と記載しましたが、初回リリースまでにどの回数が妥当なのか?の結論を出すことが出来ませんでした。

ですので、FirebaseのA/Bテストを採用して、インタースティシャル広告を出すタイミングの検証を行っています。

www.project-unknown.jp

ここまでは、アプリ設計のお話でした。
次に、実装面でのお話をしていこうと思います。

実装開始

さぁ、ここからお祭りの始まりです。
いやぁ…とにかく紆余曲折しまくった…。

プロトタイプ開発

今回のアプリ開発では、特に真新しい事を中心に、やりたいことは何でもやろうと思い色々tryしました。

大きい所で言うと、

Carthageにも挑戦しましたし

www.project-unknown.jp

Quickにも挑戦しましたし

www.project-unknown.jp

RealmSwiftにも挑戦しました

www.project-unknown.jp

Firebaseにメインの解析機能を移管しましたし、
Firebaseを使ってA/Bテストにも挑戦しています。

www.project-unknown.jp

以下の「Code Complete」等の技術書も読み漁り、開発設計についての色々な挑戦も行いました。

そして気づいたのが、

tryばっかしていて、いつまで経っても完成しない

特に、開発設計のところは、色々な本を読みすぎて、読む本全てに影響を受けて設計が日々変わりすぎていっているのが色々と足を引っ張りました。
こんなところにもエターナル現象は顔をだすんですね。

なので、プロトタイプとしてある程度のものが出来上がったタイミングで、tryは封印し、アプリ完成にフォーカスする事にしました。

以後は、アプリ制作時に大きな節目について記載します。

DBシステムの変更

これまでDBをRealmSwiftで開発していたのですが、
諸々の事情で、結局CoreDataに移しました。

RealmSwiftはCoreDataと違い、Objectを生成して値を突っ込むだけで物理領域にも保存され、直感的にデータを扱うことができる優れもので、開発当初はRealmSwiftを崇拝してやまなかったです。

しかし、

  • DeleteRuleが使えない
  • マイグレーション時に、意図しないデータが潜り込む
  • トランザクション範囲が操作し辛い

等、不満が次々と湧いてきて、最終的にCoreDataにデータを移管しました。

ココらへんは、ここに詳細を記載しています

ただ、RealmSwiftとCoreDataとでは、やはりアプリ設計が異なってくる為、ここを直すために、結構作り直しを余儀なくされました。

(それでもDAOを全て直す気力は無く、一部のDAOはRealmSwift前提とした設計のままになってしまっています)

アイコンをやっぱり全部自分らで造ることにする

開発完了が大きく遅れたのは、ここが原因です。ですがこれは英断だと思っています。

これまで、アイコンをフリーで提供してくださっているサイト様から使わせてもらうつもりでおり、完成直前までこれで行くつもりまんまんでした。

ただ、せっかく造るんだからアイコンも自作する事に決め、ぽぽたが泣きながら作業開始しました。

元々100を超えるアイコンを他サイト様のフリー素材から用意していたのですが、
流石に、この量を開発過渡期に用意するのは無理があったので、数を70ほどにしぼりました。

https://cdn-ak.f.st-hatena.com/images/fotolife/p/project-unknown/20180716/20180716161615.png
(作成したアイコン一覧)

この辺の詳細については、ぽぽたが詳細の記事に起こしています。

www.project-unknown.jp

始まるエターナル化

今回一番厄介だったエターナル化は、機能のエターナル化より、コードのエターナル化でした。
一つの機能を造る度に、4,5回リファクタリングを行ったり。
一度仕上げたコードを、設計しなおしたり。
自分が満足するまで何度も何度も作り直してました。

ただ、これって、

システムの保守性だけ上がって、いつまで経ってもリリースできなくなる自体を引き起こしていました

確かに保守性は非常に大事なのですが、とにかく時間がかかる。
プライベート開発なので納期を気にしないというのが更に拍車をかけてしまっていました。

塩梅を調整するのが本当に難しい…。
このリファクタリングも最終的には自分の中で最低限に抑えるようにしました。

例えば、以下の時だけリファクタリングを行うようにしています。

  • DRYを守れていない
  • SOLIDの原則に準拠していない
  • ViewからModelクラスの細かいメソッドにまでアクセスしようとした時

観点は以下です。


DRYを守れていない

似た機能が乱立した場合、開発過渡期になればなるほど、修正漏れによるさらなる不具合、進捗の遅れに響きます。
ここは徹底的に潰しました。
ちなみにDRYが有名になったのは、以下の「達人プログラマー」からですね。この概念は道を踏み外しそうになった際の羅針盤になり得る考えでいつも大いに助けられています。


SOLIDの原則に準拠していない

iOSライクなMVVMとかではなく、またはプロトコル指向でもなく、オブジェクト指向の1つの考えであるSOLIDをとにかく大事にしました。

プロトコル指向で統一したかった所はありますが、Swiftのプロトコル指向は熟成されていません。これを守るがあまりに無駄で汚いコードになりがちです(会社の現場でこれに囚われて、意味不明なコードを書くエンジニアを散々見ています)。

また、何でもかんでもMVVMを採用するのは愚の骨頂です。ただクラスを増やすだけで、自己満足だけ満たされるコードになるでしょう。
こういうのは適材適所であるべきです。

なので、SOLIDの原則だけを準拠することにしています。
これはDRYを包括している思想ですので、DRYルールと非常に相性が良い。

以下は、SOLIDの原則について記載しています。

  • S - 単一責任の原則 (Single Responsibility Principle)
  • O - 開放・閉鎖原則 (Open/closed principle)
  • L - リスコフ置換原則 (Liskov substitution principle)
  • I - インタフェース分離の原則 (Interface segregation principle)
  • D - 依存性逆転の原則 (Dependency inversion principle)

当たり前だけど、気がついたら守れていない原則ですが、ちゃんと守れている時の効果は絶大です。
このルールには非常に気を使いました。


ViewからModelクラスの細かいメソッドにまでアクセスしようとした時

これはSOLIDの一種ですね。
ViewからModelのデータをごにょごにょするのはイケていません。
その場はそれで良いかもしれませんが、後ほど似たようなデータアクセスが必要になる度にインタフェースを作る羽目になります。


終わらないローカライズ

TIME HACKERは日本以外の世界で発信しています。
細かく言うと、GDPRを受け、EU諸国を除いた全世界に発信しています。

アプリ内のローカライズは、元々ローカライズを意識した実装を行う癖があったので、さほど問題無かったのですが、
問題は、ブログに紹介やらヘルプやらをやたらと親切に記載してしまっていた為、これら全てをローカライズする必要ができました

いやぁー、これはアプリをリリースするのを辞めるか悩むくらい霹靂しました。
最後の1ヶ月間は、このローカライズを延々とやっていた気がします。
実際にアプリ以外のローカライズは以下を対応しました。

繰り返しますが、、

アプリのローカライズ以上に、このローカライズの方が遥かにしんどかったです

しかも、後述しますが、最終的にこの1ヶ月の作業は殆ど無駄になり、申請日にプロジェクトメンバー総掛かりで作り直してます。

最後の追い込み

具体的な日付だと、2018/7/15(土)にプロジェクトメンバーで集まり、
最終テスト、ヘルプページやプロモーションページの仕上げを行って、
夜までに申請を上げて打ち上げを想定していたのですが…、

相次ぐ不具合
まさかの、申請日にUI変更
UI変更に引っ張られて、全ての画像変更

が発生し、気づいたら申請したのは、2018/7/16(日)の夜でした…。
特に、UI変更が本当にしんどかった…。
これにより、具体的には、

以下の、ストアキャプチャ作り直し

英語版iPhoneX

f:id:project-unknown:20180716013728p:plain

英語版5.5 inch

f:id:project-unknown:20180716013744p:plain

日本語版iPhoneX

f:id:project-unknown:20180716013811p:plain

日本語版5.5inch

f:id:project-unknown:20180716013830p:plain

というか、最後の2日間、殆ど画像の差し替えしかやってない気がする…。


くらうリジェクト

今書いているブログ記事もそうですが、
申請を待っている間に、私含め、ぽぽたもTIME HACKER関連の記事を色々書き溜め、申請が通ったらアプリのリリースも含め、ブログ記事のリリースをするために待ち構えていましたが、ものの見事にリジェクトをくらいました

内容は

2. 4 Performance: Hardware Compatibility
Guideline 2.4.1 – Performance – Hardware Compatibility

iPadでの表示が崩れているから、ちゃんとUI整えろよというご指摘です。

実は、リリース前にiPadで表示が崩れる事を知っていましたし、Appleの審査する人はiPadで審査するということも知っていました。

(iPhoneアプリであっても、iPadに内蔵されているiPhoneシミュレータで動くべきであるという考えなので、私が知る限りでは、iPadで審査を行っていそうです。)

ですが、まぁ、操作できない事ないじゃんという謎の過信で、対応を見送っていたところを、案の定指摘されました。

もう、目も当てられない…。

こちら、OSSを使ってUI表現していた所だったので、ライセンスの問題もあり、手を加えるわけにもいきませんから、その場しのぎとして、表示崩れが発生する端末では、一部機能を削除して再申請しました。

初回リリースで諦めた事

たくさんあります。
本当にたくさんあります。

何故か申請日にiTunes Connectの調子が悪くて、iTunes Connectにバイナリアップロードするのに数時間かかった為、
心が盛大にポッキリ折れて、気づいていた簡単なバグはそのままにしてしまっていたり、

UXの検討の時に記録忘れ防止のための通知機能も、
通知タイミングを間違えたらユーザーの不満がものすごく上がりそうという、チキンハートがひょっこり顔を出して見送ったり、

本当に色々積み残しています。

ざっくりと列挙すると、以下の機能を初回リリースでは諦めています。

通知機能

上述した通りです

Apple Watch

Twitterに思いっきりApple Watch化すると記載しているのに、工数の兼ね合いという大人の都合で諦めてます。

Widget対応

大人の都合

分析機能の何点か

最終的にTIME HACKERの押しにしていきたいのですが、
まずは、ユーザーの行動ログを貯めることが何より優先すべき事だったので、ここの機能も結構諦めています。

アプリ内課金

やりたかった…。
本当にやりたかった…。
できなかった…。

KPT

締めくくりとして、今回のTIME HACKERのKPTを考えたいと思います。

Keep

自分の信念を最後まで貫いた

ライフハック大全を読んで、是非このアプリを使いたい、そして造りたいと言う思いを最後まで貫けたのは良かった。

このアプリは、万人受けするアプリではないという事はわかっています。
しかし、ターゲット層項目に記載した人に長く使ってもらう事だけを想定して造っています。

エターナル化を防いだ

本当はAppleWatch、Widget、分析機能の強化、Push等もっとやりたかったけど、これを全て捨てました。
初回リリースでは、ユーザーのログを貯めることを何より重要視し、それ以外の機能は二の次にしました
結果として、この判断を行わなければ、リリースは来年になっていたでしょう。

Problem

何でもかんでもやりすぎた

最低限の事だけしかやっていないつもりだったのですが、今考えると色々手を広げすぎたと思います。

特にローカライズはリリース後でも良かったかもしれません…が、iTunes Connectの仕組み上、プライマリー言語を最初から英語に指定していないと、後々更に面倒な事になっていたので、これでよかったのかもしれませんが…。

またヘルプやプロモーションはやりすぎました、これこそリリース後でも良かった。

至る所でチキンハートが顔を出した

RealmSwiftをやめたのも結局の所、チキンハートが問題でしたし、
チキンハートが顔を出したせいで、ハレーションを生みそうな機能を見送ったり、もっと強気に造っても良かった。

プロジェクトとして、集まって作業する頻度が少なすぎた

最近はリモートワークが流行っていますが、やはり対面でやり取りする作業だと、それぞれのクオリティ・速度が全然違います。

特に申請日に集まって、最後のテストを行った際の不具合の出方がやばかったです。
もうちょっと早めに集まってテストするなどすれば、問題の早期発見に気づけたでしょう。

明らかにリスクがあるとわかっていた不具合を放置した

最後のリジェクトの所ですね。
これは本当にもう愚かだったとしか言いようが無い。
面倒くさがって、対応を先送りにした結果としか言いようが無い。

Try

プロジェクトとして集まる頻度を増やす

これはProblemの裏返しですね、クオリティ・速度を上げる意味で、無理にでも集まって集中して造る時間を確保したほうが良いです。

チキンハートをどうにかする

なにかする度に、いや契約が…とかいや請求一気にきたら怖いしー…とか、
出す前にでもでもだってを発揮して、チャンスをことごとく逃しているのがとにかくもったいない。
やる前に後悔するなら、やって後悔するの精神を持とうと思います。

多少面倒でも正しいことをする

これは最後のProblemの不具合と気づいていて目を背けたものに掛かります。
結局バグの放置や不具合の放置は、問題の先送りでしか無いですし、別な言い方をすれば、ローンと同じで先送りにすれば金利(より面倒な工数)が増えるものとし、リスクがあるものに関しては、速攻潰していくマインドは持ち続けなければと痛感しました。

最後に

自分たちで言うのも変な話ですが、TIME HACKERは、自分の時間を生み出す為に有用なツールに仕上がっています。
また、ユーザーの声を聞いて、機能追加やアイコン追加も行っていこうと思いますので、気になる事などありましたら、いつでも、以下のフォームよりお問い合わせください!

TIME HACKER ご要望・お問い合わせ

是非、TIME HACKERを使って、毎日の生活を少しでも改善していきましょう!

RealmSwift vs CoreData

はじめに

iOSのローカルデータベースとして、これまでは、CoreDataが主流でした。
CoreDataの他だと直接SQLiteを弄ったり、ラッパーとなるFMDBなどのOSSがありましたが、Appleが提供している機能として、やはりCoreDataのシェア率は高かったと思います。

実際に、以下はAppleが公開している資料ですが、
CoreDataを採用する事で、純粋にSQLを叩くよりスピードが上がるや、メモリ効率も良い等、夢のようなツールとして紹介されています。

f:id:project-unknown:20180717010316p:plain:w430

しかし、CoreDataは学習コストが非常に高く、扱えるようになるまで時間が掛かったり、 扱えるようになったとしても、他のDBMSライクに扱おうとしたらどハマりしたりと、非常にクセが強いです。

iOSアプリの現場で、CoreDataを採用しているPJ等に配属された際に、困った人も多いのではないでしょうか。

そんな中登場したのが、RealmSwiftです。

f:id:project-unknown:20180717010114p:plain:w430

Realmは、公式サイトから紹介を引用すると

Realm Swiftはアプリケーションのモデル層を効率的に安全で迅速な方法で記述することができます。

実際に使ってみた感想だと、とにかく実装が楽です。
あまり考えなくてもデータの永続化もでき、UserDefaultsみたいなお手軽感があります。

しかし、

結論から先に言うと、TIME HACKERアプリを開発中に、私はCoreDataを選択し、RealmSwiftベースで作ってきたアプリの構造をCoreDataに移しました。

他のサイトでも、RealmSwiftとCoreDataの比較がなされているところが多いですが、私の主観でも書いてみようと思います。

リレーションシップをやろうとすると面倒

RealmSwiftでリレーションシップライクな事をやろうとすると、以下のように記述します。

FirstEntity

import UIKit
import RealmSwift

class FirstEntity: Object {
    @objc dynamic var id = NSUUID().uuidString
    @objc dynamic var second: SecondEntity? = nil                

    override static func primaryKey() -> String? {
        return "id"
    }
}

SecondEntity

import UIKit
import RealmSwift

class ActionEntity: Object {
    @objc dynamic var id = NSUUID().uuidString
    
    let firstEntity = LinkingObjects(fromType: FirstEntity.self, property: "second")  // FirstEntityがぶら下がる
}

上記のように書くと、SecondEntityにFirstEntityがぶら下がる形となり、
SecondEntityにFirstEntityを登録する事で、データを一気に登録・参照する事が可能になります。

これだけなら、非常に便利なのですが、 削除する時(Delete Rule)が問題になります。
RealmSwiftでは、DeleteRuleを設定することができないため、他のDBMSにあるように、親となるデータを削除した際に、子のデータも一緒に削除することはできません。
(私が開発していた時に、気づいていなかっただけなら申し訳ありません。)
ですので、RealmSwiftでデータ削除する際に、親も子も手動で削除していく必要があります。

反面、CoreDataの場合だと、

f:id:project-unknown:20180716180641p:plain:w430

こうする事で、親のデータを消したら、子のデータまで一気に消えます。


トランザクションが分かりにくい

これは完全に慣れの問題ですが、RealmSwiftの場合、コードブロック内でトランザクションが貼られます。

逆にいうとトランザクションから外れてコードを書くと思わぬところでデータ更新が走って詰まった事がありました。

CoreDataのコンテキスト内に更新ロジックを書いて、最後にsaveする書き方に慣れてしまったので、ここは凄く違和感を感じました。


マイグレーションに違和感がある

例えば以下の構成があったとします。

import UIKit
import RealmSwift

class FirstEntity: Object {
    @objc dynamic var id = NSUUID().uuidString
    @objc dynamic var updateDate? = Date()
}

これにデータを突っ込んで、
アップデートのタイミングで、以下のようにカラム名を修正します。

import UIKit
import RealmSwift

class FirstEntity: Object {
    @objc dynamic var id = NSUUID().uuidString
    @objc dynamic var createdDate? = Date()
}

この時、これまで入っていたDateが初期化されて、1970年が入ってました。

単純にマイグレーションする際の考慮漏れはありますが、CoreDataの場合だとここでクラッシュして気付けます。

RealmSwiftの場合だとデータを登録して結果を見て初めて気付くと言うところが個人的にはしんどいです。

実際に個人で開発しているときは、コアな箇所だとユニットテストを書いたり、ある程度のテストをおこないますが、それでも余暇の時間を使うのもあってテスト漏れは発生しやすいです。

この時に気付けるのか?というのがCoreDataを採用する1番の動機となりました。


締めくくり

結局のところで言うと、RealmSwiftに慣れていなくて、CoreDataは散々使ってきたので慣れていた。
が、CoreDataを採用した1番の理由です。

RealmSwiftは触ったから分かる素晴らしいものですが、私の学習が追いつかなかった。
RealmSwiftはサーバとのデータのやり取りも非常に楽に実装できるよう、サポートされていますし、いずれは使いこなせるにしていきたい限りです。

最後に

TIME HACKER

記事の途中で記載しましたが、今回の記事は、TIME HACKERを開発中の記事となります。

是非お使いください!

Firebase A/Bテストを試す - Firebase A/B Testing

Firebase A/Bテスト

Firebaseが提供しているA/Bテストを利用した記事となります。

この記事では、A/Bテストとは?から、 実際にTIME HACKERアプリで、インタースティシャルの表示頻度を確認する際に、A/Bテストを導入しているので、実際に実装した際の手順・コードをご紹介します。

最初に、A/Bテストの説明や、TIME HACKERでのA/Bテスト設計について論じています。
実装方法だけを確認したいのであれば、こちらから御覧ください。

A/Bテストとは?

A/Bテストとは、アプリやWebページ等で数パターンの機能・表示を用意して、ユーザー毎に出し分けを行う事で、
用意した数パターンの機能・表示の内、どのパターンが優れているのかを見極めるのに利用します。

A/Bテストは単純に機能だけの評価だけではなく、マーケティング上だとより高いCTR, CVRを得られるのか検証したり、比較的メジャーなテストです。

特にアプリではこのテストは非常に有用で、iOSアプリ等は、機能検証しようとしたら、毎回申請を挟まないと行けないなど、多大な時間を要するのですが、
A/Bテストの概念を採用する事で、一度の申請で複数の検証を行うことが出来ます。

A/Bテストはどうやるのか?

いろんなサービスがありますが、どこもやることは簡単です、
Webサーバにパターン毎のjsonファイルを設置し、アプリ側でそのjsonファイルを取得し、イベントログ等を送信する事で検証を行います。

f:id:project-unknown:20180716184438p:plain:w430
(イメージ図)

今回のお題としているのは、FirebaseでもA/Bテストをサポートしている為(まだβですが 2018/7/16段階)、それを採用した実例の紹介となります。

Firebase A/Bテストは、jsonの設置もそうですが、イベントトラッキングも全てサポートしてくれているので、一つのコンソール上でA/Bテストで必要な一連の操作ができるので非常に便利です。

何のA/Bテストを実施するか?

当たり前ですが、何のA/Bテストを行うのか?はちゃんと設計しないといけません。
TIME HACKERでは、冒頭でも記載しましたが、インタースティシャルの表示頻度の最適解を求める上で、A/Bテストを実施しています。

ここでは、TIME HACKERでのインタースティシャルA/B設計について記載します。

インタースティシャルの頻度を何故測るのか?

インタースティシャルは全画面に表示される広告なので、ユーザの行動を大きく阻害します。
頻度を間違えた場合は、最悪ユーザがアプリから離脱していってしまうでしょう。
TIME HACKERではアクションの計測を開始したタイミングでインタースティシャル広告を表示するように設計しています。
ですが、アクションを計測するのは、1番ユーザーが利用する機能です。
なのに、毎回アクションを計測する際に表示するのでは、あまりにもフラストレーションが溜まるでしょう。

この解決策として、インタースティシャルの表示頻度を調整します。
まず1度表示したら10分間は表示させません。(これはいたずらに広告をタップする事による、アドセンス狩り対策の意味でも行っています。)

しかし、10分だけで本当に良いのか?
答えはNOです。TIME HACKERでは、ユーザがアクションを起こす時に起動するアプリです。
言うなれば、アクションとアクションのつなぎ目に起動してもらうアプリです。
そのアクションが、例えば

  • スマホをいじるのであれば、次起動するのは1時間後
  • 映画を見るのであれば、次起動するのは2時間後

等、10分だけの制限では、ほぼ毎回記録測定のタイミングで広告が表示されてしまします。

ですので、10分制限は、あくまでアドセンス狩りとして捉え、ユーザーの行動を阻害しない策として、何回計測実行したのか?で表示するようにします。

TIME HACKERでのA/Bテストで見る数値

以下の数値を追います。

アプリの定着率

定着は4-7日で計測します。
TIME HACKERは毎日複数回利用していただく事を想定したアプリです。
生活に密着している指標として、1日で見るのではなくて、4-7日の定着数を見ます。
ここが、インタースティシャルの頻度によって変わるのであれば、見直す必要が出てきます。

広告のクリック数

ユーザーの事を考えないのであれば、そもそもインタースティシャル広告を出さない事が1番であることは明白です。
ですが、アプリの収益で生活するなどを考えるのであれば、収益を得るのはマストでしょう。

なので、広告のクリック数を見る事は、絶対必要です。

AdMobの推定収益

こちらは、広告のクリック数でも似たような指標を取ることができますが、

TIME HACKERでのインタースティシャル表示制限のA/Bテスト設計

TIME HACKERでは、以下の条件でテストを行います。

Firebase A/Bテストを用いたA/Bテストを実施する

A/Bテストの準備をコンソール上で行う。

Firebaseコンソールから、A/B Testingを選択します。

f:id:project-unknown:20180716185256p:plain

テストの名前や、概要、どの割合のユーザをA/Bテストの範囲に含めるのか?を設定します。

f:id:project-unknown:20180701142853p:plain:w430

今回は100%のユーザとしていますが、規模の大きなアプリ等では、全てをテスト対象にするのではなく、ごく1部のユーザをA/Bテストのターゲットとしても良いかと思います。

次に、バリアントを設定します。
要はA/Bテストターゲットユーザのうちに、どの割合でどんな値を設定するのか?を指定します。

TIME HACKERでは、インタースティシャル広告の割合を決めたいので、
25%区切りで、値を設定しています。

f:id:project-unknown:20180701145338p:plain:w430

最後に目標を入力します。
ウォッチしたい数値をここで指定しておくことで、Firebaseが該当する数値を集計してツール上で確認できるようになります。

f:id:project-unknown:20180701145347p:plain:w430

ここまで設定したら、次へ進みます。

次に進むと、今設定したA/Bテストが下書きの状態で表示されます。

f:id:project-unknown:20180701150301p:plain:w430

アプリの設定を行う。

次にアプリの設定を行います。

plistを用意する。

f:id:project-unknown:20180701151216p:plain:w430

アプリを実行して、tokenを確認しておく。
↓の箇所。

debugPrint("a/b test token :\(InstanceID.instanceID().token()!)")

Firebase コンソールの準備を続け、テスト開始する

テスト確認してみましょう。

f:id:project-unknown:20180701153510p:plain:w430

今確認したTokenを入力して、テスト確認したいバリアントを設定します。

f:id:project-unknown:20180701153615p:plain:w430

では、テストを開始しましょう。

f:id:project-unknown:20180701153703p:plain:w430

開始して間もないのでデータは無いですが、以下の画面のようにテスト実施の画面になっているはずです。

f:id:project-unknown:20180716192307p:plain:w430

アプリ側でA/Bテスト用のコードを埋め込む

以下、実際にTIME HACKERアプリで実際に作ったコードの一部です。

import Firebase

#if DEBUG
fileprivate let Interval = 0
#else
fileprivate let Interval = 60 * 60 * 24
#endif

fileprivate let RemoteConfigKeys  = "interstitialTimesOnTapNum"
fileprivate let RemoteConfigPlist = "RemoteConfig-default"

class InterstitialTestManager: NSObject {
    
    private let remoteConfig = RemoteConfig.remoteConfig()
    
    private static let instance = InterstitialTestManager()
    
    public class var shared: InterstitialTestManager {
        
        return instance
    }
    
    
    /// ABテストのセットアップ
    /// 初回AppDelegateなどで1度呼ぶだけで十分
    func setup() {
        
        remoteConfig.setDefaults(fromPlist: RemoteConfigPlist)

        if let settings = RemoteConfigSettings.init(developerModeEnabled: false) {
            remoteConfig.configSettings = settings
        }
        
        remoteConfig.fetch(withExpirationDuration: TimeInterval(Interval), completionHandler: { (status, error) -> Void in
            if status == RemoteConfigFetchStatus.success {
                self.remoteConfig.activateFetched()
            } else {
                print("fetch error.")
            }
        })
    }

    
    /// インタースティシャルの頻度を取得します
    ///
    /// - Returns:
    func interstitialFrequency() -> Int {
        var frequency = 10
        
        if let number = remoteConfig[RemoteConfigKeys].numberValue {
            frequency = number.intValue
        }
        
        return frequency
    }
    
    
    /// インタースティシャルを表示するかを判定します。
    ///
    /// - Returns:
    func isDisplayInterstitial() -> Bool {
        
        if actionRunningNumForCount() > interstitialFrequency() {
            return true
        }
        
        return false
    }
    
}

使い方は、まずAppDelegateでセットアップを呼び出します。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    InterstitialTestManager.shared.setup()
}

後はインタースティシャルの発動条件の条件分に以下のコードで判定を埋め込むだけです。

func displayInterstitial() {
    guard InterstitialTestManager.shared.isDisplayInterstitial() else {
        return
    }
    // インタースティシャル表示
}

これで、A/Bテストを埋め込む事が出来ました。

注意点

載せているSampleコードの以下の部分

if let settings = RemoteConfigSettings.init(developerModeEnabled: false) {

ここの、developerModeEnabledを開発時はtrueにすることで、RemoteConfigのキャッシュタイムを好きにいじることができるようになります。
が、リリース時は、falseにして、Debugモードを解除しておいたほうが無難でしょう。


締めくくり

全て自分でつくろうと思うと、かなり難しいというより、面倒な作業が必要となるため、
Firebase A/Bテストを採用する事で、簡単にA/Bテストを実行する事ができ、
またイベントトラッキングはGoogle Analyticsを利用しているので、より詳細な分析も行うことができそうです。

まだリリースして間もないので、Firebase コンソール上でテスト結果を集計出来ていないので、結果については、このタイミングではお話することができません。

ある程度集計できて、新たな気付きがあれば加筆修正を行おうと思います。

最後に

img

今回の記事で紹介していますが、TIME HACKERで、Firebase A/Bテストを採用しています。
是非お使いください!

written by ゆう@あんのうん

デザイン視点でTIME HACKER

img

はじめに

KeyHolderに次いでProject.Unknown2作目のアプリ、時間管理系ツールTIME HACKERがリリースされました!
デザイン担当のぽぽたです。

この記事ではデザイン視点で、TIME HACKER完成までのいろんなことを紹介させていただこうと思います。

TIME HACKERの紹介ページはこちらになります。
TIME HACKER - 時間管理 - Project Unknown

ロゴデザイン決定までの流れ

f:id:project-unknown:20180716170654p:plain

ロゴのデザインは、ゆう@あんのうんさんとの度重なる話し合いで決めていきました。

自分の時間を管理し、生活改善のサポートをするアプリということで、ゆう@あんのうんさんから時間や計測のイメージと聞き、いろんなサイズや色違いのアイコンを作ってみました。
テーマカラーになる青は割と早い段階にノリで決まった気がします。ゆるい。

f:id:project-unknown:20180716170706p:plain

そこから更に絞って...を何度か繰り返し。
ロゴはアプリの顔になるので、納得いく形になるまで話し合いました。

f:id:project-unknown:20180716170011p:plain

最終的に決まったTIME HACKERの公式ロゴ。
時計と円グラフを合わせたイメージになっています。

作成したアイコン

TIME HACKERで作成したアクションアイコンの一覧です。

f:id:project-unknown:20180716161615p:plain

ここに来て初めて数えてみたのですが、計68個になりました。
こう並べてみると数より少なく感じてしまいますね、自分の感覚的にはこの倍くらい作ったように思うのですが...。

本当は船、飛行機、バイク、ホワイトボードなども作る予定だったのですが、話し合いの末現段階では見送ることになりました。
こちらより機能・アイコン追加要望のページを設けていますので「ボツになったアイコンを使いたい!」や「こんなアイコンが欲しい!」などありましたらお気軽にご提案ください^^

TIME HACKER ご要望・お問い合わせ

アクションアイコンのデザインについて

ゆう@あんのうんさんから「こういうアイコンが欲しい」というリストをもらい、それを元に考えていきました。
ロゴもそうなんですが、アイコンを作るということ自体あまり経験がなかったので、結構苦戦してしまいました...。

シンプルで わかりやすく 使いやすいを意識しているアプリなので、アイコンも極力シンプルにしたかったんですよね。

f:id:project-unknown:20180716193234p:plain

初期に作ったアイコン3種。
〜不純物0%、クリアーな味わい〜という感じのシンプルさですね。

f:id:project-unknown:20180716193839p:plain

後期に作ったアイコン3種。
描けば描くほど前面に乗り出してくる『伝われ...!』という気持ちとこだわりが反映されていくアイコン達......。

電車は最初、シンプルに横向きの四角いハコにしたかったんです。
しかしサイズ的にとても短く路面電車のようになってしまうな...と思ったので、奥行きを意識したのは苦肉の策ではありました。
単純にシンプルといっても、伝わらなければ仕方ないですもんね...。

f:id:project-unknown:20180717002236p:plain

いろいろなアイコンの要望をもらい着手し、初めて気づいたのは動詞を表現するのは難しい!ということですね。
上のアイコンは何を表現しているかわかりますでしょうか?
アイコンを作る際の要望リストには、左からくつろぐ 遊ぶとありました。

くつろぐスタイルは人それぞれですよね。
スマホをいじりながらベッドでゴロゴロや...ソファーに座りコーヒーを飲みながら本を読む...。
遊ぶに関しても、なにでどう遊ぶのか...シンプルなアイコンでどう表現すれば伝わるだろうかと結構悩みました。
いろいろ考えてみて、最終的に決めたのがくつろぐ=ソファーと、遊ぶ=楽しそうな感じでした。

今後アイコンを追加する際は、もっと分かりやすく、もっとシンプルに表現できるよう表現力も養いたいところです。
アクションアイコンは全て好きに名前をつけることができるので、自分の感覚で好きに使っていただければ嬉しいです!

レポートアイコンのデザインについて

f:id:project-unknown:20180717010732p:plain

こちらは手描きで紙にいくつか案を出してみたもの。

グラフ的なものは、あまり考えずにロゴに寄せて円グラフ表現にしていたのですが、一番下の画像の、時間管理の画面は棒グラフ表現なのに円グラフアイコンだと混乱しない?という、どうまずさんからのごもっとも過ぎるツッコミにより、全員意見一致で急遽円グラフから棒グラフに変更になりました。
アプリ申請当日の出来事です(笑)

f:id:project-unknown:20180717012427p:plain

最終的に決まったレポートアイコン各種。

最後に

KeyHolderでもアイコンは少し作成したのですが、比でないほどたくさんのアイコンを作成することになったTIME HACKERでした。
そして初めての経験や思考錯誤の末の発見やと、とても実りのある開発でした。

まだまだデザイナーと胸を張れるほどの力量はないのですが...これからもProject.Unknownのデザイン担当として少しづつ精進していきたいです!

f:id:project-unknown:20180717051430p:plain

TIME HACKERは自分の時間を管理し、生活改善のサポートをする時間管理アプリです。
気になりましたら是非使ってみてください^^
timehacker.page.link

う〜のん語録

うなな:バナナ
うまいらる : おいしいもの、おやつ、食べること

GDPRを受けてアプリで対応したこと

GDPR(General Data Protection Regulation)とは

詳しいことは他サイト様に委ねますが、ざっくりというとEUでのデータ保護法則の新しい個人情報保護法が策定され、EU諸国では勝手に個人情報を取得、解析する事ができなくなりました。

ここ最近(2018年)、AdMobやAdSense、Twitter等でユーザ許諾が出まくったのは、これが原因です。

アプリ開発者としてはどんな影響を受けるのか?

冒頭でも記載しましたが、個人情報を勝手に取得してごにょごにょするのがNGになります。
ここで大問題となるのが、AdMob等の広告です。

広告は、現在ターゲティング広告が主で、ユーザが閲覧したWebなどの情報を元に、興味・関心を分析し、ユーザにあった広告を出しています。

なので、これまでのように、EU諸国で単純に広告を埋め込んだアプリを展開する事はNGとなります。

対応方法は?

勝手に取得するのがNGなので、ちゃんとユーザの許諾を取ればOKです。
が、あまり許諾を得られる可能性は低いでしょう。

アプリではどんな対応が必要なのか?

ここは、Project.Unknownとして取った方法です。
上述通り、広告を出すための許諾を取るのは難しいのと、かといって個人情報を取得してのユーザベネフィットを新たに構築するのも難しかったので、該当する国で広告・アプリの展開を止めました。

AdMobのメディケーション、並びにiTunes Connectから、以下の諸国への配信を停止させます。

  • アイスランド
  • アイルランド
  • イタリア
  • イギリス
  • エストニア
  • オーストリア
  • オランダ
  • キプロス
  • ギリシャ
  • スウェーデン
  • スペイン
  • スロバキア
  • スロベニア
  • チェコ
  • デンマーク
  • ドイツ
  • ノルウェー
  • ハンガリー
  • フィンランド
  • フランス
  • ブルガリア
  • ベルギー
  • ポーランド
  • ポルトガル
  • マルタ
  • ラトビア
  • リトアニア
  • リヒテンシュタイン
  • ルーマニア
  • ルクセンブルク


欧州GDPR全解明 (日経BPムック)

iTunesConnectで銀行情報の「Account Holder Type」でエラーが起きている時の対処法

はじめに

iTunes Connectを開いたら、

f:id:project-unknown:20180630231341p:plain

こんな画面になっており、Agreements, Tax, and Banking のリンクを開いた際に、以下のように、あまり見慣れないエラーが出て焦りました。

f:id:project-unknown:20180630231456p:plain

Our records indicate that your banking information is incomplete: Missing bank account holder type Please access your banking information and update as necessary.

銀行の設定である、「account holder type」が何やらおかしいみたいなので、対応します。

対応方法

Bank Infoの「Edit」をクリック。

f:id:project-unknown:20180630231611p:plain

Banking Informationの「View/Edit Existing Bank Account」をクリック。

f:id:project-unknown:20180630231708p:plain

「Edit Bank Account Info」をクリック。

f:id:project-unknown:20180630231808p:plain

確かに「Account Holder Type」が未選択の状態になっていました。

f:id:project-unknown:20180630231912p:plain

これを、「Individual」に変更します。 (個人口座という意味です)

この後、確認画面を押して「Save」すると以下のような画面になります。

f:id:project-unknown:20180630232457p:plain

この後、「Save」を押してもエラーになります。
おそらく、銀行情報を更新しているんだろうと思います。

これで、本件のエラーは対応できたと考えてます。
解決できていない場合は、追加調査・更新を行います。

今日のう〜のん3

f:id:project-unknown:20180531202718p:plain

ふかふかのチュンチュンに挟まれて温もり200%。
幸せなお昼寝のとろけるう〜のんでした。

デザイン担当のぽぽたです。
ちょっと聞いてください、使い始めたペイントツールのCLIP STUDIOがとてもすごいんです。
今回描いた絵の葉っぱも雲も筆でスッと撫でただけでこのクオリティなんです!
感動メーターが振り切れました、楽しすぎてやみつきになりそうです。

う〜のん語録

うまいらる : おいしいもの、おやつ、食べること


その他のデザイン・イラスト関連の記事

その他のう〜のんに関する記事は、以下のページから見れますよ!

www.project-unknown.jp

う〜のんがこれまで登場した作品

ぱたぱたう〜のん

f:id:project-unknown:20180510010406p:plain:w300

www.project-unknown.jp

Blender はじめの一歩 vol 1 - オブジェクトの選択・削除と球体をつくる

はじめに

今後のアプリ開発に向けて3D技術の勉強をぼちぼちと。
デザイン担当のぽぽたです。

無料の3DCGソフト、Blenderを一から始めてみようと思います。
文字通り一からですので、少しでもこの勉強が、これから始めてみようという方の
手引きになれたらなあという気持ちで。
そして追い抜いて、私を導いてください!

使用環境はMacで操作しています。

オブジェクトを画面上で移動させる

次の画像はBlender起動時の全体画面です。

f:id:project-unknown:20180523132156p:plain

わあもう難しい。
今すぐに思考を停止してBlenderを終了してPCをたたんで布団に潜り込みたいですね。

...でもそうも言ってられないので私の心が折れないようにゆっくり覚えていきます。

f:id:project-unknown:20180523132737p:plain

中心に鎮座しているのが3D空間の主、オブジェクトですね。
そして3Dカーソル、3D空間内をクリックした場所に現れます。

これらをああやったりこうやったりしていくのでしょう。
まだ何もわかっていません。これから少しずつ覚えていくんです!

f:id:project-unknown:20180523133249p:plain

オブジェクトの中心から生えている3本の矢印は各座標を示していると同時に、
矢印の先をクリックしつつ引っ張ると、その座標の軸に沿って動かす事ができます。

球体を作る

初めての3D...ここはやっぱりう〜のんを作ってみたいですよね!
とりあえずデフォルトの四角形は端っこに追いやって、球体を作ってみます。

f:id:project-unknown:20180523133858p:plain

画面左上に、縦に並んでいるタブを作成に切り替えます。
するとUV球というボタンがありますので押してみましょう。

う〜のんの基盤になりそうな球体ができました!

オブジェクトは3Dカーソルが示していた場所に現れるようです。
せっかくなので、中心に来てもらいましょう。

f:id:project-unknown:20180523134556p:plain

shift+Sからカーソル→原点を選ぶと、3Dカーソルが原点に移動します。

f:id:project-unknown:20180523135218p:plain

四角形に代わり、球体が中心にきました。

オブジェクトを削除する

端っこから覗いている四角形が邪魔なので消してしまいましょう。

f:id:project-unknown:20180523140608p:plain

削除したい四角形の上で右クリックすることによって四角形が選択されます。
選択されたら、キーボードの「x」を押すと削除の確認画面が出るので、削除を決定します。
これで...

f:id:project-unknown:20180523141120p:plain

画面上に球体だけになりました!
とりあえずここまでできた自分を褒めて次に繋げようね...よくやった!
こんな感じで、少しずつ進んでいきたいと思います。

最後の最後に、保存はこちら。

f:id:project-unknown:20180523143136p:plain

走る - GIFアニメ

f:id:project-unknown:20180521183654g:plain

はじめに

今回は人のアニメーションに挑戦してみました。
デザイン担当のぽぽたです。

描き始める前のイメージは抜群の疾走感だったのに、どことなく感じが怒りのノシノシ感に。
「こんなはずでは」というのは絵描きあるあるなので仕方ないです。...ですよね...?
でももったいないので性懲りもなく解説していきます。

解説

次の画像は全体の動作です。

f:id:project-unknown:20180521191843g:plain f:id:project-unknown:20180521183715p:plain

今回は8枚になりました、再生時間は全て0.1秒です。

走る動画を延々リピート再生していて気づいたのですが、
意外と頭の位置は激しく上下しないようですね。
手と足の位置関係なども勉強できました。

せっかくなので右から左へ走り抜けるGIFアニメも作っていたのですが、
上手く表現できませんでした...。
うーん修行してまた出直してきます!

リズミカル - う〜のんGIFアニメ

f:id:project-unknown:20180518123057g:plain

はじめに

最近は、1年前に買ってサボっていた温めていたCLIP STUDIOに引っ越し中の
デザイン担当のぽぽたです。

CLIP STUDIOのサポート機能はとてもすごいですね、本当に万能すぎて...私いらないのでは…。
なんだか悔しいので、練習のつもりでCLIP STUDIOでリズミカルな動きをするう〜のんを作ってみました。

動きの解説

今回作ったGIFアニメは、あまり位置が動かないので、
どう作ったのかが分かりやすいように遅めのGIFアニメも合わせて作りました。

f:id:project-unknown:20180519011124g:plain

それぞれの再生時間は次のようにしています。

  • 4〜5枚目と9〜10枚目
    • 0.2秒
  • 残りの画像
    • 0.1秒

なんだかGIFアニメを作っていると、0.1秒の差って意外と大きいんだなと気づかされました。
1秒の1/10なのに侮れない…。

う〜のんに限りませんが、いつか音楽と合わせてアニメのオープニングやエンディング
のように動画を作るのが夢のひとつです^^


その他のデザイン・イラスト関連の記事

その他のう〜のんに関する記事は、以下のページから見れますよ!

www.project-unknown.jp

う〜のんがこれまで登場した作品

ぱたぱたう〜のん

f:id:project-unknown:20180510010406p:plain:w300

www.project-unknown.jp