第1回コベチケの会

コベリンの最近の取り組みとして、業務などで役立ちそうな知見を共有する会を開催することになりました。今回は記念スべき第1回目。

この記事は、会のアジェンダをそのままコピペ(※)して公開したものなので若干見にくい箇所もあるかもしれませんが、ご了承ください。

※ コベリンの活動をオープンに紹介するためにこのようにしています。

休日と休暇の違いについて @yanac

  • 休日と休暇の違いについて 所定休日 法定休日 法定休暇 などの違いについて

React 関連古今 @mironal

コベリンで以前 React を作ったプロダクトを作ってたときと最近は全然雰囲気変わったのでその違いを解説する。詳しくは説明はせず、こういう変化があったということを説明するので必要に応じて各々ググってほしい。

副作用(state) の取り扱い方と hooks API の登場

これから React 使うときに hooks API の存在を知らないと、タイムマシーンに乗ってきた人なの?って思われちゃうので情報展開。

React の state 持ってて副作用もってたり componentDidMount 使いたいときに class 作ってた.

class Example extends React.Component {
  this.state = { 
    count: 0,
    disableButton: false,
    title: null
  }
  componentDidMount() {
   this.state.count = 1 // 更新されない
  setState({
    ...this.state
    count: 0
  })
    // state いじってる
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate(prevProps, prevState) {
      // こんなの書いてた
     if (prevState.count !== this.state.count) { 
     }
    // componentDidMount と同じ処理を書くの辛すぎ
    document.title = `You clicked ${this.state.count} times`;
  }
  render() { /* 略 */ }
}

最近は hooks (日本語だと副作用フック と呼ばれている) が追加されたので class 書かなくて良くなった. 登場したのは 2019年.

もう最近いつ class 書いたか覚えていない...

hooks API は use で始まるメソッド. React 自体が用意している useStateuseEffect、その他のいろいろなライブラリが用意しているもの useRouteruseHogeHoge など無限にある.

// コードが短くなってハッピー
// function で書けてハッピー
function Example() {
  const [count, setCount] = useState(0);
  const [disableButton, setDisableButton] = useState(true);
  const [title, setTitle] = useState<boolean | null>(null); // true or false or null
count = 1 // error 
  // boolean? : true | false | undeined
  // boolean | null: true or false or null

  useEffect(() => { // 一箇所だけ書けば良くてハッピー
    document.title = `You clicked ${count} times`;
  }, [count]);
  return /* 略 */
}

色々良いことが多いが hooks はトップレベルで呼び出さなければいけないなど制限も色々ある. しかしこの辺は lint 入れておけば入れておけば警告してくれるので大丈夫.

https://github.com/streamich/react-use という便利な hooks が集まってるライブラリもあるので要チェック.

  • redux の connect(mapStateToProps,mapDispatchToProps)(App) みたいなやつも useSelectoruseDispatch で書けるようになった
  • react router も withRouter を使わずに useHistory とかが書けるようになった

default export withRouter(HogePage) とかもうやらない.

まとめ

いろいろな変化があるので hooks API が用意されてる場合はまずはそれを使うようにしよう.

ググったときに古い情報を参考にしないようにしよう.

hooks API (というか React)は今も議論されていて変化があるので、常に気をつけよう.

numa08

Androidアプリで使うDI、依存性注入のこと。以前はDagger2やkoinなど様々なライブラリ、手法が乱立していてしのぎを削っていたが昨年登場したHiltを使うのがデファクトスタンダードになりつつあるので、その流れに乗ったほうが良いよという話。

今までのDI

今までAndroidでDIを行うにはDagger2やkoinと呼ばれるライブラリがあって、どれを使うか検討が必要だった。

  • Dagger2 : DIと呼ばれることに必要な機能は全て揃えているが、前提となる知識も多くて学習コストが高い。利用者数が多いのでデファクト・スタンダードだった
  • koin: KotlinでAndroidのプロジェクト開発をするなら選択肢に上がる、kotlinのために作られたDIライブラリ

なぜDIを使うのか

Android特有の理由として、Androidの「ライフサイクル」という概念が普通のJava、Kotlinのコーディングを邪魔している背景がある。ライフサイクルに管理されたコンポーネントはコンストラクタをプログラマが呼び出してはならないルールがあることから、コンストラクタを使った依存性の注入が不可能。そこでApplicationなどの広いスコープでインスタンスを管理することになるが、その場合インスタンス初期化に関するボイラープレート的コードがどうしても増えてしまう。テストコードも同様に複雑になる。

// ボイラープレート的なコードの例

class MyApplication: Application() {
  companion object {
    // アプリ全体で再利用したいインスタンスを格納しておく
    // テストできるように var にしておくが、本来別の場所からインスタンスの変更を意図したもの
    // ではないので、無用なバグを生みがちなコードとなっている
    var database: Databse = //DBにアクセスするためのなにか
    var apiService: ApiService // Web apiにアクセスするためになにか
  }
}

class MainActivity: Activity() {
  lateinit var database: Database
  lateinit var apiService: ApiService
  
