‘開発のハナシ’

iOSの写真アプリライクな写真ピッカーNohanaImagePickerをOSSとして公開しました

nip-logo

こんにちは。iOSエンジニアの原です。
果物の中では梨がいちばん好きです。

nohanaのiOSアプリの写真選択UIをOSS化して公開しました🎉

nohana/NohanaImagePicker: A multiple image picker for iOS app.

NohanaImagePickerとは

複数の写真を選択できるピッカーのOSSです。
nohanaの写真選択画面を作り直すことになったので、
せっかくだからということでOSSにしてみました。
nohanaのiOSアプリでは2016年の5月ごろから使っているので、多くのユーザに使われている実績があります。

特徴

オシャレトランジション

collection

写真一覧画面から写真詳細画面に遷移するときに、セルが拡大するようなオシャレトランジションがついてます。
遷移後の画面の操作によって、遷移前の画面の状態が変わっていることがあるので、トランジションをカスタムする時は 行き よりも 帰り を丁寧に作る必要があります。

transition

具体的には、写真詳細画面で写真をいっぱい切り替えて、写真一覧画面に戻ろうとすると、いま写真詳細画面で表示している写真が写真一覧画面には表示されていないという状態になります。なので、写真詳細画面から写真一覧画面に戻る直前に写真一覧画面をいいかんじにスクロールしておくという処理をしています。オシャレ〜。

モーメント

moment

iOS標準の写真アプリのモーメントのように、写真一覧を撮影した日付と場所でグルーピングして表示する機能を作りました。
グルーピング自体はiOSが自動的に実行してくれていて、グルーピングしたアルバムの一覧は

class func fetchAssetCollectionsWithType(_ type: PHAssetCollectionType, subtype subtype: PHAssetCollectionSubtype, options options: PHFetchOptions?) -> PHFetchResult

のtypeにPHAssetCollectionType.Momentを指定すると、取得できます。

ロゴ

nohana_imagepicker_logo

OSSにロゴがあるとやる気が出るので、デザイナさんにお願いしたら、かわいいロゴマークを作ってくれました!
以下、デザイナさんコメントです。

  • たくさんある写真の中からpick(選択)している様子をアイコンにしてみました。
  • 細かい違いですが、色味の調整にもこだわりました!

Swift 3.0

バージョン0.7.1でSwift3.0に対応しました🎉

使い方

基本的には以下だけで、ピッカーの表示と、選択した写真の情報を取得できます。

import NohanaImagePicker
class ViewController: UIViewController, NohanaImagePickerControllerDelegate {

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(true)
        let picker = NohanaImagePickerController()
        picker.delegate = self
        present(picker, animated: true, completion: nil)
    }

    func nohanaImagePickerDidCancel(_ picker: NohanaImagePickerController) {
        print("🐷Canceled🙅")
        picker.dismiss(animated: true, completion: nil)
    }

    func nohanaImagePicker(_ picker: NohanaImagePickerController, didFinishPickingPhotoKitAssets pickedAssts :[PHAsset]) {
        print("🐷Completed🙆\n\tpickedAssets = \(pickedAssts)")
        picker.dismiss(animated: true, completion: nil)
    }

}

READMEとDemoアプリのコードで解説しているので、詳しくはそちらを見てください。

工夫ポイント

ItemList

アルバム一覧データを格納するクラス(PhotoKitAlbumList) と 写真一覧データを格納するクラス(PhotoKitAssetList) のどちらもが 一覧データを格納するクラス という特徴を持っているので、その特徴をItemList (バージョン0.7以前はItemListType)というプロトコルで表現しました。
ImageListは以下のように定義しています。

public protocol ItemList: Collection {
    associatedtype Item
    var title:String { get }
    func update(_ handler:(() -> Void)?)
    subscript (index: Int) -> Item { get }
}

ImageListがCollectionに準拠しているので、PhotoKitAlbumListとPhotoKitAssetListは少ない実装でCollectionの強力な機能を利用することができています。
またsubscriptも実装しているので、添字で要素にアクセスでき、コードがシンプルになりました。

PhotoKitのラッパーのようなコードですが、写真選択UIのOSSなのにPhotoKitのラッパーを含んでいることは、若干やり過ぎだと思っているので、今後PhotoKitAlbumListとPhotoKitAssetListは別ライブラリにするかもしれません。

画面の状態管理

各画面には、ロード中状態ロード完了状態空状態(表示するデータがない状態)表示するデータがある状態の4つの状態があるので、それらをprotocolで表現しています。

public protocol EmptyIndicatable {
    func isEmpty() -> Bool
    func updateVisibilityOfEmptyIndicator(_ emptyIndicator: UIView)
}

public protocol ActivityIndicatable {
    func isProgressing() -> Bool
    func updateVisibilityOfActivityIndicator(_ activityIndicator: UIView)
}

