BotKit
บทความ › เขียนโค้ด & ดูแลระบบ

แอป Flutter + Supabase ใช้ออฟไลน์ได้ และประหยัดโควตาด้วยการเช็คเลขเวอร์ชันข้อมูล

ผมกำลังทำแอปท่องเที่ยวและเดินป่าในเชียงใหม่ด้วย Flutter + Supabase ข้อมูลหลักคือรายการสถานที่ เบอร์ฉุกเฉิน และข้อมูลเส้นทาง ซึ่งนานๆ จะเปลี่ยนที แต่ผู้ใช้ต้องเปิดดูได้แม้อยู่บนดอยที่ไม่มีสัญญาณ บทความนี้เล่าวิธีทำให้แอปใช้งานออฟไลน์ได้ และประหยัดโควตาแผนฟรีด้วยเทคนิคเช็คเวอร์ชันข้อมูล

ปัญหาของการโหลดใหม่ทุกครั้ง

ถ้าแอปดึงรายการสถานที่ทั้งหมดทุกครั้งที่เปิด ผู้ใช้หนึ่งคนเปิดวันละหลายครั้ง คูณจำนวนผู้ใช้ ปริมาณข้อมูลที่ส่งออกจากฐานข้อมูลจะโตเร็วมาก ทั้งที่ข้อมูลส่วนใหญ่ไม่ได้เปลี่ยนเลย และถ้าเปิดแอปตอนไม่มีสัญญาณ ก็ไม่เห็นอะไรเลย

ทางแก้: เก็บสำเนาในเครื่อง + เช็คเลขเวอร์ชัน

ทำตารางเล็กๆ เก็บเลขเวอร์ชันของข้อมูลทั้งชุด

create table app_config (
  key   text primary key,
  value text not null
);
insert into app_config values ('data_version', '1');

ทุกครั้งที่แก้ข้อมูลสถานที่ (เพิ่ม ลบ แก้) ให้เพิ่มเลข data_version ขึ้นหนึ่ง จะทำด้วย trigger ในฐานข้อมูล หรือให้เครื่องมือแอดมินเพิ่มเองก็ได้

ฝั่งแอป

  1. ตอนเปิดแอป อ่านข้อมูลจากที่เก็บในเครื่องมาแสดงทันที (แสดงได้แม้ไม่มีเน็ต)
  2. ถ้ามีเน็ต ขอแค่ค่า data_version จากเซิร์ฟเวอร์ — ข้อมูลไม่กี่สิบไบต์
  3. ถ้าเลขตรงกับที่เก็บไว้ ไม่ต้องทำอะไร
  4. ถ้าไม่ตรง ค่อยโหลดข้อมูลทั้งชุดใหม่ แล้วบันทึกทับพร้อมเลขเวอร์ชันใหม่
Future<void> syncPlaces() async {
  final local = await LocalStore.load();            // ข้อมูล + เวอร์ชันที่เก็บไว้
  show(local.places);                               // แสดงก่อนเสมอ

  try {
    final row = await supabase.from('app_config')
        .select('value').eq('key', 'data_version').single();
    final remote = row['value'] as String;
    if (remote == local.version) return;            // ไม่เปลี่ยน จบ

    final places = await supabase.from('places').select()
        .eq('is_published', true);
    await LocalStore.save(places, remote);
    show(places);
  } catch (_) {
    // ไม่มีเน็ตหรือเซิร์ฟเวอร์ล่ม: ใช้ข้อมูลในเครื่องต่อไป ไม่ต้องเด้ง error
  }
}

ผลคือการเปิดแอปส่วนใหญ่ใช้ข้อมูลแค่การเช็คเลขเวอร์ชัน แผนฟรีของ Supabase จึงรองรับผู้ใช้ได้มากขึ้นมาก

ข้อมูลฉุกเฉินต้องออฟไลน์ 100%

เบอร์ฉุกเฉินและวิธีติดต่อเมื่อไม่มีสัญญาณเป็นข้อมูลที่ต้องใช้ตอนไม่มีเน็ตพอดี ผมจึงฝังชุดข้อมูลตั้งต้นไว้ในแอปตั้งแต่ติดตั้ง (asset) ไม่ต้องรอซิงก์ครั้งแรก ถ้ามีเน็ตเมื่อไหร่ค่อยอัปเดตทับ

เรื่องความปลอดภัยของ key ในแอป

  • แอปมือถือใช้ได้แค่ anon key และต้องเปิด RLS ทุกตาราง (ดู Supabase RLS เบื้องต้น)
  • ห้ามใส่ service_role key ในแอปเด็ดขาด ไฟล์แอปถูกแกะได้
  • key สำหรับรันบนเครื่องพัฒนา เก็บในไฟล์ที่อยู่ใน .gitignore ไม่ฝังลงไฟล์ที่ commit

แผนที่: เลือก API ให้ถูกตัว

ถ้าแอปเป็นแอปมือถือแท้ (Flutter บน Android/iOS) ใช้ Maps SDK ของแพลตฟอร์มมือถือ ซึ่งเงื่อนไขค่าใช้จ่ายต่างจาก Maps JavaScript API ที่ใช้บนเว็บมาก ตรวจหน้าราคาของ Google Maps Platform ให้ตรงกับ SDK ที่ใช้จริงก่อนออกแบบ และตั้งข้อจำกัดของ API key ให้ใช้ได้เฉพาะแอปของเรา

สรุป

  • ข้อมูลที่เปลี่ยนนานๆ ครั้ง: เก็บในเครื่อง + เช็คเลขเวอร์ชัน
  • แสดงข้อมูลในเครื่องก่อนเสมอ แล้วค่อยซิงก์เบื้องหลัง
  • ไม่มีเน็ตต้องไม่ใช่ error ที่ผู้ใช้เห็น
  • ข้อมูลฉุกเฉินฝังมากับแอป
เกี่ยวกับผู้เขียน — Jark นักพัฒนา LINE Bot และระบบ AI อัตโนมัติ อยู่เชียงใหม่ ทำระบบตอบแชทและจองคิวให้ร้านค้าจริง บทความในเว็บนี้มาจากงานที่ลงมือทำเอง อ่านเพิ่ม