Flutter Demo by KBTG: วิธีใช้ App Clips

Amorn Apichattanakul
KBTG Life
Published in
6 min readJul 7, 2021

ชาว Swift สามารถใช้บทความนี้ได้เช่นกันนะครับ โดยข้ามส่วนที่เป็น Flutter เพราะไม่ต้องมีส่งข้อมูลกลับมา

App Clips เป็นหนึ่งฟีเจอร์ใน iOS 14 ที่ให้ผู้ใช้งานได้ทดลองเล่นแอปของคุณก่อนใช้งานจริง โดยเริ่มจากการสแกนตัว QR Code (ต้อง QR Code พิเศษของ Apple เท่านั้น) หรือแตะที่ NFC หรือเปิดผ่านเว็บไชต์ เมื่อทำสำเร็จจะมีภาพเลื่อนจากด้านล่างของจอออกมาพร้อมกับคำบรรยายสำหรับ App Clips

ทาง Flutter เองได้ทำ App Clips ขึ้นมา แต่มีข้อจำกัดว่าตัว App Clips ต้องการขนาดที่เล็กกว่า 10 MB ซึ่งตอนนี้ทีม Flutter ยังคงติดปัญหาในการลดขนาดตามที่ iOS ต้องการอยู่

ในบทความนี้เราจึงจะมาเขียน App Clips ด้วย Native และเชื่อมต่อกับ Flutter กันครับ 😄 ผมไม่ได้จะบอกว่าวิธีผมดีกว่าของทีม Flutter เพราะแบบของผมจะต้องเขียน 2 ครั้งใน Flutter และ Swift ในขณะที่ทีม Flutter ต้องการให้เขียนใน Flutter และเฉพาะบางส่วนที่จะแชร์ไปให้ App Clips ใช้ ไม่ว่าจะเป็นเรื่อง UI หรือระบบฟังก์ชันการทำงาน แต่ถ้าโปรเจคของคุณต้องการด่วนจริงๆ รอ Flutter ไม่ได้ ก็สามารถใช้วิธีแบบผมได้ครับ โดยเริ่มจาก…

flutterd doctor

เพื่อเช็คข้อมูล Flutter ของเครื่องผม ซึ่งผมจะใช้ Flutter 2.2.2 ส่วนทำไมต้องคำสั่ง Flutterd? สามารถคลิกบทความด้านล่างเพื่อศึกษาวิธีการทำให้เครื่องคุณมี Flutter ทั้ง 2 เวอร์ชันได้ครับ

นี่เป็นข้อมูลในเครื่องผมครับ

เมื่อพร้อมแล้ว มาเริ่มสร้างโปรเจคกับ Flutter 2.2.2 ได้เลย

flutterd create appclip

หลังจากที่สร้างเสร็จแล้ว ไปที่ Xcode Workspace แล้วเปิด Runner.xcworkspace จะได้หน้าตาคล้ายๆ แบบนี้

มาใส่ App Clips กัน โดยไปที่ Xcode > File > New > Target

เลือก iOS > App Clip

ใส่ Product Name และเลือก UIKit App Delegate สำหรับ Life Cycle

หลังจากนั้นจะมีโฟลเดอร์ Appclip ใน Xcode

ใส่ Layout SwiftUI นี้ใน ContentView.swift

จากนั้นใส่ AppClipViewModel แบบโล่งๆ ไว้ก่อน เราจะนำมาใช้ทีหลังครับ

ลองเทสจาก Simulator ก่อนก็ได้ครับ เพื่อดูว่าทุกอย่างทำงานได้ปกติไหม ผลลัพธ์จะได้ออกมาตามภาพด้านล่าง

ข้อมูลจาก App Clips จะยังไม่มานะครับ เพราะเรายังไม่ได้เริ่มทำอะไร แค่วาง Layout เฉยๆ ขั้นตอนต่อไปเราจะดึงข้อมูลมาจาก App Clips

App Clips นั้นมีรูปแบบพิเศษที่มาจากทาง Apple เท่านั้น ไม่สามารถใช้ QR Code ปกติได้ วิธีการทำ App Clips เราจะใช้คำสั่ง AppClipGenator ซึ่งสามารถดาวน์โหลดได้ที่ลิงค์ด้านล่างนี้

เมื่อติดตั้งเรียบร้อยแล้ว ก็จะสามารถใช้ Command Line ตามด้านล่างได้

AppClipCodeGenerator generate --url https://clip.amorn.com/p?id=4141z2k --index 9 --output /Users/amornapichattanakul/Desktop/temp/clip-amorn.svg