protocol extensionでデフォルトの挙動を実装してあり、インジケータの表示/非表示の切り替え処理をViewController側で書く必要がありません。

public extension ActivityIndicatable where Self: UIViewController {
    
    func updateVisibilityOfActivityIndicator(_ activityIndicator: UIView) {
        if isProgressing() {
            if !view.subviews.contains(activityIndicator) {
                view.addSubview(activityIndicator)
            }
        } else {
            activityIndicator.removeFromSuperview()
        }
    }
}

ロード中状態空状態などが同時に起こりうる作りですが、1画面内だけで完結する状態管理なので今回はこれで十分かなと思います。

開発時に気をつけたこと

用語の統一

以下のように用語を統一しています。

用語 意味 備考
pick 写真を選択すること。 selectはUITableViewControllerやUICollectionViewControllerで使われているので、他のものを考えました。
drop 写真を非選択にすること。 同上。
asset 1枚の写真(または動画)のこと。 PHAssetに対応しています。
asset list 写真の集合のこと。アルバムとも呼びこともできる。 PHCollectionに対応しています。
album list asset listの集合のこと。アルバム一覧とも呼ぶことができる。 PHCollectionListに対応しています。

nohanaに特化しすぎないこと

nohanaのために書いているコードなので、nohanaに特化して書きたくなってしまうことが多々ありましたが、ぐっとこらえて汎用的に書けるように気をつけました。
おかげで、nohana側のコードで挙動をカスタムする処理が多くなってしまいましたが、逆に言えば挙動をカスタムし易いOSSになったと思います。
※nohanaは画面回転をサポートしていませんが、NohanImagePickerには画面回転にも対応しています(これが意外と大変だった)

まとめ

今後もいろいろと機能追加やリファクタをすすめていきますので、ぜひ使ってみてください。PRやissueも待ってます!

nohana/NohanaImagePicker: A multiple image picker for iOS app.

ノハナのインターンシップに参加しました

はじめまして。インターンシップ生の松本です。

僕は石川県の大学院に通っているのですが、単身で東京に乗り込み08/01〜09/30の二ヶ月間ノハナでインターンシップをしておりました。インターンシップを通してAndroidアプリの知識と技術を身につけたのでそのことについて話したいと思います。

きっかけ

インターンシップのきっかけは逆求人イベントに参加したことです。
そこでミクシィ社からインターンシップの招待を頂き、ミクシィグループのノハナ社でインターンシップをすることになりました。

インターンシップに参加するまで

実はインターンシップに参加するまで、Javaを使ったAndroidアプリ開発をした経験はほとんどありませんでした。
Javaの知識もほとんどなく、オブジェクト指向についても名前だけは知っている程度でした。

インターンシップに参加して

ノハナのインターンシップに参加して身につけたものは数多くあります。
基礎的なJavaの知識はもちろん、オブジェクト指向におけるデザインパターン、AndroidのライフサイクルからDagger、Subthread、Push Notification、DataBindingに至るまで二ヶ月間一人で勉強しても身につけられないなと思うぐらいの知識と技術を身につけることができました。

またこの過程で実装したコードは実際にアプリ内部で使われており、既にリリースされております。

どうしてこれだけの技術を学べたか

これだけの知識と技術を身につけることができた理由は、ノハナのエンジニアの皆さんの技術力と指導力の高さだと思います。

指導社員の先輩のスタンスは「自分で考えることを軸に知識をつけていく」でした。
自ら付けた知識を先輩に説明して指摘をもらい、間違えていれば再度考え直す。
苦しみながらもこのフローを通す度に新しい知識・技術を身につけることができました。

どうしてもわからないところは教えてくれるのですが、その際もブログ等の解説記事を見るのではなくAndroidそのもののソースコードやライブラリのソースコードからどういった動作をしているかを解説していただきました。
普段自分が考えもしない深い動作まで解説していただきエンジニアの皆さんの技術力の高さを思い知らされました。
このおかげで上辺だけを知るのではなく、どういった動きをするのか、またどういったところで応用が利くのかを理解することができました。

ノハナで体験したこと

ノハナではエンジニア・デザイナー・総合職等関係なく交流する目的で行っているシャッフルランチ、ユーザー目線を常に忘れない目的で行っているドッグフーディングランチなどの定期的なイベントがあります。
それらに参加することで普段聞くことのできない話や、サービスを提供する中の人がどういう風に自分たちのサービスと接しているのかを体験することができました。

まとめ

二ヶ月間のインターンシップの中でAndroidを始め、Java、オブジェクト指向等多くの知識と技術を学びました。

