EDIT MODE

モバイルアプリ開発に関することを書いています

AndroidXでのバックキー制御

AndroidX以前

今までバックキーの制御と言えば、例えば以下のように Activity#onBackPressed() でゴニョゴニョしていました。

    override fun onBackPressed() {
        // バックキーイベントでFragmentで何かしたいとき
        val fragment = supportFragmentManager.findFragmentByTag("HogeFragment")
        fragment?.something()

        // スタックに積んだFragmentを1個前に戻したいとき
        if (supportFragmentManager.backStackEntryCount > 0) {
            supportFragmentManager.popBackStack()
        } else {
            super.onBackPressed()
        }

BackStackとかはまだいいですけど、Fragment側の処理をcallしたいときなどはActivityに特定のFragmentの依存が入ってしまって、残念な気持ちになっていました。

AndroidX以降

AndroidXで提供されているActivityには、Version 1.0.0-alpha01で Activity#addOnBackPressedCallback () というメソッドが追加され、 OnBackPressedCallback インターフェース経由でバックキーのイベントを受けられるようになってました。

つまりFragment内にバックキー制御のロジックを閉じ込めることが出来るということです。控えめに言って最高です。

developer.android.com

こんな感じで書くことが出来ました。

...
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
...

class MyFragment : Fragment() {

    private val callback = object : OnBackPressedCallback {
        override fun handleOnBackPressed(): Boolean {
            return if (isHoge) {
                something()
                true
            } else {
                false
            }
        }
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        activity?.addOnBackPressedCallback(callback)
        ...
    }

    override fun onDestroy() {
        super.onDestroy()
        activity?.removeOnBackPressedCallback(callback)
    }

これだけでも嬉しいですが、リファレンスを見るとArchitecture ComponentのLifecycleにも対応していました。

developer.android.com

つまり先程のサンプルコードは、以下のように書けます。

...
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
...

class MyFragment : Fragment() {

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        activity?.addOnBackPressedCallback(this, object : OnBackPressedCallback {
            override fun handleOnBackPressed(): Boolean {
                return if (isHoge) {
                    something()
                    true
                } else {
                    false
                }
            }
        })
        ...
    }

これでaddしたcallbackの開放を気にする必要が無くなりました。コード量も減るし、読みやすいです。 (OnBackPressedCallbackはJavaで実装されているのでSAM変換も可能ですし、実際にはさらにコード量は減らせます。)

これを発見したときにAndroidXに移行して良かったなと、心の底から思いました。AndroidX最高です。

MockKを使ったViewModelのテスト

概要

以下のような、シンプルなViewModelのテストをMockitoを使って書いたのですが、これをMockKで書き換えることにしました。

Mockitoを使ったViewModelのテスト

@RunWith(RobolectrictTestRunner::class)
class UserViewModelTest {

    @Mock private lateinit var mockRepository: MyRepository
    @Mock private lateinit var observer: Observer<List<UserPoint>>
    
    private lateinit var viewModel
    
    @Before
    fun init() {
        val immediate = object : Scheduler() {
            override fun createWorker(): Scheduler.Worker {
                return ExecutorScheduler.ExecutorWorker(Executor { it.run() })
            }
        }
        RxJavaPlugins.setInitIoSchedulerHandler { _ -> immediate }
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> immediate }

        MockitoAnnotations.initMocks(this)
    }
    
    @Test
    fun someTest() {
        val userList = listOf()
        `when`(mockRepository.findUserList()).thenReturn(Observable.just(userList))

        viewModel = UserViewModel(repository)
        viewModel.users.observeForever(observer)
        viewModel.loadUserList()

        verify(mockRepository, only()).findUserList()
        verify(observer).onChanged(userList)
    }
}

MockKとは

MockKはKotlinのためのMocking Libraryです。 ドキュメントも充実しているし、KotlintだとMockitoで困ることがほぼ解決出来ます。

便利なポイントはドキュメントにも列挙されています。個人的にはObjectのMockがシュッと書けて好きです。

mockk.io

MockKでViewModelのテスト

前述のコードをMockKで書き直すと以下のようになりました。

MockKを使ったViewModelのテスト

@RunWith(RobolectricTestRunner::class)
class UserViewModelTest {

    @MockK
    private lateinit var mockRepository: MyRepository
    @MockK
    private lateinit var observer: Observer<List<User>>

    private lateinit var viewModel

    @Before
    fun setup() {
        val immediate = object : Scheduler() {
            override fun createWorker(): Scheduler.Worker {
                return ExecutorScheduler.ExecutorWorker(Executor { it.run() })
            }
        }
        RxJavaPlugins.setInitIoSchedulerHandler { _ -> immediate }
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> immediate }

        MockKAnnotations.init(this)
    }

    @Test
    fun someTest() {
        val userList = listOf()
        every { mockRepository.findUserList() } returns Observable.just(userList)

        viewModel = UserViewModel(mockRepository)
        viewModel.users.observeForever(observer)
        viewModel.loadUserList()

        verify { mockRepository.findUserList(authToken) }
        assert(viewModel.users.value == userList)
    }
}