หรือจะลองใช้คำสั่ง AppClipCodeGenerator เพื่อตรวจสอบว่ารับค่าใดๆ ได้บ้างก็ได้นะครับ ตอนผมลองรันดู ได้ผลลัพธ์ออกมาตามนี้

ถ้าให้อธิบายวิธีการทำงานของคำสั่งด้านบน App Clips จะทำ URL https://clip.amorn.com/p?id=4141z2k พร้อมกับ Index 9 และ Color Template ของ App Clips จากนั้นบันทึกที่ Temp Folder บนคอมพิวเตอร์ของผมและใช้ชื่อว่า clip-amorn.svg. สามารถเปลี่ยนค่าได้ตามที่ต้องการครับ

ทั้งนี้ปัญหาที่พบคือ URL นั้นจะยาวมากไม่ได้ จากที่ผมลองผิดลองถูกแล้ว เหมือน Apple จะอนุญาตได้ประมาณ 10 ตัวอักษรเท่านั้นเอง ลองดูได้ครับ ถ้าเกินที่กำหนด Apple จะมีการแจ้งว่า Parameter นั้นยาวเกินไป

หลังจากลองทำแล้วเรียบร้อย ผมก็จะได้ App Clips แบบด้านล่างครับ เราก็รีบเปิดแอปกล้อง แล้วสแกนกันเลย!!

ปรากฏว่าไม่รู้จักครับ…

เอาน่ะ อย่างน้อยมันก็รู้จัก App Clips ตัวนี้และกำลังทำงานอยู่ แม้ว่าจะยังทำงานไม่ได้ก็ตาม สาเหตุเป็นเพราะยังอยู่ในวงเทสครับ จึงยังไม่รู้จัก ดังนั้นเราจะต้องติดตั้งเพิ่มเติมบนเครื่องจริงของเราก่อนเพื่อที่จะเทสได้

เริ่มจากการเพิ่ม App Clips ใน Developer Apple ในส่วนของ Certificate และ Bundle ID ซะก่อน และอย่าลืมกดเช็คที่ App Groups ด้วยนะครับ เดี๋ยวเราจะนำมาใช้

ในส่วนนี้เราจะต้องสร้างทั้งแอปธรรมดาและ App Clips นะครับ เพื่อที่จะดาวน์โหลด Certificate จาก Apple มาลงที่เครื่องและทำการเทสได้ จากนั้นเราจะทำการตั้งค่า Local Experience ในเครื่องของคุณกัน ต้องลงทะเบียน App Groups ด้วยนะครับ ของผมจะใช้ group.com.amorn

วิธีติดตั้ง App Clips ในเครื่อง iOS

ไปที่ Settings > Developer > APP CLIPS TESTING > Register Local Experience

  • URL PREFIX: https://clip.amorn.com (ใช้ URL เดียวกันกับที่สร้างไว้ใน AppClipGenerator)
  • BUNDLE ID: com.theamorn.appclip.Clip (ต้องเป็นอันเดียวกับ Bundle ใน Xcode)
  • Title: Flutter
  • Subtitle: App Clip
  • Action: Open (ทาง Apple ให้มา 3 แบบ ซึ่งจะเป็นแค่ชื่อปุ่มครับ ดังนั้นไม่ต้องกังวล เลือกอันไหนก็ได้เลยครับ)
  • Choose Photo: เลือกรูปอะไรก็ได้ที่ต้องการ

ห้ามลืม! รัน Xcode ให้ App และ App Clips ให้อยู่ในเครื่องก่อนนะครับ หลังจากกดบันทึก ก็เริ่มสแกนได้เลย ซึ่งก็จะทำงานได้… เอ๊ะ? ทำไมทำงานไม่ได้ล่ะ ยังขึ้นแบบเดิมอยู่เลย!?!

ปัญหาคือ App Clips จะใช้เวลาสักพักใหญ่ๆ ก่อนที่จะเก็บ Cache ไว้ในเครื่อง ซึ่งในบอร์ดสอบถามของ Apple ทุกคนก็พบปัญหาเหมือนกัน บางคนบอกว่าทิ้งไว้สัก 1-2 ชั่วโมงก็จะทำงานได้เองครับ ไม่ต้องไปยุ่งอะไรกับมัน

เอาจริงดิ APPLEEEEE

บางคนก็บอกว่าทำตามวิธีนี้แล้วเขาได้ App Clips เร็วขึ้น คือไปที่เครื่องมือถือ แล้วเลือก Settings > Developer > Clear Experience Cache

ผมลองแล้ว 2–3 ที ตอนแรกก็ยังไม่ได้นะ ลองไปอีก 2–3 ที อ้าวทำงานได้แล้ว โค้ดเดิมเราไม่ได้เปลี่ยนใดๆ เลย?!! แต่ผมลองมาแล้ว ซึ่งทำงานได้จริงๆ ถ้าทำตามนี้ ช่วงแรกที่ลองอาจจะสแกนไม่เจอ แต่ลองทำซ้ำๆ ดูก็จะได้ผลลัพธ์ตามด้านล่างครับ

