Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Scala.jsとAndroidでドメイン層を共有しよう / Scala.js and And...
Search
AGAWA Koji
December 12, 2019
Programming
830
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
49k
PipeCDプラグインへの期待 / Anticipating PipeCD Plugins
atty303
0
140
EmscriptenでC/C++アプリをWASM化してブラウザで動かしてみた
atty303
0
700
良いソフトウェアとコードレビュー / Good software and code review
atty303
38
19k
Scala + Caliban で作るGraphQL バックエンド / Making GraphQL Backend with Scala + Caliban
atty303
0
620
もう一つのビルドツール 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
1.1k
ADC2016: Axion meets HashiCorp
atty303
0
870
Other Decks in Programming
See All in Programming
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
FreeBSDでZabbixを動かす
kenkino
0
340
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
810
Androidだけじゃない、DroidKaigiで広がった私の技術スタック
akkeylab
0
110
Heart of Swift Concurrency
koher
0
1.1k
フロントエンドUIフレームワークのこれまでとこれから
ssssota
5
3.1k
手動確認はもう限界 〜XCUITestでCustom URL Schemeの遷移を起動種別ごとに自動テストする〜 / Testing Custom URL Schemes with XCUITest
otouto
0
370
wkhtmltopdfの次どうするか問題2026
willnet
2
1.8k
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
5.2k
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
330
Turning Architecture into Unit Tests in the AI Era (NSSpain XIV)
steliosf
PRO
1
120
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
200
Featured
See All Featured
KATA
mclloyd
PRO
35
16k
Technical Leadership for Architectural Decision Making
baasie
3
590
brightonSEO & MeasureFest 2025 - Winning Strategies for Black Friday CRO & PPC - Christian Goodrich
cargoodrich
3
860
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
560
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.4k
BBQ
matthewcrist
89
10k
Practical Orchestrator
shlominoach
192
12k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
A Modern Web Designer's Workflow
chriscoyier
699
190k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.3k
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