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

小鳥さんとKotlinを勉強できるサイトを 100%Kotlinで作る

小鳥さんとKotlinを勉強できるサイトを 100%Kotlinで作る

IM@S Engineer Talks 2019の30分枠発表資料です

https://imas.connpass.com/event/134735/
小鳥さんの自習室: https://github.com/subroh0508/otonashi-kotlin.dev

Avatar for subroh_0508

subroh_0508

July 06, 2019
Tweet

More Decks by subroh_0508

Other Decks in Technology

Transcript

  1. ࣗݾ঺հ w ʹ͜͠Γ͞ͿΖʙ ͱಉ͍೥  w גࣜձࣾ#FBS5BJM w "OESPJEΤϯδχΞ ,PUMJO+BWB

    ˑ w 8FCΤϯδχΞ 3BJMT3FBDU  w ୲౰଎ਫ૗ɾࡾๆ݁՚ɾਅนਸ਼ر 5XJUUFS*%!TVCSPI@ ˣ/&8 ϛϦUI෱Ԭʙ
  2. ͦͷલʹʜ w ,PUMJOͱ͸ w +FU#SBJOT੡ɺ੩తܕ෇͚ͷΦϒδΣΫτࢦ޲ϓϩάϥϛϯάݴޠ w +7.্Ͱಈ࡞ +BWBͱ4DBMBͷதؒʹҐஔ  w

    (PPHMF͕"OESPJEΞϓϦͷ։ൃݴޠͱͯ͠ਖ਼ࣜαϙʔτʂ w ,PUMJO+4ɾ,PUMJO/BUJWF౳ɺΫϩεϓϥοτϑΥʔϜ։ൃʹ΋ରԠ
  3. ,PUMJO͸͍͍ͧʜ Button button = …; button.setOnClickListener(new View.OnClickListener() { @Override public

    void onClick(View v) { Log.d("tag", "Clicked!") } }); ྫϘλϯΛԡ͢ͱʮ$MJDLFEʯͱදࣔ "OESPJE +BWB ˠΠϯλʔϑΣʔεఆ͕ٛ৑௕
  4. ,PUMJO͸͍͍ͧʜ val button: Button = ... button.setOnClickListener { v ->

    Log.d("tag", "Clicked!") } ྫϘλϯΛԡ͢ͱʮ$MJDLFEʯͱදࣔ "OESPJE ,PUMJO ˠ4".ม׵ ΠϯλʔϑΣʔεఆٛΛϥϜμͰॻ͚Δ
  5. ,PUMJO͸͍͍ͧʜ ྫϦετΛηϛίϩϯͰܨ͛ͯେจࣈʹม׵ ˠ/VMMڐ༰ɾඇڐ༰Λʮ ʯͰ໌ࣔɺڐ༰ͷ৔߹ίϯύΠϧΤϥʔ ˠʮMJTU KPJO5P4USJOH lz ʯͰ ɹɹɹʮMJTU͕OVMMͷ࣌͸KPJO5P4USJOHΛ࣮ߦ͠ͳ͍ʯ fun

    joinAndUpperCase(list: List<String>?): String { return list?.joinToString(":")?.toUpperCase() :? "" } γϯϓϧ͔࣮ͭ༻తͳจ๏Ͱʮ+BWBͭΒ͍ʯΛղܾʂɹͦΕ͕,PUMJOʂ
  6. ࡞ͬͨ΋ͷ w খௗ͞Μͷࣗशࣨখௗ͞Μͱ,PUMJOΛֶ΂ΔαΠτ w (JUIVCTVCSPIPUPOBTIJLPUMJOEFW w ػೳ w ,PUMJOͷجૅతͳจ๏ղઆ w

    8FC্Ͱখௗ͞ΜͱϖΞϓϩάϥϛϯά͠ͳ͕Β,PUMJOΛษڧͰ͖Δʂ w ,PUMJOΛษڧ͍ͨ͠খௗ͞ΜͱͷίϛϡΛָ͠ΊΔʂ
  7. ͳͥ࡞Ζ͏ͱࢥͬͨ ͱΓ͋͑ͣখௗ͞Μͱʮ)FMMP 8PSMEʯͯ͠Έͨ w Ṗͷ೤ҙͰશͯ,PUMJOͰ࣮૷ w ͔Θ͍͗ͨ͢ɹਓͰൃڰͨ͠ w 35͍͍Ͷ w

    1͚ͩͰͳ͘ɺ,PUMJOք۾ͷਓ͔Β΋͍͍൓Ԡ খௗ͞ΜͱͷϖΞϓϩ͠ͳ͕ΒษڧͰ͖ΔαΠτ ׂͱधཁ͕͋Γͦ͏ʂɹ͋ͱ๻͕ΊͬͪΌָ͍͠
  8. ࣮૷ղઆ w αʔόʔαΠυ w ,UPS w ϑϩϯτΤϯυ w w ,PUMJO+4

    w LPUMJOQMBZHSPVOE w SFBDISPVUFS w NBUFSJBMVJ w SFBDUTXJQFBCMFWJFXT w SFWFBMKT
  9. ,UPSͰ)FMMP 8PSME w "QQMJDBUJPOLUʹϧʔςΟϯάͱϨεϙϯε௥ه fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

    @Suppress("unused") // Referenced in application.conf @kotlin.jvm.JvmOverloads fun Application.module(testing: Boolean = false) { routing { get("/") { call.respondText("Hello, World!”, ContentType.Text.Plain) } } } ίί
  10. ,UPSͰ)FMMP 8PSME w HSBEMFXSVOΛ࣮ߦˠMPDBMIPTUͰαʔόʔ্ཱ͕ͪΔ $ ./gradlew run Starting a Gradle

    Daemon, 1 busy and 1 stopped Daemons could not be reused, use --status for details > Task :compileKotlin … $ curl http://localhost:8080 Hello, World! ʻͰ͖·ͨ͠ʂ
  11. route("/api/v1") { @Location("/{section}/{task}/task_results") data class TaskResultsParams(val section: String, val task:

    String, val output: String, val status: String) get<TaskResultsParams> { params -> val result = TaskResultService.validate(...) call.respond(HttpStatusCode.OK, result) } @Location("/{section}/{task}/start_conversations") data class StartConversationsParams(val section: String, val task: String) get<StartConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } @Location("/{section}/{task}/coding_conversations") data class CodingConversationsParams(val section: String, val task: String, val code: String?) get<CodingConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } }
  12. route("/api/v1") { @Location("/{section}/{task}/task_results") data class TaskResultsParams(val section: String, val task:

    String, val output: String, val status: String) get<TaskResultsParams> { params -> val result = TaskResultService.validate(...) call.respond(HttpStatusCode.OK, result) } @Location("/{section}/{task}/start_conversations") data class StartConversationsParams(val section: String, val task: String) get<StartConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } @Location("/{section}/{task}/coding_conversations") data class CodingConversationsParams(val section: String, val task: String, val code: String?) get<CodingConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } } ඪ४ग़ྗͷ஋ʹର͢Δ খௗ͞Μͷฦ౴Λฦ͢"1*
  13. route("/api/v1") { @Location("/{section}/{task}/task_results") data class TaskResultsParams(val section: String, val task:

    String, val output: String, val status: String) get<TaskResultsParams> { params -> val result = TaskResultService.validate(...) call.respond(HttpStatusCode.OK, result) } @Location("/{section}/{task}/start_conversations") data class StartConversationsParams(val section: String, val task: String) get<StartConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } @Location("/{section}/{task}/coding_conversations") data class CodingConversationsParams(val section: String, val task: String, val code: String?) get<CodingConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } } ՝୊Λදࣔͨ͠௚ޙͷ খௗ͞Μͷฦ౴Λฦ͢"1*
  14. route("/api/v1") { @Location("/{section}/{task}/task_results") data class TaskResultsParams(val section: String, val task:

    String, val output: String, val status: String) get<TaskResultsParams> { params -> val result = TaskResultService.validate(...) call.respond(HttpStatusCode.OK, result) } @Location("/{section}/{task}/start_conversations") data class StartConversationsParams(val section: String, val task: String) get<StartConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } @Location("/{section}/{task}/coding_conversations") data class CodingConversationsParams(val section: String, val task: String, val code: String?) get<CodingConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } } ฤूதͷίʔυΛड͚ͯ খௗ͞ΜͷಠΓݴΛฦ͢"1*
  15. route("/api/v1") { @Location("/{section}/{task}/task_results") data class TaskResultsParams(val section: String, val task:

    String, val output: String, val status: String) get<TaskResultsParams> { params -> val result = TaskResultService.validate(...) call.respond(HttpStatusCode.OK, result) } @Location("/{section}/{task}/start_conversations") data class StartConversationsParams(val section: String, val task: String) get<StartConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } @Location("/{section}/{task}/coding_conversations") data class CodingConversationsParams(val section: String, val task: String, val code: String?) get<CodingConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } } ϦΫΤετύϥϝʔλʔʹର͢Δ ܕόϦσʔγϣϯΛઃఆ
  16. route("/api/v1") { @Location("/{section}/{task}/task_results") data class TaskResultsParams(val section: String, val task:

    String, val output: String, val status: String) get<TaskResultsParams> { params -> val result = TaskResultService.validate(...) call.respond(HttpStatusCode.OK, result) } @Location("/{section}/{task}/start_conversations") data class StartConversationsParams(val section: String, val task: String) get<StartConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } @Location("/{section}/{task}/coding_conversations") data class CodingConversationsParams(val section: String, val task: String, val code: String?) get<CodingConversationsParams> { params -> val conversation = CodingConversationService.build(...) call.respond(HttpStatusCode.OK, conversation) } } ϞδϡʔϧΛ௥Ճ͢Δ͜ͱͰ 0CKFDUˠ+40/΁ͷϚοϐϯάΛ ͋Δఔ౓ ࣗಈԽ
  17. ϑϩϯτΤϯυ w ,PUMJO+4,PUMJOΛ+BWBTDSJQUʹτϥϯεύΠε͢ΔϑϨʔϜϫʔΫ w ඪ४ϥΠϒϥϦ w ,PUMJOˠ+4ม׵ͷ(SBEMFϓϥάΠϯ LPUMJOKT  w

    OQN΍8FCQBDLͷίϚϯυΛ(SBEMFܦ༝Ͱ࣮ߦ͢ΔϓϥάΠϯ LPUMJOGSPOUFOEQMVHJO ʻԿ΋Θ͔Βͳ͍Ͱ͢ʜ
  18. ϑϩϯτΤϯυͷ࣮૷ w Ͳ͏΍ͬͯ,PUMJOͰॻ͍ͯΔͷ w 3FBDU+FU#SBJOT͕ϥούʔϥΠϒϥϦΛఏڙ LPUMJOSFBDU class App extends React.Component

    { render() { return ( <div className='title'> Welcome to React with Kotlin </div> ) } } class App : RComponent<RProps, RState>() { override fun RBuilder.render() { div("title") { +"Welcome to React with Kotlin" } } } 3FBDU ,PUMJO+4
  19. ϑϩϯτΤϯυͷ࣮૷ w ͦͷଞ!+T.PEVMFΞϊςʔγϣϯΛ࢖ͬͯ,PUMJOͷੈքʹΠϯϙʔτ w ྫSFBDUTXJQFBCMFWJFXT @JsModule("react-swipeable-views") private external val reactSwipeableViewModule:

    dynamic fun test() { reactSwipeableViewModule.default.onChangeIndex = { ... } } +4ϥΠϒϥϦΛΠϯϙʔτ PO$IBOHF*OEFYʹ ؔ਺ϦςϥϧΛ୅ೖ
  20. EZOBNJDܕͷ᠘ w EZOBNJD,PUMJOͰ+4ͷΦϒδΣΫτΛͦͷ··ѻ͑Δܕ // { foo: 'foo', bar: { hoge:

    '111' } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: '111' } }") print(jsObject["foo"]) // => 'foo' print(jsObject["bar"]["hoge"]) // => 111 jsObject["aaa"]["bbb"] = "add from kotlin" print(jsObject["aaa"]["bbb"]) // => add from kotlin
  21. EZOBNJDܕͷ᠘ w EZOBNJD,PUMJOͰ+4ͷΦϒδΣΫτΛͦͷ··ѻ͑Δܕ // { foo: 'foo', bar: { hoge:

    '111' } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: '111' } }") print(jsObject["foo"]) // => 'foo' print(jsObject["bar"]["hoge"]) // => 111 jsObject["aaa"]["bbb"] = "add from kotlin" print(jsObject["aaa"]["bbb"]) // => add from kotlin KT ʜ ϝιου,PUMJOίʔυதʹੜͷ+4ίʔυΛ௚઀ॻ͚Δϝιου
  22. EZOBNJDܕͷ᠘ w EZOBNJD,PUMJOͰ+4ͷΦϒδΣΫτΛͦͷ··ѻ͑Δܕ // { foo: 'foo', bar: { hoge:

    '111' } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: '111' } }") print(jsObject["foo"]) // => 'foo' print(jsObject["bar"]["hoge"]) // => 111 jsObject["aaa"]["bbb"] = "add from kotlin" print(jsObject["aaa"]["bbb"]) // => add from kotlin ଘࡏ͢ΔΩʔͷ஋Λऔͬͯ͘Δ
  23. EZOBNJDܕͷ᠘ w EZOBNJD,PUMJOͰ+4ͷΦϒδΣΫτΛͦͷ··ѻ͑Δܕ // { foo: 'foo', bar: { hoge:

    '111' } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: '111' } }") print(jsObject["foo"]) // => 'foo' print(jsObject["bar"]["hoge"]) // => 111 jsObject["aaa"]["bbb"] = "add from kotlin" print(jsObject["aaa"]["bbb"]) // => add from kotlin ଘࡏ͠ͳ͍Ωʔͷ஋Λ +4ʹ͍ۙจ๏Ͱ୅ೖͰ͖ͯ͠·͏
  24. EZOBNJDܕͷ᠘ w EZOBNJD,PUMJOͰ+4ͷΦϒδΣΫτΛͦͷ··ѻ͑Δܕ // { foo: 'foo', bar: { hoge:

    '111' } }をKotlinで扱う場合 val jsObject: dynamic = js("{ foo: 'foo', bar: { hoge: '111' } }") print(jsObject["foo"]) // => 'foo' print(jsObject["bar"]["hoge"]) // => 111 jsObject["aaa"]["bbb"] = "add from kotlin" print(jsObject["aaa"]["bbb"]) // => add from kotlin ΍ͩʜมͩͶ ,PUMJOΛɺ,PUMJOΛॻ͍ͯͨ͸ͣͳͷʹʜ ͍͔ͭΒ+BWBTDSJQUॻ͍ͯͨΜͩΖ͏ʜ ʜͰ΋ʜࢲʜͨͩʜࢲʜΈΜͳͱʜ
  25. EZOBNJDܕͷ᠘ w FYUFSOBMम০ࢠ,PUMJOͷΠϯλʔϑΣʔεͱ+4ͷΦϒδΣΫτΛࣗಈͰ Ϛοϐϯάͯ͘͠ΕΔम০ࢠ external interface TestObject { var foo:

    String var bar: TestChildObject } external interface TestChildObject { var hoge: String } val jsObject: TestObject = js("{ foo: 'foo', bar: { hoge: '111' } }") print(jsObject.foo) // => 'foo' print(jsObject.bar.hoge) // => 111 5ZQF4DSJQUͷܕఆٛϑΝΠϧͱ ໾ׂ͸ಉ͡
  26. EZOBNJDܕͷ᠘ @JsModule("react-swipeable-views") private external val reactSwipeableViewModule: dynamic external interface ReactSwipeableProps

    : RProps { var index: Int var disabled: Boolean var onChangeIndex: (Int, Int, ChangeReason) -> Unit var onSwitching: (Int, String) -> Unit } fun ReactSwipeableProps.onChangeIndex(block: (Int, Int?, ReasonType?) -> Unit) { onChangeIndex = { index: Int, indexLatest: Int, reason: ChangeReason -> block(index, indexLatest, ReasonType.valueOf(reason.reason)) } }
  27. EZOBNJDܕͷ᠘ @JsModule("react-swipeable-views") private external val reactSwipeableViewModule: dynamic external interface ReactSwipeableProps

    : RProps { var index: Int var disabled: Boolean var onChangeIndex: (Int, Int, ChangeReason) -> Unit var onSwitching: (Int, String) -> Unit } fun ReactSwipeableProps.onChangeIndex(block: (Int, Int?, ReasonType?) -> Unit) { onChangeIndex = { index: Int, indexLatest: Int, reason: ChangeReason -> block(index, indexLatest, ReasonType.valueOf(reason.reason)) } } ʮPO$IBOHF*OEFYʹؔ਺ϦςϥϧΛ୅ೖʯΛ ,PUMJOͬΆ͘ॻ͚ΔΑ͏ʹ֦ͨ͠ுؔ਺
  28. .BUFSJBM6*ͱͷಆ͍ w .BUFSJBM6*Ҏ֎ͷϥΠϒϥϦ w ͦ΋ͦ΋খ͍͞ˠߦຬͨͳ͍͘Β͍ͷܕఆٛ ؔ਺ఆٛͰ0, w .BUFSJBM6* w Λ௒͑Δ6*ίϯϙʔωϯτΛ࣋ͭ

    w ܧঝؔ܎͕͋ͬͨΓɺ,PUMJO+4ͷ࣮૷Λਂ۷Βͳ͍ͱ࢖͑ͳ͔ͬͨΓ w ϥούʔϥΠϒϥϦ࡞੒ɺ࢖͑ΔΑ͏ʹˠTVCSPILPUMJONBUFSJBMVJ
  29. ·ͱΊ w ,PUMJO཰ʹ͍ۙ8FCαΠτΛṖͷ೤ҙͰ࡞ͬͨ w ,UPS͸͙͢ʹ৮ΕΔʂ w ,PUMJO+4͸,PUMJOʹ׳Ε͔ͯΒ৮Δͱ୔ࢁͷൃݟ͕͋Δʂ w ,PUMJO͸͘͢͝ྑ͍ݴޠ w

    ʮ+BWBͭΒ͍ʯͷଟ͘Λղফɺ։ൃऀͷੜ࢈ੑʹେ͖͘د༩ w ,PUMJO'FTU݄೔։࠵ ෼࿮$G1ఏग़ࡁ