สำหรับ Simulator ไม่สามารถเทส App Clips ได้นะครับ ต้องมีเครื่องจริงเท่านั้น

สแกน QR Code สำหรับ App Clips ได้แล้ว ขั้นตอนต่อไปเราจะส่งข้อมูลจาก App Clips เข้ามาแทนคำว่า N/A ที่เราเตรียมไว้

จาก URL อันนี้ https://clip.amorn.com/p?id=4141z2k เราจะส่งข้อมูลจากพารามิเตอร์ที่ชื่อว่า ID และค่า 4141z2k ไปที่ App Clips โดยไปที่ SceneDelegate.swift ในโปรเจค และใส่คำสั่งตามด้านล่าง ซึ่งคำสั่งนี้จะดึงค่าจาก App Clips มาใส่ในแอปเรา

เราจะดึงค่า ID ออกมา แล้วนำไปใส่ใน viewModel ที่เราเตรียมไว้และลองรัน เมื่อสแกนอีกครั้งจะได้ผลลัพธ์ตามนี้

ซึ่งเราสามารถนำค่านี้ไปดึงข้อมูลหรือทำงานใดๆ ต่อตามที่ต้องการได้ครับ และถ้าผู้ใช้งานได้ลอง Demo และรู้สึกชอบขึ้นมา ก็สามารถพาไปดาวน์โหลดแอปตัวเต็มได้ ซึ่ง iOS 14 จะมีคำสั่ง SKOverlay อยู่ ตัวอย่างตามด้านล่างครับ

ตอนนี้เราสแกน App Clips ดึงข้อมูล นำข้อมูลมาแสดง และพาผู้ใช้งานไปดาวน์โหลดแอปได้แล้ว ถัดไปเราจะส่งข้อมูลที่ผู้ใช้งานเล่นจาก App Clips ไปที่แอปตัวเต็ม

ทำไมต้องส่งไปด้วย? เพราะเราอยากให้การใช้งานมีความลื่นไหลมากที่สุด ไม่ว่าผู้ใช้งานจะทำอะไรค้างไว้ในตัว App Clips เราก็อยากให้มาทำงานต่อในแอปตัวเต็มได้โดยไม่ติดขัดใดๆ เช่น การลงทะเบียน หรือหากเข้าระบบใน App Clips ไปแล้ว พอไปดาวน์โหลดตัวเต็ม ลูกค้าก็จะสามารถเข้าแอปได้เลย ไม่ต้องลงทะเบียนซ้ำ สำหรับกระบวนการนี้ เราจะใช้ฟีเจอร์ของ iOS ที่เรียกว่า App Groups

หลักๆ คือเราสามารถส่งข้อมูลผ่าน App Groups ไปยังกลุ่มของแอปที่คุณได้ลงทะเบียนไว้ จะกี่แอปก็ได้นะครับ (ถึงเรียกว่า Groups) เริ่มจากการ Add Groups

ไปที่ Xcode > Targets > Signing & Capabilities > + Capability > App Groups

จำตอนแรกที่ผมบอกได้ใช่ไหมครับว่าตอนลงทะเบียนแอปตอนแรก ให้เพิ่มตอนที่ทำ AppID ซึ่งเราจะมาพูดถึงในตอนนี้ครับ

ผมเพิ่ม group.com.amorn ในส่วนของ App Groups ถ้าเราทำถูกต้องและเรียบร้อย Xcode จะไม่แจ้งเตือน หรือมี Error ใดๆ ดังรูปด้านล่าง

เมื่อเราได้ข้อมูลจาก App Clips แล้ว เราจะนำมาบันทึกลง App Groups แอปตัวไหนที่จะนำข้อมูลนี้ไปใช้ก็สามารถดึงข้อมูลออกมาได้ แต่ต้องอยู่ในกลุ่ม App Groups group.com.amorn เท่านั้นนะครับ

เราจะบันทึกลง UserDefault และใช้ suiteName ตามที่ลงทะเบียนไว้ โดยเราจะไปเพิ่มโค้ดใน viewModel ที่ setProfile ตามด้านล่าง

หลังจากที่เราบันทึกไปแล้ว แอปอื่นๆ ก็สามารถดึงข้อมูลจาก App Groups ได้โดยใช้ suiteName ให้ตรงกัน จะเห็นว่าใน Xcode ของผมที่ Console ขวาล่างสุดของรูปจะแสดงค่า 4141z2k ที่ผมไฮไลท์ไว้ครับ แสดงว่าข้อมูลมาถูกต้องเรียบร้อย