ところが、これを実行するとMockKのイニシャライズが失敗し、以下のようなエラーが発生してしまいました。

java.lang.NoClassDefFoundError: io/mockk/proxy/jvm/dispatcher/JvmMockKWeakMap

    at io.mockk.proxy.jvm.JvmMockKAgentFactory$init$Initializer.handlerMap(JvmMockKAgentFactory.kt:98)
    at io.mockk.proxy.jvm.JvmMockKAgentFactory$init$Initializer.init(JvmMockKAgentFactory.kt:43)
    at io.mockk.proxy.jvm.JvmMockKAgentFactory.init(JvmMockKAgentFactory.kt:102)
    at io.mockk.impl.JvmMockKGateway.<init>(JvmMockKGateway.kt:45)
    at io.mockk.impl.JvmMockKGateway.<clinit>(JvmMockKGateway.kt:163)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
        ...
Caused by: java.lang.ClassNotFoundException: io.mockk.proxy.jvm.dispatcher.JvmMockKWeakMap
    at org.robolectric.internal.bytecode.InstrumentingClassLoader.getByteCode(InstrumentingClassLoader.java:168)
    at org.robolectric.internal.bytecode.InstrumentingClassLoader.findClass(InstrumentingClassLoader.java:123)
    at org.robolectric.internal.bytecode.InstrumentingClassLoader.loadClass(InstrumentingClassLoader.java:95)
    ... 34 more

何が原因か分からなかったのですが、そもそもRobolectrictTestRunnerを使っている理由は、テストにLiveDataが含まれておりvalueの更新処理をMockしてもらうためです。

調べてみると、下記記事のようにLiveDataのユニットテストは core-testing ライブラリを使うのが正攻法のようなので、Robolectrictと置き換えてみました。

medium.com

MockK&core-testingを使ったViewModelのテスト

class UserViewModelTest {

    @Rule
    @JvmField
    var rule = InstantTaskExecutorRule()

    @MockK
    private lateinit var mockRepository: MyRepository
    @MockK
    private lateinit var observer: Observer<List<User>>

    private lateinit var viewModel

    @Before
    fun setup() {
        val immediate = object : Scheduler() {
            override fun createWorker(): Scheduler.Worker {
                return ExecutorScheduler.ExecutorWorker(Executor { it.run() })
            }
        }
        RxJavaPlugins.setInitIoSchedulerHandler { _ -> immediate }
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> immediate }

        MockKAnnotations.init(this)
    }

    @Test
    fun someTest() {
        val userList = listOf()
        every { mockRepository.findUserList() } returns Observable.just(userList)

        viewModel = UserViewModel(mockRepository)
        viewModel.users.observeForever(observer)
        viewModel.loadUserList()

        verify { mockRepository.findUserList(authToken) }
        assert(viewModel.users.value == userList)
    }
}

変更したのは、 RobolectrictTestRunner を外しRuleに InstantTaskExecutorRule を設定したことです。 これでやっとうまくテストをパス出来ました。

まとめ

  • LiveDataのユニットテストは core-testing を使う
  • 今の所MockK(v1.8.9)とRobolectrict(v3.0.0)の組み合わせはうまくいかない

Android Architecture Components 勉強会#1 に参加してきました

勉強会の参加レポート

GDG東京が主催のAndroid Architecture Components 勉強会の第1回目に参加してきました。 素晴らしい勉強会だったので記事にしたかったのと、学んだことの復習のために書いてます。

