1年間休学したことの振り返り

ここ最近忙しく、更新をしていませんでしたが約1年間インターンをしていた、Popshootを2月18日にやめました。 4月からは、大学生活を送っています。

最近いろいろな人にプログラミングの勉強方法を聞かれることが多いです。 僕の経験は、エンジニアを始めたいと思う人のきっかけになるのではないかと思いこの1年を振り返ることにしました。

インターン先を見つける

17年の12月に、大阪でSkylandの木下さんと大山さん(Popshootの社長)が来られるイベントがあると東京でインターンをしていた友達から聞きました。 その友達は、半年前から東京に住んでいて、学校で会うたびに「考え方変わるし、絶対東京に来た方がいい」と言われていました。 そんな友達のオススメのイベントということもあり、興味半分でこのイベントに参加することにしました。 イベント後に大山さんが、春休みの2ヶ月間記事を書くライターとして、住み込みインターンを募集すると言いました。 春休みを関西で過ごすのか、東京でインターンをするのかを考えた時に、東京で過ごす方が濃密な時間を過ごせると感じ、大山さんにインターンをしたいとお願いをしました。

僕は何かを選択する時、それをした自分としない自分を比べ、選択肢が増える方を選択するようにしています。

春休みインターン

インターンには、2ヶ月前からインターンをしているエンジニアがいました。 初めて僕は、プログラミングができる友達と出会いました。 もともと、リッチマンプアウーマンが好きな僕はエンジニアに興味がありました。 近くにエンジニアがいるのは、プログラミングを勉強するいいきっかけなので、ライターの仕事が終わるとプログラミングの勉強をするようになりました。 コードを書きながらアプリが作られている過程に興奮をしたのを今でも覚えています。 勉強を始める時にまず、本屋で1冊本を買いました。そして、その本を毎日写す事から始めました。

休学をしてエンジニアの勉強を始める

春休みが終わる2週間ほど前に、友達から休学をすると「本格的にプログラミングを教える」と言ってもらいました。

この時も、インターンをする決断と同じ考え方をしました。休学を終えた1年間と関西で大学生活を送る自分を比較しました。そしてエンジニアという力をつけた方が選択肢が増えると思い休学を決意しました。

脱初心者

休学をして最初の2週間~1ヶ月は、「テックキャンプ」と「Udemy」のiOSの講座を何度も見て写経から始めました。 ・テックキャンプは、プログラミング未経験の人のための講座のため本当の初心者にはおすすめです。 ・Udemyは、比較的安くコンテンツも豊富で、初心者から中級者程度の人でも勉強になることが多くあります。

写経をした理由は、コードを1つずつ理解するのではなくパターンを覚えるのを優先したからです。 今でも、新しい言語に触れるときは、写経をし全体を捉えるようにしています。 エンジニアを始めるきっかけとして良かったのは、「わからないことを聴ける環境」と「プログラミングをする時間」があったことです。

自分でアプリを作成

リリースまでは至らなかったのですが、個人でアプリ作成を1ヶ月ほど行いました。 プログラミングの書き方は、言語が同じでも会社によって異なります。 インターン先の書き方に慣れるためにその書き方を真似しながらアプリを作ることにしました。 写経でインプットしてきたものをアウトプットすることで、これまでパターンで覚えたことを深く理解できるようになりました。

チーム開発に入る

ある程度、力がついて来たことを認められ開発チームに入れてもらうことになりました。 開発していたのは、「ぴたコイン」というアプリです。 ‎「ぴたコイン-ビットコイン予想、仮想通貨チャートfxゲーム」をApp Storeで

チーム開発は、個人開発とは違い「読みやすいコード」を意識して書くことが大事です。 これまでは、自分だけが理解できればよかったですがチーム開発ではチーム全員が書いたコードを理解できる必要があるからです。 また、すでにある膨大なコードを読むことにも苦労しました。 アプリを触りながら、この画面ではこんな風にコードが動いているんだなと地道に覚えていった記憶があります。 また、デザイナーの方とアプリの見た目や使いやすさについてもたくさん教えてもらいました。 この時期から新しいアプリが作られるたびにアプリを入れて、アプリのデザインや使いやすさの工夫を見るという日課ができました。

ある程度、コードを書けるようになるとどこかの会社でインターンをすることがおすすめです。 レビューといって、自分の書いたコードを見てもらいより良いコードを教えてもらったり、知らない知識を教えてもらうことができるからです。

サイバーエージェントインターン

iOSのプログラミングの勉強を初めて、半年がたち自分の力を試してみたいと思い、サイバーエージェントで1ヶ月のインターンをしました。 異なった環境での開発は、「もっともっと成長しないといけない」と改めて思う機会になりました。 また、メンターの方にも恵まれて毎日充実した時間を過ごす共に、成長を実感できる時間でした。 詳しくはこちらを参考にしてください。 https://blog.hatena.ne.jp/shanksryouop/shanksryouop.hatenablog.com/edit?entry=10257846132667628649