ตอนนี้ App Clips ก็สามารถส่งข้อมูลมาที่แอปตัวเต็มได้แล้ว ได้เวลาไปที่ Flutter กันซะที!!!

สำหรับชาว Swift ก็จะเรียบร้อยแล้วครับ ต่อไปจะเป็นส่วนที่ส่งข้อมูลจาก Native ไปที่ Flutter

เราจะทำการส่งข้อมูลไปที่ Flutter ด้วย Flutter Method Channel ที่ชื่อว่า com.amorn.appclip ชื่อจะไม่เกี่ยวข้องกับของเก่าๆ ใดที่เราทำไปเลยนะครับ จะตั้งเป็นชื่ออะไรก็ได้ เพราะอันนี้เป็นท่อเชื่อมระหว่าง Flutter กับ Native ทั้งนี้จะต้องเป็นชื่อเดียวกันระหว่าง Dart กับ AppDelegate และใช้ Method ที่ชื่อว่า getDataFromAppDelegate ซึ่งก็ต้องตรงกับใน Dart เช่นกัน

อันนี้เป็นตัวอย่างใน Dart ครับ เรามี MethodChannel ที่ชื่อ com.amorn.appclip และ invokeMethod ที่ชื่อว่า getDataFromAppDelegate นำมาใส่ในตัวแปร ID

ในขณะเดียวกัน ให้ใส่ Method Channel ลงไปใน Native ด้วยที่ AppDelegate.swift

จะเห็นว่าเราใช้ com.amorn.appclip และ getDataFromAppDelegate เหมือนกับ Dart จะต้องตรงกันทุกคำนะครับ ไม่งั้นจะทำงานไม่ได้

สุดท้ายแล้วครับ เราจะกลับไปที่ SceneDelegate.swift ในโฟลเดอร์ Appclip และไปเพิ่มคำสั่งที่อยู่ในฟังก์ชันนี้

func scene(_ scene: UIScene, continue userActivity: NSUserActivity)

ไปที่ฟังก์ชันนี้ด้วย

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)

เพราะถ้าคุณมี App Clips อยู่แล้ว ฟังก์ชันแรกจะไม่ได้ถูกเรียกทุกครั้ง ทำให้การสแกนบางครั้งข้อมูลจะไม่เข้ามา แต่ฟังก์ชันที่ 2 จะถูกเรียกเสมอ

ถึงตรงนี้ก็เรียบร้อยหมดแล้วละครับกับ Flutter กับ App Clips และผมก็ได้มี Demo ให้มาลองเล่นกันที่นี่เลยครับ

ด้านล่างเป็น VDO ที่ทำจาก Demo ครับ

สำหรับชาว Flutter ผมยังรู้สึกว่าวิธีแบบนี้ทำให้เราดึงประสิทธิภาพของ Flutter ได้ไม่เต็มที่ เพราะท้ายสุดแล้วเราไม่ได้ทำการแชร์โค้ดจาก Flutter มาที่ App Clips จริงๆ สมมตเราจะต้องมีการแชร์ Layout หรือแชร์ Logic ในการเรียกข้อมูลจาก Flutter ก็จะทำไม่ได้ เราจะต้องมาเขียนกันใหม่อีกครั้งหนึ่ง ซึ่งจะไม่เหมือนกับ Demo ของ Apple Watch ที่สุดท้ายผมส่งข้อมูลไป Process ที่ Flutter และส่งกลับมาที่ Apple Watch ได้ เดิมทีผมตั้งใจจะทำวิธีนั้นกับ App Clips ด้วยแต่ยังหาทางไม่เจอ ถ้าใครพอรู้รายละเอียด สามารถแนะนำได้นะครับ และผมจะทำการแก้ไขบทความนี้อีกครั้ง

แต่สำหรับชาว Swift นั้นสามารถแชร์โค้ดมาที่ App Clips ได้เลย เพียงแค่ต้องแก้ไขบางส่วนที่ Swift เท่านั้นเพื่อให้ App Clips สามารถทำงานได้ด้วยตัวเองโดยไม่ต้องพึ่งแอปตัวเต็มใดๆ ครับ 🕺

สำหรับชาวเทคคนไหนที่สนใจเรื่องราวดีๆแบบนี้ หรืออยากเรียนรู้เกี่ยวกับ Product ใหม่ๆ ของ KBTG สามารถติดตามรายละเอียดกันได้ที่เว็บไซต์ www.kbtg.tech

--

--

Amorn Apichattanakul
KBTG Life

Google Developer Expert for Flutter & Dart | Senior Flutter/iOS Software Engineer @ KBTG