ちなみにこの勉強会は全4回の予定だそうで、Lifecycles(今回) -> LiveData -> ViewModel -> Roomと続いていくそうです。

gdg-tokyo.connpass.com

第1回目の講師は「あんざい」さんで、この方は本当に発表が上手いのですが今回もとても聞きやすくて、最後まで1ミリも飽きませんでした。

前半はLifecyclesの説明で、後半はあんざいさんが用意してくださった課題をみんなで取り組みました。課題にはいくつかの問いが用意されていて、動作確認後に参加者が答える形式だったので、楽しく課題に取り組めました。

課題も1個が丁度いいボリュームで、1時間くらいしかなかったにも関わらず、全部で5個くらいやったと思います。

以下のスライドに課題も載ってますが、現地でみんなとやるのが楽しかったので、もしお時間と場所的に可能であれば第2回目に参加することをおすすめします。

Lifecyclesの復習

1. 基本

必要な設定

プロジェクトのbuild.gradleにgoogleリポジトリ(Android Gradle Plugin 3.0ならデフォルトで記述有り)を追加して、モジュールのbuild.gradleにsupport-v4もしくはappcompat-v7を入れる。理由は、v26.1.0からsupport-v4のFragmentActivityやFragmentが、Lifecycleを組み込んでいるため。

メインクラス

  • Lifecycle
    • ライフサイクル状態の取得やObserverの追加・削除を行う

Enum

  • Lifecycle.State
    • ライフサイクル状態を表すenum
  • Lifecycle.Event
    • ライフサイクルのイベントを表すenum

Interface

  • LifecycleOwner
    • Lifecycleを持つクラスを表すためのsingle method interface
  • LifecycleObserver
    • Lifecycleに追加・削除できるObserverを表すinterface
    • @OnLifecycleEvent アノテーションをつけて自分でイベントメソッドを作る
    • Lifecycle.Event.ON_ANYは全てのイベントで呼ばれる
    • 第2引数でEventを受け取れる
  • GenericLifecycleObserver
    • あんざいさんが資料作成中にドキュメントから姿を消した幻のinterface
    • 便利なんだけど今後は使用しないほうがいいかも

その他

  • LifecycleRegistry
    • 複数のObserverを扱えるLifecycleの実装

2. 拡張機能

必要な設定

モジュールのbuild.gradleに以下を追記するだけ

dependencies {
    implementation 'android.arch.lifecycle:extensions:1.0.0'
}

便利クラス

  • LifecycleService
    • LifecycleOwnerが実装されたService
  • ServiceLifecycleDispatcher
    • ServiceにLifecycle機能をもたせるためのヘルパークラス
  • ProcessLifecycleOwner
    • アプリのプロセス全体に対するLifecycleを提供
    • こいつでアプリのForeground/Background判定が簡単に出来る
    • サポートライブラリでないFragmentでも検知可能

3. 注意点

onSaveInstanceState() まわりは要注意。 SupportLibraryのFragmentでは、onSaveInstanceState()が呼ばれると状態は CREATED になるようだ。

詳細は以下を読むほうがいい。

https://developer.android.com/topic/libraries/architecture/lifecycle.html#onStop-and-savedState

4. 基本的なサンプルコード

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private val TAG_NAME = MainActivity::class.java.simpleName

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // create observer
        val observer = object: LifecycleObserver {
            @OnLifecycleEvent(Lifecycle.Event.ON_START)
            fun calledWhenOnStart(source: LifecycleOwner) {
                Log.i(TAG_NAME, "ON_START: " + source.lifecycle.currentState.name)
            }

            @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
            fun calledWhenOnAny(source: LifecycleOwner, event: Lifecycle.Event) {
                Log.i(TAG_NAME, "ON_ANY: " + source.lifecycle.currentState.name + " arg2: " + event.name)
            }

            @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
            fun calledWhenOnDestroy(source: LifecycleOwner) {
                Log.i(TAG_NAME, "ON_DESTROY: " + source.lifecycle.currentState.name)
                // onDestroy()でやるより、ここでremoveObserverするのが楽
                source.lifecycle.removeObserver(this)
            }
        }
        // 何回addしても呼ばれるのは1回
        lifecycle.addObserver(observer)
    }

