Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Scala.jsとAndroidでドメイン層を共有しよう / Scala.js and And...
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
AGAWA Koji
December 12, 2019
Programming
820
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Scala.jsとAndroidでドメイン層を共有しよう / Scala.js and Android
社内カンファレンスで発表した資料です。
AGAWA Koji
December 12, 2019
More Decks by AGAWA Koji
See All by AGAWA Koji
Software Architecture in an AI-Driven World
atty303
79
47k
PipeCDプラグインへの期待 / Anticipating PipeCD Plugins
atty303
0
120
EmscriptenでC/C++アプリをWASM化してブラウザで動かしてみた
atty303
0
660
良いソフトウェアとコードレビュー / Good software and code review
atty303
38
18k
Scala + Caliban で作るGraphQL バックエンド / Making GraphQL Backend with Scala + Caliban
atty303
0
600
もう一つのビルドツール mill で作る Docker イメージ / Build docker image with mill the yet another build tool
atty303
2
2.6k
Case of Ad Delivery System is Implemented by Scala and DDD
atty303
4
3.7k
ログのメトリックを取ってみる話
atty303
0
1k
ADC2016: Axion meets HashiCorp
atty303
0
840
Other Decks in Programming
See All in Programming
ふつうのFeature Flag実践入門
irof
8
4.2k
AI 輔助遺留系統現代化的經驗分享
jame2408
1
1k
A2UI という光を覗いてみる
satohjohn
1
160
Spec Driven Development | AI Summit Lisbon
danielsogl
PRO
0
210
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
8.4k
Strategic Design in the Frontend: Moduliths & Micro Frontends @DDDEurope
manfredsteyer
PRO
0
130
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
200
Contextとはなにか
chiroruxx
1
380
その問い、本当に正しいですか?AI時代のエンジニアに必要な哲学と認知科学 / ai-philosophy-cognitive-science
minodriven
14
6.4k
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
280
技術記事、 専門家としてのプログラマ、 言語化
mizchi
13
6.6k
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
170
Featured
See All Featured
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.7k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
400
A Tale of Four Properties
chriscoyier
163
24k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
A designer walks into a library…
pauljervisheath
211
24k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
170
Building a Scalable Design System with Sketch
lauravandoore
463
34k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
260
HDC tutorial
michielstock
2
720
Testing 201, or: Great Expectations
jmmastey
46
8.2k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Transcript
Scala.jsͰAndroidͱυϝΠ ϯΛڞ༗͠Α͏ Ѩ ߞ࢘ @atty303 AI tech studio / CyberAgent,
Inc. © 2019 CyberAgent, Inc. 1/35
Ѩ ߞ࢘ ςοΫϦʔυ @ൢଅαΠωʔδAd • ۀྺ 14 / Scala
ྺ 5 • ήʔϜ͖ (Steam ϝΠϯ) ࠷ۙࣨαΠΫϦϯάΛؤ ுͬͯ·͢ɻ © 2019 CyberAgent, Inc. 2/35
ൢଅαΠωʔδAd © 2019 CyberAgent, Inc. 3/35
© 2019 CyberAgent, Inc. 4/35
৽͍͠ސ٬ମݧͱ࣮ళ ฮͷՁΛ͢Δ Λϛογϣϯʹܝ͛ɺίϯϏχ ΤϯεετΞɾυϥοάετ Ξɾεʔύʔͱ͍࣮ͬͨళฮͷ σδλϧԽɾϝσΟΞԽΛɺσ δλϧαΠωʔδͱίϯςϯπ ʗࠂ৴γεςϜΛ༻͍࣮ͯ ݱ͠·͢ɻ ©
2019 CyberAgent, Inc. 5/35
αΠωʔδͷٕज़ελοΫ • Android 6.0 (API 23) • Kotlin • MediaPlayer
+ TextureView • WebView + Scala.js © 2019 CyberAgent, Inc. 6/35
ϨΠϠʔͱٕज़ • Kotlin υϥΠόͷΈ • υϝΠϯ͓ΑͼΞϓϦέʔ γϣϯ Scala.js © 2019
CyberAgent, Inc. 7/35
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
Scala.js © 2019 CyberAgent, Inc. 9/35
Scala.js • Scala ͔Β JavaScript ʹίϯύΠϧ͢Δͭ • 2013͔Β։ൃ͕ଓ͍͍ͯΔ • ࠷ۙ
1.0-RC ͕ग़ͯɺ20201݄ʹ 1.0 ϦϦʔε༧ ఆ © 2019 CyberAgent, Inc. 10/35
ݸਓతͳҹ • ScalaͰJSॻ͚Δͷ໘ന͍ • JSΤίγεςϜͱͷ૬ੑΛߟ͑ͨΒTypeScript͕ ແͰʁ • มԽͷܹ͍͠ React, Vue,
... ͳͲΛ͑Δͷ͔ • ͍ॴ͕ͦ͠͏ © 2019 CyberAgent, Inc. 11/35
ࠓճ࠾༻ͨ͠ཧ༝ • υϝΠϯΛαʔόʔͱαΠωʔδͰڞ༗ͨ͠ ͔ͬͨ • αʔόʔ Scala Ͱ֬ఆɺαΠωʔδ Android Ͱ֬ఆɺͱ͍͏ঢ়گ
• Android ΑΓ(׳Ε͍ͯΔ) Web ٕज़ʹد͔ͤͨͬ ͨ → Scala.js ͕ϕετϚονͰʂʁ © 2019 CyberAgent, Inc. 12/35
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
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
Scala.js ͷಋೖ • sbt Ͱ fastOptJS λεΫΛ࣮ߦ͢Δͱ js ͕ॻ͖ग़ ͞ΕΔ
$ sbt fastOptJS Ҏ্ɺ؆୯Ͱ͢Ͷ © 2019 CyberAgent, Inc. 15/35
Scala.jsಋೖ·Ͱ • औΓֻ͔͔ͬͯΒ 2 ఔͰྃ • ͱͱ Scala.js ͷ͜ͱΛҰߟྀ͍ͯ͠ͳ͍ ίʔυϕʔε
• υϝΠϯϥΠϒϥϦґଘ͕গͳ͔ͬͨ © 2019 CyberAgent, Inc. 16/35
AndroidͱScala.jsͷܨ͗ © 2019 CyberAgent, Inc. 17/35
interop KotlinͷυϥΠόͱScala.jsͷΞϓϦέʔγϣϯ Λܨ͙ύʔπ © 2019 CyberAgent, Inc. 18/35
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
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
υϝΠϯͷશମ૾ © 2019 CyberAgent, Inc. 21/35
SignageProgram ͜ΕΛαʔόʔͰΈཱͯɺAndroidଆͰར༻͢Δͷ ͕ओత • Protocol Buffers Ͱαʔόʔ͔ΒAndroidૹ৴ • Scala ͰγϦΞϥΠζͯ͠ɺScala.js
ͰσγϦΞϥ Πζ • ྆ଆͱ ScalaPB Ͱ؆୯ʹͰ͖ͨ © 2019 CyberAgent, Inc. 22/35
Scala.js ར༻ͷ © 2019 CyberAgent, Inc. 23/35
Long ܕͷར༻ʹؾΛ͚ͭΔ • JVM ͷ Long 63bit • JS
ͷ Number Double ૬ͳͷͰԾ෦ 52bit • LocalTime#toNanoOfDay() Ͱਫ਼མͪʹૺ۰ • ࣮ߦ࣌ΤϥʔʹͳΔ • toSecondOfDay() + getNano() ͰରԠ © 2019 CyberAgent, Inc. 24/35
Java API جૅతͳ෦ͷΈར༻Մ ೳ • ৄࡉ https://github.com/scala-js/scala-js/tree/ master/javalib/src/main/scala/java Λࢀর ©
2019 CyberAgent, Inc. 25/35
Java API Ͱࠓճࠔͬͨͱ͜Ζ • java.time ύοέʔδ͕σϑΥϧτͰ͑ͳ͍ • "com.zoepepper" %%% "scalajs-jsjoda-as-java-
time" ͷར༻Ͱղܾ © 2019 CyberAgent, Inc. 26/35
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
UseCase Λ JVM/JS Ͱڞ༗͢Δ • DisplayContentUseCase JVM/JS Ͱڞ༗͍ͨ͠ •
JS ͰαΠωʔδʹίϯςϯπΛදࣔ͢Δ • JVM Ͱදࣔ͞ΕΔίϯςϯπΛϓϨϏϡʔ͢ Δ • UseCase ͕ڞ௨ͳͷͰ LocationRepository ͳͲ ͷ Repository JVM ͱ JS ͰಉҰ trait Λ͍ͨ © 2019 CyberAgent, Inc. 28/35
࠷ॳͷܗ trait LocationRepository { def findById(id: LocationId): ConnectionIO[Option[Location]] } •
doobie.free.connection.ConnectionIO • doobie (JDBC ϥΠϒϥϦ) ͷ JS ൛ଘࡏ͠ͳ ͍(વ) • JS ͔Β ConnectionIO ܕΛར༻Ͱ͖ͳ͍ • ͳΜΒ͔ͷநԽ͕ඞཁ © 2019 CyberAgent, Inc. 29/35
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
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
རศੑͷͨΊʹ 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
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
UseCaseͷڞ༗ʹ͍ͭͯ • UseCaseͷڞ༗ͪΐͬͱΓ͔͗͢͠Εͳ͍ • F[_] ͕͍ͬͺ͍ͰΑ͘Θ͔Βͳ͍ʁ • ࣮ࡍɺJVM/JS ͰͦΕͧΕͰUseCaseඍົ ʹҧ͏
• ແཧʹڞ௨ԽͤͣɺJVM/JS ͰUseCaseΛ͚Δ͔ © 2019 CyberAgent, Inc. 34/35
Scala.js ಋೖͷॴײ • Scala.js ࣗମશʹ 1.0 ΫΦϦςΟ͋Δ • पลϥΠϒϥϦॆ࣮ (Pure
Scala ͳϥΠϒϥϦ ΄΅͑Δ) • ࠓճͷΑ͏ʹ༻్͕ϋϚΕ͘͢͝༗༻ • ͰWebϑϩϯτΤϯυΛScala.jsͰॻ͜͏ͱ· ͩࢥΘͳ͍ © 2019 CyberAgent, Inc. 35/35