これで僕のノハナでのインターンシップは終わりです。
インターンシップを通してノハナには高い技術力を持っているエンジニアが多く在籍していることを知りました。そんな人たちがいるノハナは、これからよりよいユーザー体験を提供するサービスとなっていくと実感しました。
そして僕自身も今回で学んだことを糧に、ノハナのエンジニアの皆さんのようなユーザーに貢献できるエンジニアを目指していきます!

P.S. フォトブック届いた!可愛いい!
1475197167158-1
夜景も綺麗です。
1475197165817

RubyKaigi2016に参加してきました

こんにちは、新卒エンジニアの轟です。
9/8-10 に京都で開催された RubyKaigi2016 に参加させていただきました。
RubyKaigi は、 Ruby というプログラミング言語に関する世界的なカンファレンスで、今年は970人もの方が参加されました。
RubyKaigi

ノハナとRubyの関係

ノハナでは、決済画面や決済に関するツールの開発やデータ抽出のスクリプトに Ruby を利用しています。決済システムの開発速度を重要視する中で、外部ツールとの連携環境が十分に整っていた Ruby を採用しています。
最新の Ruby の動向をつかんで、チームの開発力を向上させプロダクトの開発に活かすことを目的として参加しました。

 

セッション内容

RubyKaigiに参加して、これからのRubyについて発表が聞けたので、気になったものをいくつか紹介したいと思います。

Ruby3 Typing @Yukihiro “Matz” Matsumoto

Ruby3 における「型」の話でした。
Ruby は動的型付け言語のひとつです。動的型付け言語のメリットとして、試行錯誤が容易なこと・初学者が学びやすいことなどが挙げられます。一方。デメリットとしては、プログラムを実行するまでエラーが起きるかわからないことが挙げられます。
Ruby の型が今後どのような方向に進むのかという内容でした。具体的には、型推論に似た方法を利用することで、型を明示的に書かずとも、型のチェックを可能としたい。そうすることで、動的型付け言語のデメリットを少しでも緩和できるのではないかという発表でした。
また、2020年の東京オリンピックまでに Ruby3 をリリースしたいという思いがあるそうで、そんなビッグイベントが早く来ないか待ち遠しく思います。

 

Unifying Fixnum and Bignum into Integer @Tanaka Akira

Ruby2.4 における class の変更に関する話でした。
Ruby2.3 までのバージョンでは、数値クラスとして Fixnum と Bignum という2つのクラスがあります(Integerはその抽象クラス)が、それが Integerクラス に統合されるそうです。
この変更によって、 Ruby に対する学習のコストが減るため、プログラミングをはじめたばかりの人でもスムーズに入門できる言語になりそうです。
ノハナには該当する部分がありませんでしたが、数値クラスによって処理を分けているなど、プロダクトは注意が必要ですね。

 

SciRuby Machine Learning Current Status and Future @Kenta Murata

大規模なデータにもとづいて、ビジネスにおける意思決定をしたいという需要が高まってきています。その手段のひとつとして「機械学習」という方法があるのですが、今の Ruby が置かれている状況についての話でした。
現状、機械学習のコードは Python で書くということが主流になっていて、Ruby で機械学習を行う環境はまだ整っていません。Pythonと同じように Ruby で機械学習を実現するため、 SciRuby というライブラリの開発話や、今後の課題についての説明がありました。まだまだ開発途上で、まずは Python の環境に追いつくところを目標としているそうです。

 

参加してみて

このような世界的で大規模なカンファレンスに参加することが初めてだったので、とても刺激的で楽しかったです。
セッションの内容も、Rubyそのものだけでなく、内部的な実装についてなど深い話がいくつも聞けて、さらに勉強したいというモチベーションが上がりました。

個人的に「機械学習」に取り組んでいきたいと思っています。業務でも機械学習を利用した仕組みを取り入れていきたいと考えているので、Ruby で本格的な機械学習ができるようしたいという発表が聞けて期待が膨らんでいます。機械学習分野において Ruby が Python を超えられるか楽しみに思っています。

 

その他

お弁当

お弁当スポンサーのみなさまに、3日間豪華なお弁当を準備していただきました。特に、2日目のお弁当の仕掛けは、エンジニアとして非常に面白かったです。
%e5%9b%b31

夜の京都散策

2日目の夜、時間が少し空いたので伏見稲荷や八坂神社に行ってきました。
夜という時間帯だったこともあり、見学できる範囲が限られてしまったのが残念でしたが、綺麗にライトアップされていて、日中とは違う雰囲気を味わうことができました。%e5%9b%b32

 

最後に

このようなカンファレンスを開催していただいたスタッフ、ならびにスポンサーのみなさま、ありがとうございました。
このRubyKaigiへの参加に関する費用はすべて会社に負担していただきました。ノハナのみなさま、ありがとうございました。
来年の RubyKaigi も楽しみです!

1 2 3