Android SDKにバンドルされてるIntentアクションのリスト

知ってる人は知ってることなんでしょうけど、自分的には大発見だったので嬉しくて記事にしました。

ことの発端は、OSが投げてくるBroadcastの一覧を探してたところ、BroadcastのAPIガイドを呼んでいたら次のような一文を見つけたためです。

For a complete list of system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the Android SDK.

引用:Broadcasts | Android Developers

慌ててfindコマンド叩いたら本当にありました。全然知らなかった。

$ find . -name "broadcast_actions.txt"
./platforms/android-24/data/broadcast_actions.txt
./platforms/android-25/data/broadcast_actions.txt
./platforms/android-27/data/broadcast_actions.txt
./platforms/android-21/data/broadcast_actions.txt
./platforms/android-26/data/broadcast_actions.txt
./platforms/android-19/data/broadcast_actions.txt

なるほど、APIレベルごとにあるんですね。

Android SDKにバンドルされているリスト

broadcast_actions以外にも一覧として欲しい情報が、いろいろとありました。いづれも sdk/platforms/android-[API level]/data の中にあります。

$ ls
NOTICE.txt            annotations.zip       broadcast_actions.txt features.txt          layoutlib.jar         service_actions.txt   widgets.txt
activity_actions.txt  api-versions.xml      categories.txt        fonts                 res                   tzdata
  • broadcast_actions.txt
    • OSがブロードキャストとして通知するアクションの一覧
  • features.txt
    • AndroidManifestに宣言できる機能の一覧
  • service_actions.txt
    • 各サービスを利用する際に指定するアクションの一覧(?)
  • widgets.txt
    • Viewコンポーネントの一覧(?)
  • activity_actions.txt
    • startActivity()とかで使えるアクションの一覧
  • categories.txt
    • 単なるカテゴリの一覧(?)

※ ?のものは推測です。もしかしたら別の意図のある一覧かもしれません。

まとめ

  • Android SDKには各APIレベル毎にIntentアクションなどのリストがある
  • 各リストはフルネームが列挙されてるので詳細はリファレンスで調べる必要がある

2段階認証を有効にしてるGitHubのHttps認証をMacのkeychainに登録する方法

GitHubのリモートリポジトリを、Httpsでコマンドラインから快適にアクセスするのに、2つ超えなければいけない壁があるのですが、PC買い換える度に毎回ググってるのでまとめました。 ここにまとめた内容は2段階認証を有効にしている場合です。

やること

  1. osxkeychain helper をインストールしてGit Configに設定する
  2. Personal Access Tokenを発行してkeychainに登録する

1. osxkeychain helper をインストールしてGit Configに設定する

1-1. oxkeychain helperがインストールされているか確認する。

以下のように表示されればインストール済み。そうでなければHomebrewで最新のgitをインストールする。

$ git credential-osxkeychain
Usage: git credential-osxkeychain <get|store|erase>

1-2. Gitのconfigにosxkeychain helperを使うことを登録する。

$ git config --global credential.helper osxkeychain

参考:Caching your GitHub password in Git - User Documentation

2. Personal Access Tokenを発行してkeychainに登録する

2-1. Personal Access Tokenを発行する。

Settings -> Developer Settings -> Personal access tokens -> Generate new token を選択する。

Permissionを選択する必要があるので、 repo を選択する。(delete repoもあってもいいかもしれない。)

※ tokenは作成直後しか見られないので、一時的に何かにメモっておいたほうがいい。

2-2. tokenを入力しkeychainに登録する。

リモートリポジトリにアクセスすると、プロンプトでuser nameとpasswordを聞かれるので、passwordに発行したaccess tokenを入力する。 一度入力すれば、自動的にkeychainに登録され次回からは入力を省略できる。

$ git clone https://github.com/username/repo.git
Username: [user name]
Password: [personal access token]

2-3. keychainに登録されるとgithub.comが見える。

f:id:androhi:20171212235049p:plain

参考:Creating a personal access token for the command line - User Documentation

Support Library の 2016 年の issue を振り返る

この記事は「 Qiita Advent Calendar 2016 Android その2 」の 8 日目の記事です。

