Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scala.jsとAndroidでドメイン層を共有しよう / Scala.js and Android

AGAWA Koji
December 12, 2019

Scala.jsとAndroidでドメイン層を共有しよう / Scala.js and Android

社内カンファレンスで発表した資料です。

AGAWA Koji

December 12, 2019
Tweet

More Decks by AGAWA Koji

Other Decks in Programming

Transcript

  1. Ѩ઒ ߞ࢘ ςοΫϦʔυ @ൢଅαΠωʔδAd • ۀ຿ྺ 14 ೥ / Scala

    ྺ 5 ೥ • ήʔϜ޷͖ (Steam ϝΠϯ) ࠷ۙ͸ࣨ಺αΠΫϦϯάΛؤ ுͬͯ·͢ɻ © 2019 CyberAgent, Inc. 2/35
  2. αΠωʔδͷٕज़ελοΫ • Android 6.0 (API 23) • Kotlin • MediaPlayer

    + TextureView • WebView + Scala.js © 2019 CyberAgent, Inc. 6/35
  3. Kotlin/Scalaͷίʔυྔ Kotlin $ wc -l `find context/signage/ -name '*.kt'` 1815

    total Scala $ wc -l `find context/signage/ -name '*.scala'` 6515 total © 2019 CyberAgent, Inc. 8/35
  4. Scala.js • Scala ͔Β JavaScript ʹίϯύΠϧ͢Δ΍ͭ • 2013೥͔Β։ൃ͕ଓ͍͍ͯΔ • ࠷ۙ

    1.0-RC ͕ग़ͯɺ2020೥1݄ʹ 1.0 ϦϦʔε༧ ఆ © 2019 CyberAgent, Inc. 10/35
  5. ࠓճ࠾༻ͨ͠ཧ༝ • υϝΠϯ૚ΛαʔόʔͱαΠωʔδͰڞ༗ͨ͠ ͔ͬͨ • αʔόʔ͸ Scala Ͱ֬ఆɺαΠωʔδ͸ Android Ͱ֬ఆɺͱ͍͏ঢ়گ

    • Android ΑΓ(׳Ε͍ͯΔ) Web ٕज़ʹد͔ͤͨͬ ͨ → Scala.js ͕ϕετϚονͰ͸ʂʁ © 2019 CyberAgent, Inc. 12/35
  6. Scala.js ͷಋೖ • project/plugins.sbt ʹ Scala.js ϓϥάΠϯΛ௥Ճ addSbtPlugin("org.scala-js" % "sbt-scalajs"

    % "0.6.31") // JVM/JS ΫϩεϏϧυ͕ඞཁͳ৔߹͸Լه΋௥Ճ addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.1") © 2019 CyberAgent, Inc. 13/35
  7. Scala.js ͷಋೖ • ϓϩδΣΫτઃఆͰ enablePlugins(ScalaJSPlugin) Λ௥Ճ͢Δ lazy val signageDriverSignageJs =

    (project in file("context/signage/driver/signage-js")) .dependsOn(signageApplication.js) .enablePlugins(ScalaJSPlugin) .settings(...) © 2019 CyberAgent, Inc. 14/35
  8. Scala.js ͷಋೖ • sbt Ͱ fastOptJS λεΫΛ࣮ߦ͢Δͱ js ͕ॻ͖ग़ ͞ΕΔ

    $ sbt fastOptJS Ҏ্ɺ؆୯Ͱ͢Ͷ © 2019 CyberAgent, Inc. 15/35
  9. Scala.js ଆ import scala.scalajs.js import scala.scalajs.js.annotation.JSGlobal @js.native @JSGlobal object interop

    extends js.Object { def requestCacheContents(urls: js.Array[String]): Unit = js.native def publish( dataBase64: String, method: String, endpoint: String, headers: String ): Unit = js.native def loadSlot( index: Int, src: String, poster: String, isImage: Boolean, invertedSlotRotation: Int ): Unit = js.native def flipSlot(index: Int): Unit = js.native } © 2019 CyberAgent, Inc. 19/35
  10. Androidଆ import android.webkit.JavascriptInterface import android.webkit.WebView class WebViewInterop( val requestCacheContentsDelegate: RequestCacheContentsDelegate

    // publish ... ) { fun addToWebView(webView: WebView): WebView { webView.addJavascriptInterface(this, "interop") return webView } @JavascriptInterface fun requestCacheContents(urls: Array<String>): Unit { requestCacheContentsDelegate.apply(urls) } // publish ... } © 2019 CyberAgent, Inc. 20/35
  11. Long ܕͷར༻ʹؾΛ͚ͭΔ • JVM ͷ Long ͸ 63bit • JS

    ͷ Number ͸ Double ૬౰ͳͷͰԾ਺෦ 52bit • LocalTime#toNanoOfDay() Ͱਫ਼౓མͪʹૺ۰ • ࣮ߦ࣌ΤϥʔʹͳΔ • toSecondOfDay() + getNano() ͰରԠ © 2019 CyberAgent, Inc. 24/35
  12. Scala.js ͱ࢖͍ͬͯΔϥΠϒϥϦ • ScalaPB • "org.typelevel" %%% "cats-core" • "org.typelevel"

    %%% "cats-effect" • "com.beachape" %%% "enumeratum" • "io.estatico" %%% "newtype" • "eu.timepit" %%% "refined" • "io.lemonlabs" %%% "scala-uri" © 2019 CyberAgent, Inc. 27/35
  13. UseCase Λ JVM/JS Ͱڞ༗͢Δ • DisplayContentUseCase ͸ JVM/JS Ͱڞ༗͍ͨ͠ •

    JS Ͱ͸αΠωʔδʹίϯςϯπΛදࣔ͢Δ • JVM Ͱ͸දࣔ͞ΕΔίϯςϯπΛϓϨϏϡʔ͢ Δ • UseCase ͕ڞ௨ͳͷͰ LocationRepository ͳͲ ͷ Repository ΋ JVM ͱ JS ͰಉҰ trait Λ࢖͍ͨ © 2019 CyberAgent, Inc. 28/35
  14. ࠷ॳͷܗ trait LocationRepository { def findById(id: LocationId): ConnectionIO[Option[Location]] } •

    doobie.free.connection.ConnectionIO • doobie (JDBC ϥΠϒϥϦ) ͷ JS ൛͸ଘࡏ͠ͳ ͍(౰વ) • JS ͔Β ConnectionIO ܕΛར༻Ͱ͖ͳ͍ • ͳΜΒ͔ͷந৅Խ͕ඞཁ © 2019 CyberAgent, Inc. 29/35
  15. Repositoryͷந৅Խͨ͠ܗ trait LocationRepository[F[_]] { def findById(id: LocationId): F[Option[Location]] } //

    JVM trait PostgresLocationRepository extends LocationRepository[ConnectionIO] { def findById(id: LocationId): ConnectionIO[Option[Location]] } // JS trait InMemoryLocationRepository extends LocationRepository[IO] { def findById(id: LocationId): IO[Option[Location]] } © 2019 CyberAgent, Inc. 30/35
  16. Transactor΋ந৅Խ trait Transactor[M[_], N[_]] { def trans(implicit ev: Bracket[N, Throwable]):

    M ~> N } // Doobie class DoobieTransactor[N[_]]( underlying: doobie.util.transactor.Transactor[N] ) extends Transactor[ConnectionIO, N] { override def trans(implicit ev: Bracket[N, Throwable]): ConnectionIO ~> N = underlying.trans } // JS class RawTransactor[N[_]]( implicit liftIO: LiftIO[N] ) extends Transactor[IO, N] { override def trans(implicit ev: Bracket[N, Throwable]): IO ~> N = LiftIO.liftK[N] } © 2019 CyberAgent, Inc. 31/35
  17. རศੑͷͨΊʹ Transactable ܕΫϥ εΛఆٛ trait Transactable[M[_]] { def transact[A](transactor: Transactor[M,

    IO])(m: M[A])(implicit ev: Bracket[IO, Throwable]): IO[A] } // Doobie implicit lazy val transactableForConnectionIO: Transactable[ConnectionIO] = new Transactable[ConnectionIO] { override def transact[A]( transactor: repository.Transactor[ConnectionIO, IO] )(m: ConnectionIO[A])(implicit ev: Bracket[IO, Throwable]): IO[A] = transactor.trans.apply(m) } // JS implicit lazy val transactable: Transactable[IO] = new Transactable[IO] { override def transact[A](transactor: Transactor[IO, IO])(m: IO[A])(implicit ev: Bracket[IO, Throwable]): IO[A] = transactor.trans.apply(m) } © 2019 CyberAgent, Inc. 32/35
  18. UseCase͔Βར༻͢Δ class DisplayContentUseCase[F[_]]( transactor: Resource[IO, Transactor[F, IO]], locationRepository: LocationRepository[F] )(

    implicit bracketForF: Bracket[F, Throwable], transactableForF: Transactable[F] ) { def apply(locationId: LocationId): IO[Unit] = { val transIo: IO[Location] = transactor.use { xa => val io = for { maybeLocation <- locationRepository.findById(locationId) location <- maybeLocation.liftTo[F](new LocationNotFountError) } yield location Transactable[F].transact(xa)(io) } } for { location <- transIo // ... } yield () } © 2019 CyberAgent, Inc. 33/35
  19. Scala.js ಋೖͷॴײ • Scala.js ࣗମ͸׬શʹ 1.0 ΫΦϦςΟ͸͋Δ • पลϥΠϒϥϦ΋ॆ࣮ (Pure

    Scala ͳϥΠϒϥϦ͸ ΄΅࢖͑Δ) • ࠓճͷΑ͏ʹ༻్͕ϋϚΕ͹͘͢͝༗༻ • Ͱ΋WebϑϩϯτΤϯυΛScala.jsͰॻ͜͏ͱ͸· ͩࢥΘͳ͍ © 2019 CyberAgent, Inc. 35/35