A week of Firebase & Kotlin

Ta Theerasan Tonthongkam
ta tonthongkam
Published in
4 min readSep 10, 2018

ในสอง Blog ก่อนหน้าได้นำเสนอการใช้ Kotlin2JS ไป และยังนำมาประยุกต์ใช้กับ Firebase functions อีกด้วย — จากนั้นก็เริ่มทำโปรเจคส่วนตัว อย่างจริงจังโดยใช้ Kotlin2Js และ Firebase Admin ได้ประมาณ 1 สัปดาห์แล้ว เลยอยากจะแชร์ความโหดร้าย กับความสวยงามให้ดูกัน เพื่อประกอบการตัดสินใจว่า ถ้าเราอยากเริ่มโปรเจค Firebase x Kotlin แล้ว เราจะพร้อมหรือป่าว

ทำไมถึงเลือกใช้ Kotlin?

อย่างแรกเลย Firebase ถูกออกแบบมาให้ใช้กับ JavaScript หรือ TypeScript แต่ในฐานะที่เป็น Android Developer เคยใช้ JAVA และ Kotlin มาก่อน ก็เลยอยากลดกำแพงด้านภาษา โดยเอาภาษาที่คุ้นเคยมากกว่ามาใช้ — แน่นอนว่า มันลดกำแพงลงไปได้ แต่ก็สร้างปัญหาอื่นๆ ตามมาโดยที่ใน Document ไม่มีบอก — แต่ชีวิตมันยังยืนยันที่จะไป — ก็เลยลองกับมันสักตั้ง

ข้อได้เปรียบของ Kotlin

เมื่อเทียบกับ JavaScript และ TypeScript และ Kotlin มีข้อแตกต่างที่เหนือกว่า เช่น Extension Function; การจัดการกับ Data Model ที่ง่ายกว่า; ความง่ายในการ import เรียกใช้ Class ไม่ต้อง Export Module; ความกระชับของภาษา และอื่นๆ อีกมากมาย

IDE ที่จะเอามาใช้กับ JavaScript และ TypeScript ได้ แล้วมี Feature ดีๆ เท่ากับ WebStrom และราคาสบายกระเป๋า แทบไม่มีเลย — แต่ Kotlin สามารถเปิดโดย IntelliJ CE ได้ และฟรีอีกด้วย — การใช้เครื่องมือดี ก็มีชัยไปกว่าครึ่งแล้ว

Document ของ Kotlin ก็ทำมาออกมาดีเลยทีเดียว สามารถหาข้อมูลใน StackOverFlow ได้มาก และมีแนวโน้มจะมากขึ้นเรื่อยๆ

เคยลองทำการเปรียบเทียบระหว่าง TypeScript และ Kotlin โดยที่ Class นี้ทำงานเหมือนกันทุกอย่าง TypeScript ใช้โค้ดไป 200+ บรรทัด ในขนาดที่ Kotlin เขียนโค้ดแค่ 50 บรรทัดเอง

ด้วยภาษา ด้วยเครื่องมือ มันต่างช่วยให้ทำงานเร็วขึ้นทั้งนั้น นั้นเป็นเหตุผลที่ว่า ทำไมผมถึงอยากเสี่ยงใช้ Kotlin + Firebase

สร้างทางเดินเองมักไม่ง่าย

เมื่อตัดสินใจแล้วก็เลยเริ่มลุยโปรเจคเลย เริ่มจากง่ายๆ ก่อน สร้าง CRUD ให้กับ Data model ชนิดนึง — การทำงานกับ Class ที่เป็น Data Object โดย Kotlin มันช่างง่ายนัก แต่เมื่อถึงเวลาต้อง integrate กับ Firebase ทุกอย่างที่สร้างมาแหลกสลายไปทันที 555555 — ต่อไปจะพูดถึงปัญหา และอุปสรรคที่เจอ เมื่อใช้ Kotlin2JS

dynamic

เนื่องจาก JS มันสามารถเป็น Type อะไรก็ได้ และ Libraries เสริมก็เป็น JS อีก มันเลี่ยงไม่ได้เลยที่จะต้องใช้ dynamic — เช่น express เป็น JS wrapper เพราะฉนั้นตัวมันไม่ใช่ Class kotlin จริงๆ เลยต้องให้มันกลายเป็น dynamic แทน

ความพินาศของมันคือ express จะไม่โชว์ Auto Suggest ใดๆ เลย เพราะมันไม่ใช่ Class ของ Kotlin — ทุกอย่างที่เป็น JS Module ต้องเรียกผ่าน require ทั้งหมด และมัน return dynamic กลับมาให้ — Firebase ก็เช่นกัน

