Help us understand the problem. What is going on with this article?

Apple Pencilでのメモを気軽にするアプリ「Like a Paper」を個人開発したので大変だったことを共有します

Like a Paperという個人開発のiOSアプリをリリースしました🎉🎉🎉

アプリの概要→使った技術的要素の紹介→開発の中で大変だったこと→開発の中で感じたことの順で書いていきます。
だいぶ長い記事になったので、興味のあるトピックスだけ拾い読みしていただければと思います。

アプリの概要

まずアプリの概要です。

なぜつくったか

僕はiPad ProとApple Pencil持ってるんですが、
それはそれとして紙とボールペンでアイディアスケッチしてるときが結構あって、
せっかくApple Pencil持ってんならそういうときもiPadでできるよなあとふと思いました。

純正のメモアプリやEvernoteでも、当然Apple Pencilには対応しているんですが、
なんかパッと思いついたことをラフに書きたいときに使うにしては、ちょっと多機能すぎて気が散ります。

パッとアプリを立ち上げたら、真っ白な画面になって、そこにすぐ書き込めて、保存→なんか良さそうなネタが書けてたら共有機能でEvernoteに飛ばす、
みたいなアプリがあったらいいな、よく考えたら俺iOSエンジニアなんやから自分でつくりゃええやん、と思ってつくることにしました。

ジャンル的にはメモアプリになるんだと思うんですが、Apple Pencil持ってる人限定アプリなので、スケールはあまり考えていないです。
もし上記のコンセプト読んで共感してもらえたなら、お試しで使っていただけたら幸いです🙇‍♂️

当初の画面イメージは下記でした。

image.png

機能一覧

  • メモ機能(Apple Pencil or 指でのインプット)
    • デフォルトは黒ボールペンですが、指でタップするとパレットが出せるので、基本的なものは全部使えます
  • メモの端末内部への保存
  • メモの一覧
  • メモの共有/コピー/削除
  • 端末の回転にも対応(ただ縦でつくったノートは縦、横でつくったノートは横向きでしか編集できない仕様です)
  • 一応iPhoneでも使えるようにしていますが、指で描けるだけなんで、ユースケースはほぼないかと……
  • ログイン機能はなく、データは端末内に保存されるだけなので、他端末に引き継げません
  • アプリを削除すると中のデータも消えるので気をつけてください
  • また、ノートを保存せずにアプリを閉じた場合、編集内容は保存されないので気をつけてください

ユースケース

こんなユースケースを想定しています。

「あ〜なんか個人開発でもするか……どんな画面にしよかなあ〜」

Like a Paperを起動

真っ白な画面にApple Pencilで描く

image.png

「うん、こんな感じかな。よし保存しよう」

画面をタップ

image.png

Doneをタップ

image.png

保存されました。
シンプル&ミニマリズムですね。

App Store公開までのざっくりした流れ

  • 2月頃にアイディアを思いつく
  • 3月、4月にPencilKit使うと簡単に実装できそうだとアタリをつける
  • アタリはつけたものの、ウダウダしていてなかなか実装が進まなかった
  • GW使って一気に完成させて、申請までいった

使った技術的要素の紹介

一応Qiitaなので、使った技術的要素の紹介をして、技術情報としての体裁を整えます。

PencilKit

ノート部分はPencilKitを使っています。
当初Apple Pencilを使うので、その部分が大変かな〜と予想していたんですが、
全然そんなことはなく、PencilKitのおかげでめちゃくちゃ簡単でした。

PencilKitはライブラリとしてはマイナーな部類に入ると思うので、あまり日本語情報がない(というか書くこともそんなない……?)ので、
最初こそとっつきづらいと感じたんですが、WWDC2019の下記のプレゼンを見たらめちゃくちゃよくわかりました。

Introducing PencilKit
Drawing With PencilKit

しかもありがたいことに、ちゃんとしたサンプルコードが公開されています。
このサンプルコードがめちゃくちゃよくできているので、途中苦しんだときは、これ全コピペでもいいんじゃね? とまで思いました。

Swift3行でつくれるApple Pencil対応アプリ!?

PencilKitがよくできているので、View部分は3行で書けます