Android 関連のプロダクトでオープンになっているものは、Issue Tracker で不具合報告や要望などが管理されているのは、ご存知かと思います。 今回はその中で、2016 年に SupportLibrary として issue が切られているものを、振り返ってみたいと思います。なお、調査したのは 2016/12/7 時点の内容です。

対象とした issue

  • Component が Support-Libraries のもの
  • 作成日が 2016/1/1 以降のもの
  • Status は全て対象
  • 上記を満たす issue は 1081 件

Stars Top 3

No.1 : 211 stars

  • ID : 202658
  • Summary : "New support library: support-sqlite. Allow developers to use customized sqlite with existing Android Java bindings"
  • Status : Assigned

サポート・ライブラリに SQLite を扱うためのものを、作ってみてはどうかという issue が最も多くのスターを集めていました。 背景として、Android の OS バージョンによって SQLite のバージョンがバラバラなので、開発者が SQLite をコントロールできないのは不便すぎるからということです。既に、3rd-party のもので存在するようですが、オフィシャルにサポートしてくれると嬉しいので、ぜひ実現して欲しいですね。

No.2 : 153 stars

  • ID : 210615
  • Summary : "Databinding with Jack compiler"
  • Status : Assinged

Jack Compiler で Databinding をビルドできるようにして欲しいという要望のようです。 中の人は Databinding だけ特別扱いできるものではないから、もうちょっと待ってくれとのことです。コメントを見ると徐々にゴールに向かってる感はあるっぽいですね。

No.3 : 109 stars

  • ID : 220250
  • Summary : "FAB can no longer be anchored to indirect children of CoordinatorLayout"
  • Status : Released

スクロールによって連動するようレイアウトしたはずの FAB が、期待通りに動かないという不具合報告のようです。 ver24.2.0 で issue が作成され、ver24.2.1 で修正がリリースされたとして Released になっていますが、その後も同様の現象が起きるという報告がコメントに書かれていました。 CoodinatorLayout と Anchor の組み合わせは、基本形はいいのですがいろんな要素が影響し合うとややこしいですし、使い方の問題なのか不具合なのか難しいですね。

Priority High & Critical

  • Databinding : 9 件
  • ConstraintLayout : 5 件
  • Other : 5 件

プライオリティが、 High もしくは Critical に設定されているものの内訳を、カウントしてみました。ほぼほぼ Databinding と ConstraintLayout に関するものでした。それだけ、どちらも注目度が高いということですね。

最後に

本当は、もっといろいろまとめたかったのですが、時間が無くこれしか書けませんでした。 後日改めて、ちょっとずつ書き足していきたいと思います。

最後に issue tracker からエクスポートした CSV を整理したものを貼って、記事を終えたいと思います。 https://docs.google.com/spreadsheets/d/1pG5e5GVxJmjuF6Y5RCBdsCPiQ8WKSBWZjEBoskUabrQ/edit?usp=sharing

CollaboTips vol.2 に参加しました

主にデザイナーとエンジニアを対象として、お互いに役立つ Tips を披露するという勉強会に参加してきたので、そのレポートを書きたいと思います。

概要

主旨
デザイナーとエンジニアの垣根を取り払いたい人のための勉強会

イベントページ
http://collabotips.connpass.com/event/40038/

日時
2016年10月5日(水)19:30 - 21:30

場所
株式会社 FiNC

ハッシュタグ
#CollaboTips

f:id:androhi:20161005191949j:plain

ウェルネスタイム

まずは LT の前に、FiNC トレーナーの方によるウェルネスタイムから始まりました。FiNC で勉強会が開催される時は恒例となっているようです。参加者のほとんどがデスクワークのため、上半身を中心としたストレッチ行いました。

勉強会も基本座りっぱなしなので、こういうのはいいですね。他の勉強会でも取り入れると良さそうだなと思いました。

LT : 1人5分

LT は全部で10回あり、みなさんそれぞれが本当にいろんな視点での発表だったので、とても楽しく聞くことが出来ました。 以下、発表順に要約と感想を書かせて頂きます。(資料が公開されたら、随時リンクを貼っていきます。)

そして本勉強会では、 @nnnzzz000 さんによるグラフィックレコーディングが行われました!今回の勉強会のまとめとしては、そちらをご覧になると楽しく理解できると思います。(記事の最後に画像をアップしました。)

1. kanamorit さん

タイトル: デザイナとの協働を通じて分かったこと

要約: 協働することで大切なのは、相手の提案、考え方を受け容れ任せる所は任せる。

感想: 実際に現場での経験を話されていたので、とても興味深かったです。

2. yucovin さん

タイトル: プログラミングのすごいところ

要約: デザインとは違う楽しさがあるから、興味ある人はぜひやって欲しい。

感想: デザインとプログラミングの対比がとても納得&面白かったです。

3. masaki_ohsumi さん

タイトル: デザインエンジニアという考え方

要約: デザイナーとエンジニアの橋渡しをするポジションとして、デザインエンジニアという役割を考察する。

感想: 双方の努力で溝を埋められるのもいいけど、その役割を担う人を立てるという考えも、なるほど理にかなってるなと思いました。

4. よこ[りんごマーク]てっく さん

タイトル: PhotoShop でプログラムを使い効率化を図ろう!

要約: スクリプト機能を使うと面倒な作業や些細なミスを激減出来るので試してみて欲しい。デザイナーさんの場合、難しければ周りのエンジニアにお願いしちゃおう。

感想: 座標情報とか本当に大変そうなので、自動化素晴らしいなと思いました。

5. d_date さん

タイトル: Storyboard を使えるようになりたいデザイナーのための思考実験

要約: 簡単な UI を使えるようにしつつ AutoLayout を理解する。ただし Storyboad のコンフリクトに注意。

感想: 最近は sketch 使ってるデザイナーさん多いみたいだし、割とすんなり AutoLayout は理解できるんじゃないかなと個人的には思います。

6. MutsumiOkano さん

タイトル: パッションがとっても大切だった話し

要約: 技術やツールの話しをする前に、お互いの思い(=パッション)を共有する。

感想: 同じゴールを向いてコミュニケーションすることが大切と、理解しました。そういうのひっくるめてパッションと呼ぶのいいかもしれないですね。

7. tomomasa_masuzawa さん

タイトル: 見えないデザイン

要約: よりたくさんの人にサービスを使ってもらうために、アクセシビリティに注意を払おう。

感想: iOS の VoiceOver や Android の TalkBack は、一度自分で使ってみると重要さが分かりますよね。

8. fumiyasac さん

タイトル: デザイナーだった記憶を忘れないために自分なりに気をつけていること

要約: お互いの役割を理解した上で、歩み寄って作業をすると良い。自作アプリなどで実際に自分でいつもと違う役割を体験してみるのもいい。

感想: デザイナーとエンジニアの両方を経験するのは中々難しそうなので、もしそういう人に出会ったら良く話を聞いてみたいなと思いました。

9. JPMartha さん

タイトル: Collaboration

要約: ZenHub の GitHub Project Management のブログは一見の価値あり。ユーザーストーリーをきちんと考えて共有することが大切。

感想: 僕もあまりデザイナーさんとの協働について考える機会を持たなかったので、上記ブログを全部読んでみます。

10. akio さん

タイトル: なし(急遽発表となったため)

要約: iOS の UI パーツを理解するために PlayGround が便利

感想: iPad でプログラミング&実行が楽しめるのは本当に便利そうでしたが、やはりソフトウェアキーボードは辛そうだなと思いました。

まとめ

この勉強会はデザイナーとエンジニアの垣根にフォーカスしてましたが、突き詰めていくとやはり「良いチーム作りとは?」という問題に収束していくのかなと感じました。

少し前に「チームが機能するとはどういうことか」という本を読んだのですが、その中でも職種を横断したチーム作りについて述べられていたり、いかにメンバーを尊重する(決して譲るわけではない)かなど、この勉強会の答えに繋がりそうな内容が多かったなと思います。

ということで、良いプロダクトは良いチームがあってこそ!ということを、今回再認識出来たように思いました。

グラフィックレコーディング

冒頭でふれたグラフィックレコーディングの画像です。 発表を聞きながらスラスラと書かれていたのに、なぜこんなに綺麗にまとめられているのか不思議でなりません。

f:id:androhi:20161005210952j:plain

f:id:androhi:20161005211008j:plain

f:id:androhi:20161005211037j:plain