แต่ถ้าในโปรเจคเราใช้ JS Library ไม่เยอะ คิดว่าจำได้ หรือเขียน Wrapper ขึ้นมาเองไหว ก็โอเคนะครับ ในโปรเจคนี้มีแค่ express และ firebase เอง ที่เหลือเป็น Logic และ Data หมด ก็เลยไปต่อ

Kotlin Object != JSObject

เมื่อเราจัดการกับ Data Object และ Logic โดย Kotlin ได้แล้ว ถัดไปเราคงอยากเขียนข้อมูลลง Firebase ถ้าเราใช้ JS เราแค่โยน JSObject ลงไปได้เลย แต่ถ้าเป็น Object ที่มาจาก Kotlin class อย่าคิดว่ามันจะแปลงกลับเป็น JSObject ให้ — ไม่เลยจ้าาาาาา เป็นสิ่งที่นั้งงมอยู่นานมากว่าเกิดอะไรขึ้น

JS
val user = {name: 'ta'}
db.ref(path).set(user)
Kotlin
val user = User(name = "ta")
db.ref(path).set(user)

จากโค้ดข้างต้น Kotlin พังจ้าาาา จากนั้นเลยลอง print log ออกมาดู ปรากฏว่า มันต่างกัน

JS
val user = {name: 'ta'}
console.log(user)
// { name: 'ta' }
Kotlin
val user = User(name = "ta")
console.log(user)
// User { name: 'ta' }

อ่าวปัญหาและ — ก็เลยต้องแปลงกับเป็น JSON object ก่อนถึงจะได้ โดยโค้ดข้างล่าง

val user = JSON.parse<User>(JSON.stringify(user))
console.log(user)
// { name: 'ta' }

จากนั้นคิดว่า Function นี้ใช้บ่อยก็เลยทำเป็น Extension ซะ — อ้าวได้ใช้ประโยชน์ของ Kotlin แล้ว เอาเป็นว่า ประเด็น Kotlin Object ก็จบไป

parsing data แต่ได้ field ที่ไม่ต้องการมาด้วย

ใน express เวลาที่ Client เรียก method post, put เราสามารถที่จะดักเอา request body เป็น JSON ได้ หลังจากนั้น parsing เป็น class ที่เราต้องการซะ เช่น

class User(id: String, name: String)JSON.parse<User>(reg.body)

ทุกอย่างจะผ่านไปได้ด้วยดี ถ้า Client ไม่ส่งค่ามาผิดๆ ถ้า Client ส่งค่ามาผิด Object User ก็จะมี Field ที่เรา Access ไม่ได้ทันทีเช่น

class User(id: String, name: String)console.log(req.body)
// {namex: 'ta', id: '123'}
val user = JSON.parse<User>(reg.body)
console.log(user)
// {namex: 'ta', id: '123'}
user.namex --> can not access

จากโค้ดจะเห็นได้ว่า user.namex ไม่สามารถเข้าถึงได้เพราะมันไม่ได้เป็น member ของ Class User แต่เวลาเรา save ลง Firebase มันกลับ Save ไปทั้งก้อน มันก็จะมี Data แปลกๆ เข้ามาด้วย เป็นเหตุผลที่ว่า ต้องสร้าง Class มา Validate Data อีก(ซึ่งก็ควรแล้ว)

เรียกใช้บาง method ของ firebase ไม่ได้

ลองมาดูคำสั่งข้างล่างกัน ว่ามันผิดปกติยังไง

snapshot.val()

ทุกอย่างดูปกติมากกกกกกก จนกระทั้งนึกได้ว่า .val() เป็น 1 ใน Function ของ Kotlin ผลคือ Compiler และ IDE เอ๋อไปเลย มันฟ้องว่า dynamic ไม่มี function .val() นะ (snapshot เป็น dynamic) แต่ยังดีที่เรามีตัวช่วยเพียงใช้ method js() แล้วส่ง โค้ดส่งไป

js("snapshot.val()")

แค่นี้เราก็สามารถที่จะเรียกบาง method ที่มีชื่อเหมือนใน Kotlin ได้แล้ว

ไม่สามารถใช้ return; เพื่อหยุด Flow ได้

function ใดๆ ก็ตามที่จะเอาไปใช้คู่กับ JS ต้อง return dynamic เสมอ นั้นเป็นผลให้ เราไม่สามารถใช้ return; เพื่อหยุดการทำงานได้เช่น