let canvas = PKCanvasView(frame: view.frame)
view.addSubview(canvas)
canvas.tool = PKInkingTool(.pen, color: .black, width: 30)

これだけです。

パレットについても、PKToolPickerを使うと簡単に純正メモアプリ同様のちゃんとしたやつを表示できます。

ただPKCanvasViewやPKToolPickerはお手軽なんですが、
データの実体はPKDrawingというStructになっていて、このモデルオブジェクトの扱いはそこそこ大変でした。

一覧表示はCollectionViewで、タップで開いて、ロングタップでメニューを出す

一覧表示画面はCollectionViewです。
サムネイルをタップしたらノートが開くようになってるんですが、長押しでコピーや削除したいなあと思って、
今まで使ったことなかったんですが、collectionView(_:contextMenuConfigurationForItemAt:point:)というメソッドを使いました。

実装は下記です。

    override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
        let index = indexPath.row
        let actionProvider: ([UIMenuElement]) -> UIMenu? = { _ in
            let share = UIAction(title: "Share",
                                 image: UIImage(systemName: "square.and.arrow.up"))
                                {[weak self] _ in self?.shareAction(index: index, point: point) }
            let copy = UIAction(title: "Copy",
                                image: UIImage(systemName: "doc.on.doc"))
                                {[weak self] _ in self?.copyAction(index: index) }
            let delete = UIAction(title: "Delete",
                                  image: UIImage(systemName: "trash"),
                                  attributes: .destructive)
                                {[weak self] _ in self?.deleteAction(index: index) }
            return UIMenu(title: "", image: nil, identifier: nil, children: [copy, delete, share])
        }
        return UIContextMenuConfiguration(identifier: nil,
                                          previewProvider: nil,
                                          actionProvider: actionProvider)
    }

本当はpreviewProviderにサムネイル画像を表示させることもできたんですが、
UIContextMenuContentPreviewProviderの扱いが上手くいかなかったんで、妥協しました。

iOS13から追加されるContext Menusについて

UIActivityViewControllerの座標

あと共有のアクションなんですけど、よく見る動きでしたけど、
これも自分で実装するのははじめてだったんで、最初ハマりました。

iPadだと動作が.popoverになるので、吹き出しをどこに出すか座標を指定してやらないといけなかったんですが、それを知らなくて何回もクラッシュしました。
個人的には別に.popoverじゃなくて、画面の真ん中に表示してくれれば良かったんですが、modalPresentationStyleを変えてもどうしても.popoverになったので、諦めてマジメに指定しました。

    // UIMenuタップ時のアクション
    private func shareAction(index: Int, point: CGPoint) {
        guard index <= drawings.endIndex else { return }
        let drawing = drawings[index]
        let shareImage = drawing.image(from: drawing.bounds, scale: 1.0)
        let activityViewController = UIActivityViewController(activityItems: [shareImage], applicationActivities: nil)
        activityViewController.popoverPresentationController?.sourceView = collectionView
        activityViewController.popoverPresentationController?.sourceRect = CGRect(origin: point, size: .zero)
        present(activityViewController, animated: true, completion: nil)
    }

ちなみに.popoverさせたい座標がボタンの時は、barButtonItemにボタンインスタンスを突っ込めばよしなにしてくれたので、こっちの指定の方が僕は好きですね。

activityViewController.popoverPresentationController?.barButtonItem = button // <-UIBarButtonItem

ダークモード対応

PencilKitはダークモードが存在する状態で開発されたライブラリなので、比較的ダークモード対応がしっかりしています。
開発者が何もしなくても、勝手にPKCanvasやPKDrawingは端末のダークモード/ライトモードによって色を反転させます。
ただ「開発者が何もしなくても切り替わる」というのは良し悪しで、場合によってはアプリ全体はダークモード対応できてないのに、
PencilKitの要素だけ反転してしまうケースもあるかと思うので、使う方は注意が必要です。

僕の開発したアプリはレイアウトが簡素なので、 ダークモードにしやすかったんですが、一個問題が出て、
アプリ起動中にダークモード/ライトモードが反転すると、更新をかけないとノートに書いていたものが見えなくなってしまう、という事象がありました。

というわけで、下記のようにcollectionViewのreloadを入れました。

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        if previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle {
            reload() // collectionViewのreload
        }
    }