Popshootに戻って開発を続ける

サイバーエージェントインターンを終えてからは、再びPopshootでインターンを行いました。 戻ってからは、ピタコインのユーザー数が一気に何倍にも伸び、毎晩ユーザー数を見るのが楽しかったのを覚えています。 そして、新規機能の追加などのタスクも拾うようになり、とてもやりがいがある日々を過ごすことができました。

QuickeRをリリース

たまたまイベントで知り合った1つ上の先輩が、全く同じアイデアのアプリを開発していたので一緒にアプリを作ることになりました。 リリース後、知らない福岡県の方からTwitterで「こんなアプリが欲しかった!」というコメントをもらった時は、アプリ作りって良いなと思いました。 現在では、アメリカ、アイルランドなど世界中でこのアプリは使われています。

エンジニアのいいこと

周りの人が持っているたくさんの課題をプログラミングという技術で解決することができることです。そして、日本だけではなく世界中の人に使ってもらうことができます。

最後に

この1年間を振り返ると本当に多くの方に出会い、助けられて1日1日を過ごしてきました。 現在でも、多くの方にはお世話になりっぱなしですが、本当に感謝しています。

現在新しい会社で、アプリを作成しています。 もう少しでリリースができるので、楽しみにしていてください!

"プロダクトマネジメントのいろは" をVolareとTechTrain共催で開かせていただきました!

Peoplyticsの奥西さんを迎えて、VolareTech Trainプロダクトマネジメントについての勉強会を開かせていただきました。 そこで、勉強会の内容をシェアしていきたいと思います。

  • 将来、PMになりたいけどPMってどんな仕事?
  • エンジニアのキャリアってどんな種類があるの?

こんな人に読んでもらえたらと思います。

プロダクトマネジメントの印象は??

プロダクトマネジメント(PM)についてどんな印象を持っていますか?

僕は勉強会に参加するまで、

大まかなイメージしか持てていませんでした。

多くの人がこんな印象を持っているのでは無いでしょうか?

実際に勉強会で聞いてみると

  • タスクを管理する
  • サービスを成長させる
  • チームのマネジメント

と言った意見が出ました。

でも、PMってこれだけでは無いことを今回知ったのでそんなことを紹介していきます。

PMとは

PMの仕事のイメージが付きにくい理由はその役割の多さでした。

PMの最も重要な責任は、『ユーザーの満足度を高めながらプロダクトを作る』ことです。

プロダクトを作る責任を持つということから、役割の多さが伝わります。

これから、その役割をもう少し細分化して行きたいと思います。

今回お話を聞くまで知らなかったのですが、PMには大きく分けて2つの役割があります。

プロダクトマネジメント(PdM)プロジェクトマネジメント(PjM) です。

PdMの役割

  • ユーザーの課題解決
  • ビジネスモデルの構築
  • サービスの実現可能性の提案

PjMの役割

  • 品質や開発コスト
  • 開発のスケジュール管理

PdMは、プロダクトの WhatWhy に、PjMは、HowWhenにフォーカスします。

PdMは、プロダクトの決定権を多く持つため、ミニCEOと呼ばれているそうです。

PdMだからと言って、PjMをしないという訳ではなく

PdMへ比重は起きながらも双方の役割を行います。

PdMのフロー

プロダクトの規模によって、PdMの役割も大きく変化するそうです。

シード 〜 シリーズA * 事業起案 * マーケット調査, 競合の調査 * ユーザーのニーズの検証

シリーズB 〜 シリーズC * 利益率を検証した実現可能性 * 売り上げ、利益の最大化

これ、言葉ではわかりにくいですよね笑

シードからシリーズAは、こんなイメージでプロダクト開発を行います。

f:id:shanksryouop:20190912174510p:plain

イデアの起案→競合を調査 → 開発 → ユーザーのニーズを把握 → アイデアの制度をあげる

このようなサイクルで、プロダクト開発を行いきます。

このフローの中で起こるプロダクトに関する意思決定を、PMが担っています。

ある程度、プロダクトのニーズが見え、シェアが取れてくると

必要なマーケティング費用から利益を上げることが可能か検証していきます。

利益率が実現できると見込めば、プロダクトの拡大期に入ります。

マーケティングや新機能追加、などの意思決定を行います。

会社の成長を意思決定するのが、CEOなら

プロダクトの成長の意思決定をするのが、PMに当たります。

まさに、ミニCEOだなと思います。

CEOは、自分の会社が社会にどんな影響を及ぼすのかを考えますが、