val user = JSON.parse<User?>(reg.body)if (user == null) {
showError()
return;
}
// do something

วิธีแก้เราสามาถใช้ if/else บังคับ Flow ได้

val user = JSON.parse<User?>(reg.body)if (user == null) {
showError()
} else {
// do something
}

แต่บางครั้งเงื่อนไขอาจจะเยอะ การที่ต้อง if แล้ว else ทุกครั้งอาจทำให้อ่ายโค้ดยากได้ ถ้ามัน Break ด้วย Kotlin ไม่ได้ Break ด้วย JS สิ

val user = JSON.parse<User?>(reg.body)if (user == null) {
showError()
js("return;")
}
// do something

Error อ่านยากมาก และ Debug ยาก

ตอนเราเขียน เราเขียนเป็น Kotlin แต่เวลา Compile มันกลายเป็น JS และโค้ด JS มันพังมากกกก

เวลาที่มี error ต้องอ่านโค้ดเยอะหน่อย แต่ถ้าเราทำไปเรื่อยๆ เราจะเริ่มจับทาง Error ออก ผมใช้เวลา 1 สัปห์ดา ตอนนี้เริ่มชินกับ Error และแก้ปัญหาได้เร็วขึ้นเรื่อยๆ แล้ว

ต้องใช้ JavaScript เป็น

จากตัวอย่างก่อนหน้าจะเป็น Code ประมาณนี้

js("code")

นั้นหมายความว่า เราสามารถเรียก JavaScript ได้โดยตรงจากคำสั่งนั้นเลย นั้นหมายความว่าเราต้องรู้จัก JavaScript ด้วย

ปัญหาข้างต้น เป็นปัญหาใหญ่ๆ ของ Firebase x Kotlin ที่เจอใน 1 สัปดาห์ที่ผ่านมา แต่ปัญหายากๆ ก็เจอน้อยลงเรื่อยๆ จากนั้นผมก็เขียน Class ต่างๆ มารับมือกับปัญหาดังกล่าว ออกแบบ Structure ให้รองรับกับ Business Logic และปัญหาที่ว่า — จากนั้นก็ดูเหมือนจะเขียนโค้ดได้ไหลลื่น และไหวกว่าเดิม

StackOverFlow

ถ้ามีปัญหาใดๆ เกิดขึ้น อย่าหวังพึ่ง StackOverFlow แบบ 100% เลย เราหาได้เป็นส่วนๆ ไป เช่นหาแค่เรื่องของ Kotlin หรือหาแค่เรื่องของ Firebase — เราจะหา 2 keyword นี้พร้อมกัน แล้วได้คำตอบที่ต้องการ มันยากมากพูดเลย — เพราะฉนั้นถ้าไม่พร้อมมาทางนี้ ที่ต้องสร้างทางเดินเอง ก็อย่าเพิ่งมาจะดีกว่า, แต่ถ้าพร้อมเป็นคนแรกๆ ที่เดินสายนี้จงเตรียมใจไว้ด้วย

Firebase & Kotlin

ก็เหมือนคู่รักข้าวใหม่ปลามัน ที่สามารถทำงานด้วยกันได้ดีกว่าหลายๆ คู่ที่ผ่านมา แต่ทุกคู่ก็ต้องให้เวลาเรียนรู้ และปรับความเข้าใจกัน มันไม่มีใครสมบูรณ์แบบ ต่างคนต่างต้องปรับเข้าหากัน เพื่อที่จะเดินไปบนทางนี้แบบยาวๆ ได้ — การสร้างเส้นทางเดินเอง มันทั้งเสี่ยงและอันตราย เพราะฉนั้นต้องเตรียมใจดีๆ

สรุป

ทั้งหมดที่เขียนมาเป็นปัญหาที่เจอ และวิธีแก้ปัญหาโดยใช้ Kotlin และ Firebase อย่างจริงๆ จังๆ ใน 1 สัปดาห์ — แต่ว่าผมใช้เวลาศึกษาทั้ง Kotlin และ Firebase มาก่อนเป็นเดือนๆ ก่อนที่จะมั้นใจพอที่จะเอามันมาเจอกัน — บทความนี้ไม่ได้ต้องการให้ใครมาใช้ Kotlin + Firebase แต่ถ้าใครอยากลองก็ยินดีจ้า และถ้าหากใครเดินทางสายเดียวกันอยู่ มาแชร์ แลกเปลี่ยนกันได้นาจาาาาาาา

Thank you

— end of the story —

--

--