開発の中で大変だったこと

技術的な話はだいたい書き切ったので、ここから苦労話を書いていきます。

開発よりしんどいStore申請作業

Store申請が大変だ、とは聞いていたんですが、マジでしんどかったです。
普通に開発より大変でした。

【2019年版】iOSアプリをApp Storeに公開するための全手順まとめ

基本的には上記の記事を見ながら、その通りにやっていけばオーケーでした。
(著者の方ありがとうございます。本当に助かりました)

2〜3時間はとられるかな〜というイメージで臨みましたが、
きちんと測っていませんが、普通に6時間ぐらいとられました。
一日がかりの作業ですね。。。
(特にはじめてなら)

プライバシーポリシー?!

申請で一番キツかったのが、プライバシーポリシー必要だったことですね。。。
僕のつくったアプリ、FirebaseのAnalyticsすら入れてないので、
ガチでサーバー通信一個もない仕様なんですが、それなのにプライバシーポリシー?! という感じでした。

まあ昨今のご時世的に仕方ないんでしょうけども……
結局下記のサイト使ってそれっぽい文章を自動生成して、Githubに貼りました。

App Privacy Policy Generator

Github Pageにしようとしてましたが、そもそも公開レポジトリだったことに気づいて、そのまま貼りました。
特に問題なかったので、Githubのドキュメント直貼りで問題ないみたいです。

スクリーンショット4バージョン?!?!

プライバシーポリシーの次にキツかったのがスクリーンショットでした。
iPad Proの第二世代でスクショ撮れという要件については、シミュレーターすらなかったので、
第三世代で撮ったスクショをそのまま貼りましたが、普通にスルーだったので良かったです。

審査は早かった

アプリの内容が大したことしてないというのもあったのか、審査はめちゃくちゃ早かったです。
Storeの審査の進捗はメール来て教えてくれるんですが、

2020/05/06 0:00 "Waiting for Review"
2020/05/06 4:43 "In Review"
2020/05/06 4:49 "Ready for Sale"

という時系列でした。
レビュー時間わずか6分。

Githubで公開すべきか

ここからはちょっと悩んでやらなかったことなんですが、ソースコードをGithubで公開するか悩みました。
転職のときのアッピールとか考えるんだったらGithub載せた方が絶対いいんですけど、
なんかまあでも普段からそんなアッピールアッピールで動きたくないな……みたいな気持ちもあり。

Git管理は別に一人で開発するんなら、ローカルで完結するので、まあ一旦それで十分か、と思って公開はしていません。

本名か匿名か

App Storeで出す上で、どうしても本名出ちゃうので、これもちょっと悩みポイントでした。
インターネットの活動は匿名中心でやっていたので、本名あんま出したくないな、と。

アプリだけStoreに出して、インターネット上では特に触れない、という形で、本名出さないままアプリだけ公開、という形もできたんですが、
どっちが楽しいかな〜と思ったときに、やっぱネットでつながってる人らにも「こんなもん作りました〜ドヤ〜〜〜」って言う方が絶対楽しいと思ったので、消極的に公開することは問題ないかなと判断しました。

開発の中で感じたこと

以下、感想や教訓を書いていきます。

アプリのアイコンは最初につくるとデバッグしながら愛着が湧く

今回アプリ開発するときに、全然機能できてない段階でとりあえずアイコンだけつくりました。
Keynoteを使って、純正アプリに限りなく近い(というかパクリで怒られても文句言えない)アイコンをつくりました。
あとサイズについては下記のサービスを使うと、全端末に対応した画像を生成してくれます。

MakeApppIcon

これがモチベーション的にバカにできなかったです。
普通の開発だとコード全部書いてから、さあ申請ってタイミングでつくると思うんですが、
アプリアイコンっていわばアプリの顔なので、早めにつくった方が、自分のアプリに愛着が湧き、
その愛着が挫けそうなときに助けてくれることもあると思うんですよね。

会社では誰かがやってくれていたことを全て一人でやる

今回簡単なアプリではありますが、ゼロイチでStore公開まで持っていくのははじめてでした。
その作業の中で、会社では当たり前にあった諸々がないことに気付きました。