  // ViewModelを使うとき、依存するインスタンスの初期化前に viewModel にアクセスするとクラッシュするのでコーディング上の
  // 注意が必要
  val viewModel: MainViewModel by lazy { MainViewModelFactory(databse, apiService).create() }

  // 画面処理開始時に呼ばれるコールバックの中で、各種インスタンスを初期化する
  // 必要なインスタンスが増えるとここのコードも増えていくが、同じコードが複数箇所に出現し
  // コピペとかになりそう
  fun onCreate() {
      database = MyApplication.database
      apiService = MyApplication.apiService
      
  }
}

この辺のことをTwitterで会話したログ。

https://twitter.com/numa08/status/1387290339753431041

Dagger2の登場で改善は見られたが、Dagger2を使うためにはModuleComponentと呼ばれるinterfaceが必要になることや、以下のように画面ごとにメソッドを用意する必要があり、アプリの規模によっては結構な量のコードが必要でアプリ開発途中からの導入が躊躇われる。

// 画面の数だけ内容が増えるModule
@Module
intarface FragmentModule {
  @ContributesAndroidInjector
  abstract fun contirbuteMainFragment(): MainFragment
  @ContributesAndroidInjector
  abstract fun contributeSecondFragment(): SecondFragment
  @ContributesAndroidInjector
  abstract fun contributeThirdFragment(): ThridFragment
  ...
}

// Moduleの数だけアノテーションの引数が増えるコンポーネント
@Component(
  modules = [
    NetworkModule::class
    DatabaseModule::class
    FragmentModule::class
    ...
  ]
)
interface ApplicationComponent {}

Hiltについて

2020年5月くらいにjetpack componentで正式リリースされたDIライブラリ。物としてはDagger2の強いラッパーなので、Dagger2を使っていた人やプロジェクトに優しいけど学習コストは抑えめなのでHiltからDIを使い始めるのも無理なくできる。

Hilt を使用した依存関係の注入  |  Android デベロッパー  |  Android Developers Android アプリでの Hilt の使用

class DatabseImpl @Inject constructor(sqlite: SQLite): Database {} // DB にアクセスするためのなにか

@Module
// InstallInでスコープごとに事前定義されたコンポーネントを指定する。
// Dagger2と逆でModuleがComponentを指定するため、Componentの引数は増えない
// Activityライフライクルで消失して良いインスタンスなら、ActivityComponent::classを使う
@InstallIn(SingletonComponent::class) 
abstract class DatabaseModule {

  @Singleton
  // コンストラクタの呼び出しをしない物については、Providesを使う
  @Provides
  fun provideSQLite(@ApplicationContext context: Context): SQLite
    = context.getSystemService("SQLite") as! SQLite
  
  @Singleton
  // コンストラクタを定義した物は Binds を使ってコンストラクタの呼び出しコードを生成する
  @Binds
  fun bindDatabase(databse: DatabaseImpl): Database
}

// ViewModelInject でコンストラクタの呼び出しコードなんかが自動生成される
class MainViewModel @ViewModelInject constructor(
  private val databse: Database,
  private val apiService: ApiService
): ViewModel() {}

@AndroidEntryPoint
class MainActivity: AppCompatActivity() {
   // by viewModel() でMainViewModelインスタンスの作成が適切に行われるので、
   // onCreate後onDestroy前の好きなタイミングでViewModelの利用ができる
  val viewModel: MainViewModel by viewModel()
}

まとめ

  • 以前までとくらべるとDI導入コストが下がったので使っていきましょう

@takkumattsu

flutter の Pods周りのあれこれ

さまざまのエラーがあるけどよくある解決方法(エラーはあとで貼っておく)

  • ios/Podsを消す
    • これ解決することは少ない気がする
  • pod repo update
    • podのリポジトリが古くて指定されているpodがない時に発生する
  • 実はPodが更新されていないパターン(Podfile.lockが更新されてない)
    • Podfile.lockを消すと更新されたPodfileの内容でインストールされて動くことがある
  • Podfile#platform :ios, '9.0 を調整する
    • デフォルトのままだと Development Target が対応していないものがありエラーになることがある
    • 例えば firebase_core の1.3.0はDevelopment Targetが 10.0以上じゃないとビルドエラーになる
    • しかしこれを上げすると他のライブラリで対応してないDevelopment Targetのものがあるとエラーになる
    • この辺のことドキュメントに書いてないような気がしている

所感

Xcode まわりのエラーが貧弱で結構辛い時が多いなー