PMは、プロダクトが社会にどんな影響を及ぼすかをイメージし、

そして、そのイメージを数値に落とし込み、実現できるかを検証することが大事だそうです。

サービスを生物と見立てる

どんな市場だとどんな機能が必要なのかを

生物に例えてお話をされていたのがわかりやすかったです。

実際に、PMになるとこういった意思決定をする機会が多いそうです。

マーケティングを生きる環境、ビジネスモデルを骨格、プロダクトを筋肉・見た目と考えます。

まず、マーケティングで自分たちが作りたいプロダクトの環境を明確にします。

ビジネスモデルは、一度作ると変えづらくサービスを作る中でグロースするかを決める重要な役割を持つので骨格にあたります。

プロダクトは、実際にユーザーに触れるため見た目の性質を持つのと同時に、

ビジネスモデルを動かすための機能的な要素を持つため、筋肉とも見ることができます。

今回の勉強会では、「木ノ実を食べる生物を考える」という例を考えました。

「鳥」「りす」「キリン」など様々な生物が出ました。

鳥は、いろんな環境に適しながらも飛ぶという機能が必要でリソースを食いそう。

キリンは、骨格が大きいからベンチャー企業では向かない。

りすは、一番骨格が小さいので簡単そう。

このような例えから、「規模」によって作る機能が異なって来ることが理解できました。

感想

冒頭でも書いたのですが、

これまで、PMになりたいと思っていたのですが、

実際に何をしているのかよくわかっていませんでした。

しかし、今回勉強会を通してイメージが湧いたのがすごくよかったと思います。

プロダクトに関する意思決定を多くできるのは、やりがいが感じれると思うし楽しそうだなと改めて思いました。

現在、Volareでサービス開発を行なっているので、

今回のPMの話を基に、サービス開発を進めて行けたらいいなと思っています。

また、奥西さんの話を聞くと自分の考えていることを言語化して、誰かに伝えることの必要性を感じました。

そのため、最近記事を書くことをやめていたのですが、

「わかったこと」「感じたこと」

を今回のように言語化してまとめて行きたいと思っています。

スライド

speakerdeck.com

まとめ記事

note.mu

note.mu

プロダクトマネジメントとは?(初めて勉強会に参加したお話) | 大学生エンジニアの日常

Herokuにデプロイする時に、Ginがデプロイできない

最近、サーバーサイドを実装してみたいという気持ちからHerokuにデプロイして個人でサーバーを書こうと思いました。

しかし、ginというライブラリがネックで ' git push heroku master ' エラーが出ていました。

' router/router.go:4:2: cannot find package "github.com/gin-gonic/gin" in any of '

上のエラーのような感じです!

そこで、メモがてらに上記のエラーがでた時の対処法について書いていきます。

修正方

今回のエラーの修正点は、' Procfile 'に以下のテキストが抜けていたからでした。

' echo web: gin_memcache > Procfile '

これを書くことで修正できました。

以下が参考記事です Scaling a Gin Application with Memcache | Heroku Dev Center

DeepLink導入に関して 詰まった所をまとめます

これまで、FirebaseのDeepLinkを使っていましたが新たに、AppsFlyerのDeepLinkを利用することになりました。 私は、今回が初めてのDeepLinkの実装であったためそこで詰まった点についてまとめておきたいと思います。 これから、FireabaseやAppsFlyerでDeepLinkを実装する方に少しでも理からになるようにまとめておきたいと思います。 この記事では、AppsFlyerを使って記事を作成していますが、Firebaseでも通じるところが多々あります。

SDKの導入

まずは、podを使ってSDKをインストールしていきます。

そして、AppDelegateに下記を記述します。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  AppsFlyerTracker.shared().appsFlyerDevKey = "<your-appsflyer-dev-key>";
       AppsFlyerTracker.shared().appleAppID= "123456789"

func applicationDidBecomeActive(application: UIApplication) { 
     AppsFlyerTracker.shared().trackAppLaunch() 
}

を実装していきます。これによりトラッキングが開始されます。

Appsflyerでは、この設定でイベントを取得することができます。

DeepLinkの実装

AppsFlyerのConfigure OneLink を選択します。

OneLinkは、FirebaseのDeepLinkに似たものでAppsFlyerが独自で用意したLinkです。

OneLinkの設定には、 App ID peefixiOS App Bundle ID が必要になります。

App ID prefixは、App Developer -> AppID -> Bundle IDと一致したアプリを選択すると確認することができます。 これで、OneLinkの設定は終了です。

しかし、ここからがドツボにハマる点でした。

AppsFlyerでは、3つの実装ができているかを確認するためのテストが用意されています。

  1. non organic install
    -> 流入してきたユーザーがorganicかそうではないかをhandleできるかをテストしています。
  2. In-app Events    -> Eventをしっかり取れているのかをテストしています。
  3. Deeplinking    -> DeepLinkが正しく押されているのかをテストしています。

1と2に関しては、非常に簡単に実装することができましたが3に10時間ほどハマりました。

なので、今回はそこを徹底的に紹介していきます。

Domain設定

iOSには、entitlementというものが存在しそこにdomainの設定を行う必要があります。 domainの設定は、 DeepLinkのpathを許容するために必要のようで設定するためには必須です。 そのDoamin追加の部分に、 applinks:mydomain.onelink.me を記述します。 まず、ここで5時間は使いました。 次に、このDomainを設定した後一度端末の電源をOFFにすることが必要のようです。

Universal Linksでアプリ起動時に利用され、自動的にiOSアプリのインストール時に自動的にダウンロードされるはずの「apple-app-site-association」ファイルが、iOS 11.2以上を使用している端末で、ダウンロードされないことがあるようです。このファイルなしでは、iOS端末はUniversal Linksはアプリを直接起動することができません。
ディープリンクのテスト時にこの事象が発生している場合、アプリをアンインストールし、ホワイトリストに登録済みのiOS端末を再起動した上で、アプリを再インストールしてください。それでも問題が解決しない場合、再度同じ手順を繰り返してください。
iOS11.2を使用している多数のiPhone端末で同様の問題が発生していることを確認していますが、どれだけこの問題が蔓延しているかは不確かです。iOSユーザーの3分の2に影響しかねないこの事象を、Appleがいち早く解決することを祈っています。

AppsFlyerでは、上記のように記述されていました。

この電源を切るか切らないかで、5時間はくらいました。

最後に、AppsDelegateで下記を設定すればテストも無事に終えることができました。

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
}

ということで、今後Deeplinkを実装される方は上のDomainの設定を忘れないようにしてください。

SignalとDriverの違い replayから読み解く

SignalとDriverについて

RxSwiftのSignalを調べてみると、 Signal は、replayを行わない。 Driver は、 replay を行う。 という解説を見かけました。 でも、replayってなんやろ?って思ったので、そこを踏まえて SignalDriver の違いについて解説をしていきたいと思います。

sharedReplayを使うメリット

1つのObservableに対して2つの購読対象がある場合2本のストリームが用意され、値が2度流れてくることになります。そこで、shareReplayを使います。 最後にshareReplayを書くと、1つの値しか流れ無くなります。 shareReplay(1)は、内部的にreplay(1).refCountを行なっています。 では、次の章でreplayを見ていきます。

ConnectableObservableとObservableの違い

Observableは、subscribeした時点でObserver毎に計算リソースが割り当てられています。 replayは、ConnectableObservableの変換を行っています。 ->map内に複数の処理が走ることになる

ConnectableObservableはsubscribeしているObserverが複数いたとしても、全てのObserver間で共有の計算リソースが割り当てられ、同時に値を通知する.

ただし、replayだけでは何も流れずconnectをする必要がある。 RxSwiftのConnectを使用すると、disposeをConnectされる毎にする必要がある。

そこで、refCountを使用する。 refCountは、subscribeされる度に内部でsubscribeしているのものをカウントしている。 そして、disposeする度に内部のカウントをデクリメントしている。 内部カウンターの値が0になると、Connectしたソースをdisposeする。

UIImageを丸くする。KingFisherを使って画像を丸くする

Kingfisherとは?

URLから画像を取得する方法として、KingfisherというLibraryがあります。

画像を丸くする

func roundImage() -> UIImage {
        let minLength: CGFloat = min(self.size.width, self.size.height)
        let rectangleSize: CGSize = CGSize(width: minLength, height: minLength)
        UIGraphicsBeginImageContextWithOptions(rectangleSize, false, 0.0)
        
        UIBezierPath(roundedRect: CGRect(origin: .zero, size: rectangleSize), cornerRadius: minLength).addClip()
        self.draw(in: CGRect(origin: CGPoint(x: (minLength - self.size.width) / 2, y: (minLength - self.size.height) / 2), size: self.size))
        
        let newImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return newImage
 }

kingfisherを使って画像を丸くする

func setCornerRadiusImage(url: URL?, placeholder: UIImage? = nil) {
        let processor = ResizingImageProcessor(referenceSize: self.frame.size) >> RoundCornerImageProcessor(cornerRadius: self.frame.size.width / 2)
        kf.setImage(with: url, placeholder: placeholder, options: [.processor(processor), .cacheSerializer(FormatIndicatedCacheSerializer.png)])
 }

jpgで、画像を保存するとキャッシュした後に白い背景が表示されるみたいです。 なので、pngに変えています。