iOSエンジニアとしての実務経験はぼちぼち一年になろうとしていて、
スキル的には一人前になってきたとは思ってるんですが、普段は気づかないところでチームの他の人に助けられていたのを実感しました。
CI/CD環境の構築やストアへの申請作業、APIの構築、パブリッククラウドとの連携などなど、
会社だとなんか当たり前にあるモノが、個人開発だと自分で1から環境構築になって、改めてやるとめちゃくちゃしんどいな〜と思いました。

仕様も自分、開発も自分

会社というものの恩恵も感じつつ、個人開発の方が良かった点は、仕様を自分で100%コントロールできるところでした。
会社の開発だとどうしても自分のエゴって抑えますけど、個人開発だと全部やりたいように決められます。

こだわるのも自分、妥協するのも自分。

純正アプリの完成度は改めてすごい

今回メモアプリつくったんですが、実装すればするほど、純正のメモアプリってよくできてるな〜と思いました。
僕はEvernote派なので、純正のメモアプリってほとんど使わないんですが、
今回の開発を通して、使ってなかったのがもったいないなと思いました。

Appleのフレームワークで開発するベストプラクティスが純正アプリに詰まっているので、
正直サードパーティアプリで純正アプリに勝つのは相当厳しいよなと思いました。

個人開発に必要な三要素

個人開発に必要なのは下記の三つかなと思いました。

  • 情熱
  • 知識
  • 時間

強いモチベーション、豊富な知識、ありあまる時間があれば、充実した個人開発ライフを送れそうですが、
実際はモチベーションはすぐ揺らぐし、知識は足らないし、時間もそんなないのが現実じゃないでしょうか。

今回は外出自粛の風潮の中のゴールデンウィークという、かなり特殊な条件でした。
4/30、5/1に会社から休みをもらったので、8連休で、その8日をほぼ丸々使いました。

最近リモートワークになって、家が仕事場になっていたので、その感覚もあって、作業はかなり捗りました。
いろんな好条件が重なって、Store公開まで達成できた感覚があります。

「このアプリ要らなくね?」とどう戦うか

開発してる途中で、「このアプリ要らなくね?」という気持ちになることが結構あります。

「こんなんショボすぎる」
「どうせ純正アプリに勝てないじゃん……」
「自己満足では?」
「どうせ誰も使わないんだから、公開する意味なくね? 大変なだけじゃん」

この悪魔の囁きに勝てずに、個人開発を中途半端に投げ出してしまう人は多いんじゃないでしょうか。

今回の僕の場合、自分を支えたのは「最低限俺は俺のアプリを使う」という気持ちでした。
つまり「自分が欲しいものをつくっている」→「それを他の人も使ってくれたら嬉しいね」というモチベです。
モチベーションとしてはどうなんでしょう?
もっと純粋に「世のため人のため」みたいな人もいるのかもしれないですが、僕のモチベーションはここでした。
「今自分が欲しいものがこの世にないからつくろう」でした。

技術の未熟さを痛感することもたくさんありますが、そういうときは
「今はこの程度だが、この次はもっと上手くできる」と思うようにしました。
別にこのアプリつくったらもう二度とアプリつくれないわけではないので、そういう意味では目の前にあるソフトウェアは常に叩き台であるわけですね。

迷走とどう戦うか

個人開発は自分で仕様を決められる分、迷走してしまうときがあります。
「ノートにタイトルあった方がいいかな」
「スクロールしたら無限にノートを大きくできたら嬉しいかな」
正直技術的にはなんでもできるので、多機能にしようと思えばいくらでもできます。

今回コードの実装に入る前に、ストアの紹介文を先に書いておきました。
自分のつくっているアプリの方向性がよく分からなくなったときは、よくそこに立ち返りました。
当初のアイディアが何を目的にしていたか、何をしたいアプリだったのか。
そこに立ち返ると、おのずと今考えている機能が本質的なのか、蛇足なのかが見えてくる感じがしました。

最初のアイディアは、単に頭の中にあるだけだと、開発する中でぼやけてしまう気がするので、
文字でも図でもいいと思うので、なんらか書き残しておくと、道に迷ったときの助けになるなと思いました。

st43
iOS Developer
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした