แอป 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 ในฐานข้อมูล หรือให้เครื่องมือแอดมินเพิ่มเองก็ได้
ฝั่งแอป
- ตอนเปิดแอป อ่านข้อมูลจากที่เก็บในเครื่องมาแสดงทันที (แสดงได้แม้ไม่มีเน็ต)
- ถ้ามีเน็ต ขอแค่ค่า
data_versionจากเซิร์ฟเวอร์ — ข้อมูลไม่กี่สิบไบต์ - ถ้าเลขตรงกับที่เก็บไว้ ไม่ต้องทำอะไร
- ถ้าไม่ตรง ค่อยโหลดข้อมูลทั้งชุดใหม่ แล้วบันทึกทับพร้อมเลขเวอร์ชันใหม่
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 ที่ผู้ใช้เห็น
- ข้อมูลฉุกเฉินฝังมากับแอป