databaseUserDefaults

การจัดเก็บข้อมูลของแอปภายใต้ Sandbox และบทบาทของ UserDefaults

การจัดเก็บข้อมูลภายในแอปพลิเคชันบนระบบปฏิบัติการ iOS เป็นประเด็นสำคัญที่เกี่ยวข้องกับทั้งสถาปัตยกรรมซอฟต์แวร์ ความปลอดภัยของข้อมูล และประสบการณ์ผู้ใช้ ระบบ iOS ได้ออกแบบกลไกพื้นฐานที่เรียกว่า Sandbox เพื่อควบคุมขอบเขตการเข้าถึงข้อมูลของแต่ละแอปอย่างเคร่งครัด โดยแต่ละแอปพลิเคชันทำงานอยู่ในพื้นที่แยก (isolated environment) ของตนเอง แอปไม่สามารถเข้าถึงไฟล์หรือข้อมูลของแอปอื่นได้โดยตรง เว้นแต่จะได้รับสิทธิ์ผ่านกลไกที่ระบบกำหนด เช่น App Groups, iCloud sharing หรือระบบ permission ต่าง ๆ

เมื่อแอปถูกติดตั้ง ระบบจะสร้างโฟลเดอร์เฉพาะสำหรับแอปนั้นโดยอัตโนมัติ โดยมีชื่อเป็น UUID แบบสุ่มเพื่อป้องกันการเข้าถึงโดยตรงจากภายนอก ภายใน Sandbox แบ่งออกเป็นส่วนสำคัญดังนี้

  • Bundle Container เก็บไฟล์โปรแกรมและทรัพยากรทั้งหมดของแอป

  • Data Container เป็นพื้นที่หลักที่แอปสามารถอ่านและเขียนข้อมูลได้

  • iCloud Container เป็น พื้นที่จัดเก็บข้อมูลแบบคลาวด์

ภายในโครงสร้างนี้ พื้นที่ที่มีความสำคัญต่อการจัดเก็บข้อมูลของแอปคือ Data Container ซึ่งเป็นตำแหน่งที่แอปสามารถอ่านและเขียนข้อมูลได้ โดยแบ่งเป็น Documents, Library และ Temporary directory ข้อมูลทั้งหมดในส่วนนี้จะปรากฏในระบบเป็น Documents & Data และจะถูกลบออกทันทีเมื่อผู้ใช้ลบแอป

UserDefaults ภายใน Sandbox

UserDefaults เป็นกลไกจัดเก็บข้อมูลแบบ key–value ที่อยู่ใน Foundation framework และทำหน้าที่เป็น persistence layer ระดับเบา (lightweight persistence) สำหรับข้อมูลที่ต้องการเข้าถึงรวดเร็ว โดยข้อมูลที่บันทึกผ่าน UserDefaults จะถูกเก็บเป็นไฟล์ property list ภายใน Library/Preferences ของ Sandbox โดยอัตโนมัติ นักพัฒนาไม่ต้องจัดการไฟล์โดยตรง ในเชิงแนวคิด UserDefaults จึงไม่ใช่ storage แยกต่างหาก แต่เป็น API ที่ช่วยจัดเก็บข้อมูลลงใน Sandbox อย่างเป็นระบบ ภายใต้ข้อจำกัดด้านความปลอดภัยของ iOS

UserDefaults เหมาะกับข้อมูลที่มีขนาดเล็ก ไม่ซับซ้อน หรืออาจจำเป็นจะต้องโหลดทันทีเมื่อแอปเปิด และที่สำคัญคือ ต้องคงอยู่แม้ปิดแอป ตัวอย่างเช่น

  • ข้อมูลโปรไฟล์พื้นฐาน เช่น ชื่อผู้ใช้ อีเมล หรือข้อความแนะนำตัว

  • ค่าต่างๆ ในการตั้งค่าพื้นฐานของแอป เช่น theme หรือ language

  • สถานะของระบบ เช่น ผู้ใช้เคย login แล้วหรือไม่

ในทางตรงกันข้าม ไม่ควรใช้ UserDefaults สำหรับข้อมูลขนาดใหญ่ เช่น รูปภาพ วิดีโอ หรือฐานข้อมูล เนื่องจากระบบจะโหลดไฟล์ทั้งหมดเข้าหน่วยความจำ ซึ่งอาจกระทบต่อประสิทธิภาพของแอป

การใช้ UserDefaults ในการเก็บ User Profile ของผู้ใช้

ในกิจกรรมนี้ผู้อ่านจะได้ทดลองพัฒนาแอป UserProfile โดยการใช้ UserDefaults เป็นเครื่องมือหลักในการจัดเก็บข้อมูลโปรไฟล์ของผู้ใช้ซึ่งเป็นข้อมูลขนาดเล็กที่ต้องการเรียกใช้ทันทีเมื่อเปิดแอป

ขั้นตอนที่ 1 สร้างไฟล์ UserProfile.swift

เริ่มต้นจากการกำหนดโครงสร้างข้อมูลของโปรไฟล์ผู้ใช้ ซึ่งประกอบด้วย ชื่อ (name) อีเมล์ (email) รายละเอียดโดยย่อ (bio) และรูปภาพของผู้ใช้ (avatarData) เพื่อเป็น Data Model กลางของระบบ และใช้การเข้ารหัส/ถอดรหัสเพื่อเก็บใน UserDefaults

ขั้นตอนที่ 2 สร้างไฟล์ UserDefaultsManager.swift

UserDefaultsManager ทำหน้าที่เป็น Persistence Layer ของแอป โดยแยกตรรกะการอ่านและเขียนข้อมูลออกจาก ViewModel เพื่อให้โครงสร้างของระบบมีความชัดเจนและลดการพึ่งพาระหว่างส่วนประกอบ ภายในคลาสรองรับการจัดการข้อมูลพื้นฐาน ได้แก่ การบันทึก (Save) การอ่าน (Load) และการล้างข้อมูล (Reset) ทั้งนี้กำหนดให้เป็น final class เพื่อสื่อว่าคลาสนี้ไม่ได้ออกแบบมาให้สืบทอดต่อ ป้องกันการ subclass และการ override เมธอดโดยไม่จำเป็น ซึ่งช่วยให้เจตนาการออกแบบมีความชัดเจนมากยิ่งขึ้นในเชิงสถาปัตยกรรม

ขั้นตอนที่ 3 สร้างไฟล์ UserProfileViewModel.swift

UserProfileViewModel ทำหน้าที่เป็นชั้น ViewModel ของแอปตามแนวคิดสถาปัตยกรรม MVVM (Model–View–ViewModel) โดยมีหน้าที่ควบคุมตรรกะการทำงานของระบบและเป็นตัวกลางระหว่างส่วนติดต่อผู้ใช้ (View) กับชั้นการจัดเก็บข้อมูล (Persistence Layer) ซึ่งในแอปนี้คือ UserDefaultsManager การแยกส่วนดังกล่าวช่วยลดการพึ่งพากันระหว่าง UI กับระบบจัดเก็บข้อมูล ทำให้โค้ดอ่านง่าย ดูแลรักษาได้สะดวก และสามารถปรับเปลี่ยนวิธีการจัดเก็บข้อมูลได้ในอนาคตโดยไม่กระทบกับส่วนแสดงผล

ภายใน ViewModel มีการประกาศตัวแปรสถานะด้วย @Published เพื่อให้ SwiftUI สามารถสังเกตการเปลี่ยนแปลงของข้อมูลและอัปเดตหน้าจอโดยอัตโนมัติ ข้อมูลที่จัดการประกอบด้วยชื่อ อีเมล ข้อความแนะนำตัว และภาพโปรไฟล์ ซึ่งสะท้อนโครงสร้างของ UserProfile นอกจากนี้ยังมีตัวแปร selectedItem สำหรับรองรับการเลือกภาพจาก Photo Library ผ่าน PhotosPicker โดยเมื่อผู้ใช้เลือกภาพ ระบบจะเรียกเมธอดภายในเพื่อโหลดข้อมูลภาพแบบ asynchronous แล้วแปลงเป็น UIImage เพื่อแสดงผลใน UI

เมื่อ ViewModel ถูกสร้างขึ้น ตัวสร้างเริ่มต้น (init) จะเรียกเมธอด loadProfile() เพื่ออ่านข้อมูลที่บันทึกไว้ใน UserDefaults และกำหนดค่าให้กับตัวแปรสถานะของ ViewModel กระบวนการนี้ช่วยให้แอปสามารถแสดงข้อมูลเดิมของผู้ใช้ได้ทันทีเมื่อเปิดใช้งาน ซึ่งสะท้อนแนวคิดของ data persistence ในแอปพลิเคชันมือถือ

ในด้านการบันทึกข้อมูล เมธอด saveProfile() ทำหน้าที่รวบรวมข้อมูลจากตัวแปรสถานะทั้งหมด สร้างเป็นอ็อบเจกต์ UserProfile และแปลงภาพโปรไฟล์เป็นข้อมูลแบบ Data ก่อนส่งไปยัง UserDefaultsManager เพื่อบันทึกลง Sandbox ของแอป การออกแบบในลักษณะนี้ทำให้ View ไม่จำเป็นต้องรู้รายละเอียดของการเข้ารหัสข้อมูลหรือการจัดเก็บไฟล์

นอกจากนี้ ViewModel ยังมีเมธอด resetProfile() สำหรับล้างข้อมูลผู้ใช้ ซึ่งจะสั่งให้ Persistence Layer ลบข้อมูลใน UserDefaults และโหลดสถานะเริ่มต้นกลับเข้าสู่ระบบ วิธีการนี้ช่วยให้วงจรชีวิตของข้อมูลในแอปครบถ้วนทั้งการสร้าง อ่าน แก้ไข และลบ

โดยสรุป UserProfileViewModel ทำหน้าที่เป็นศูนย์กลางของตรรกะการทำงานของแอป ทั้งในด้านการประสานข้อมูลระหว่าง UI กับระบบจัดเก็บ การจัดการสถานะของผู้ใช้ และการควบคุมกระบวนการโหลด บันทึก และรีเซ็ตข้อมูล การออกแบบในลักษณะนี้สะท้อนแนวคิดของ MVVM ที่มุ่งแยกความรับผิดชอบของแต่ละชั้นออกจากกันอย่างชัดเจน เพื่อเพิ่มความยืดหยุ่น ความสามารถในการทดสอบ และความง่ายต่อการพัฒนาในระยะยาว

ขั้นตอนที่ 4 สร้างไฟล์ ProfileView.swift

ProfileView ทำหน้าที่เป็นส่วนแสดงผลหลักของแอปในชั้น View ตามแนวคิดสถาปัตยกรรม MVVM โดยรับผิดชอบการนำข้อมูลโปรไฟล์ของผู้ใช้มาแสดงบนหน้าจอ และทำหน้าที่เป็นจุดเริ่มต้นของการนำทางไปยังหน้าปรับแก้ข้อมูล การออกแบบในลักษณะนี้ช่วยแยกความรับผิดชอบของส่วนติดต่อผู้ใช้ออกจากตรรกะของระบบ ทำให้โค้ดมีความชัดเจนและดูแลรักษาได้ง่าย

ภายใน View มีการสร้างอินสแตนซ์ของ UserProfileViewModel ด้วยตัวห่อหุ้ม @StateObject ซึ่งมีความสำคัญในเชิงวงจรชีวิตของข้อมูล เนื่องจาก StateObject จะสร้างและเก็บ ViewModel ไว้ตลอดอายุการใช้งานของ View ทำให้ข้อมูลโปรไฟล์ที่โหลดจากระบบจัดเก็บยังคงอยู่แม้มีการ redraw ของหน้าจอ คุณสมบัตินี้ทำให้ ViewModel ทำหน้าที่เป็นแหล่งข้อมูลหลัก (single source of truth) ของหน้าจอได้อย่างเหมาะสม

ส่วนโครงสร้างของหน้าจอถูกจัดวางภายใน NavigationStack เพื่อรองรับการนำทางแบบลำดับชั้นตามรูปแบบของแอป iOS สมัยใหม่ โดยภายในใช้ VStack เพื่อจัดวางองค์ประกอบในแนวตั้ง ประกอบด้วยภาพโปรไฟล์ ข้อมูลข้อความของผู้ใช้ และปุ่มสำหรับเข้าสู่หน้าปรับแก้ข้อมูล

การแสดงภาพโปรไฟล์ใช้เงื่อนไขตรวจสอบว่ามีภาพที่ผู้ใช้เลือกไว้หรือไม่ หากมีจะแสดงภาพจาก UIImage และจัดรูปแบบให้เป็นวงกลมเพื่อให้สอดคล้องกับรูปแบบ UI ของระบบ แต่หากยังไม่มีข้อมูลภาพ ระบบจะแสดงสัญลักษณ์มาตรฐาน person.circle ของ SF Symbols แทน ซึ่งช่วยสื่อความหมายว่าเป็นตำแหน่งของข้อมูลผู้ใช้และทำหน้าที่เป็น placeholder ของโปรไฟล์ แนวทางนี้เป็น pattern ที่พบได้ทั่วไปในแอป iOS และช่วยให้ผู้ใช้เข้าใจสถานะของข้อมูลได้ทันที

ถัดลงมาเป็นการแสดงข้อมูลข้อความ ได้แก่ ชื่อ อีเมล และข้อความแนะนำตัว โดยข้อมูลเหล่านี้ถูกผูกกับตัวแปรใน ViewModel ผ่านระบบ data binding ของ SwiftUI เมื่อค่าใน ViewModel เปลี่ยน หน้าจอจะอัปเดตโดยอัตโนมัติ กลไกนี้สะท้อนแนวคิด declarative UI ที่เป็นหัวใจของ SwiftUI

ในส่วนล่างของหน้าจอมี NavigationLink สำหรับนำผู้ใช้ไปยัง EditProfileView ซึ่งรับ ViewModel ตัวเดียวกันเป็นพารามิเตอร์ การส่ง ViewModel เดียวกันไปยังหน้าถัดไปช่วยให้ข้อมูลยังคงสอดคล้องกันทั้งสองหน้าจอ และทำให้การแก้ไขข้อมูลสามารถสะท้อนกลับมายังหน้าหลักได้ทันทีโดยไม่ต้องโหลดข้อมูลใหม่ แนวทางนี้แสดงให้เห็นการใช้ ViewModel เป็นตัวกลางในการแบ่งปันสถานะของข้อมูลภายในแอป

ขั้นตอนที่ 5 สร้างไฟล์ EditProfileView.swift

EditProfileView ทำหน้าที่เป็นส่วนติดต่อผู้ใช้สำหรับการแก้ไขข้อมูลโปรไฟล์ โดยรับผิดชอบการรับข้อมูลจากผู้ใช้ การเลือกภาพโปรไฟล์ และการสั่งบันทึกหรือรีเซ็ตข้อมูลผ่าน ViewModel การออกแบบลักษณะนี้ช่วยให้ส่วนแสดงผลทำหน้าที่เพียงจัดการปฏิสัมพันธ์กับผู้ใช้ ขณะที่ตรรกะของระบบยังคงอยู่ใน ViewModel

ภายใน View มีการรับออบเจกต์ UserProfileViewModel ผ่านตัวห่อหุ้ม @ObservedObject ซึ่งหมายความว่า View นี้ไม่ได้เป็นเจ้าของ ViewModel แต่ทำหน้าที่สังเกตการเปลี่ยนแปลงของข้อมูลเท่านั้น การออกแบบเช่นนี้เหมาะสมเนื่องจาก ViewModel ถูกสร้างจากหน้าหลัก (ProfileView) และถูกส่งต่อมายังหน้าปรับแก้เพื่อให้ทั้งสองหน้าจอใช้ข้อมูลชุดเดียวกัน หากข้อมูลถูกแก้ไขในหน้านี้ การเปลี่ยนแปลงจะสะท้อนกลับไปยังหน้าหลักโดยอัตโนมัติผ่านกลไกของ SwiftUI

โครงสร้างของหน้าจอใช้ Form ซึ่งเป็นองค์ประกอบมาตรฐานของ iOS สำหรับหน้าป้อนข้อมูล โดยแบ่งออกเป็นหลาย Section เพื่อจัดกลุ่มข้อมูลอย่างชัดเจน ส่วนแรกของฟอร์มใช้สำหรับแสดงภาพโปรไฟล์และปุ่มเลือกภาพ โดยมีการตรวจสอบว่ามีภาพอยู่แล้วหรือไม่ หากมีจะแสดงภาพที่ผู้ใช้เลือกในรูปแบบวงกลม แต่หากยังไม่มี ระบบจะแสดงสัญลักษณ์ person.circle.fill เป็น placeholder ซึ่งช่วยสื่อสถานะของข้อมูลและสอดคล้องกับรูปแบบ UI ของระบบ iOS

การเลือกภาพใช้คอมโพเนนต์ PhotosPicker จากเฟรมเวิร์ก PhotosUI ซึ่งเป็นวิธีการเลือกภาพที่รองรับ SwiftUI โดยตรง เมื่อผู้ใช้เลือกภาพ ค่าจะถูกผูกกับตัวแปร selectedItem ใน ViewModel และ ViewModel จะเป็นผู้จัดการโหลดข้อมูลภาพแบบ asynchronous และแปลงเป็น UIImage เพื่อแสดงผล การแยกขั้นตอนนี้ออกจาก View ช่วยรักษาหลัก separation of concerns และทำให้ View ไม่ต้องจัดการตรรกะของการประมวลผลไฟล์

ส่วนถัดมาเป็น Section สำหรับข้อมูลข้อความของผู้ใช้ ได้แก่ ชื่อ อีเมล และข้อความแนะนำตัว ซึ่งใช้ TextField ผูกกับตัวแปรใน ViewModel โดยตรงผ่านระบบ binding ของ SwiftUI ทำให้ข้อมูลใน UI และในโมเดลมีความสอดคล้องกันตลอดเวลา

ด้านล่างของฟอร์มมีปุ่มสำหรับบันทึกข้อมูล ซึ่งเรียกเมธอด saveProfile() ใน ViewModel เพื่อส่งข้อมูลไปยัง Persistence Layer และบันทึกลง UserDefaults ภายใน Sandbox ของแอป กระบวนการนี้สะท้อนให้เห็นการไหลของข้อมูลจาก View ไปยัง ViewModel และลงสู่ระบบจัดเก็บอย่างเป็นลำดับ

นอกจากนี้ View ยังมี Section สำหรับรีเซ็ตข้อมูลผู้ใช้ ซึ่งถือเป็นการกระทำที่มีผลกระทบต่อข้อมูลทั้งหมดของแอป จึงมีการใช้ confirmationDialog เพื่อให้ผู้ใช้ยืนยันก่อนดำเนินการ การออกแบบลักษณะนี้เป็นแนวปฏิบัติด้านประสบการณ์ผู้ใช้ของ iOS สำหรับการกระทำแบบ destructive action เมื่อผู้ใช้ยืนยัน ระบบจะเรียกเมธอด resetProfile() ใน ViewModel เพื่อสั่งลบข้อมูลจาก UserDefaults และรีเซ็ตสถานะของข้อมูลในหน่วยความจำ

บทสรุปจากการสร้าง UserProfile App

จากการพัฒนาแอป UserProfile ผู้อ่านได้เรียนรู้แนวคิดสำคัญของการพัฒนาแอป iOS ตั้งแต่ระดับสถาปัตยกรรมของระบบไปจนถึงการจัดการข้อมูลภายในแอปจริง โดยโปรเจกต์นี้ถูกออกแบบให้เป็นกรณีศึกษาที่แสดงความสัมพันธ์ระหว่างโครงสร้าง Sandbox ของระบบ iOS กลไกการจัดเก็บข้อมูลผ่าน UserDefaults และแนวคิดการออกแบบซอฟต์แวร์แบบ MVVM

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

ในระดับกลไกการจัดเก็บข้อมูล ผู้อ่านได้เรียนรู้การใช้ UserDefaults ซึ่งเป็น persistence layer แบบ lightweight สำหรับเก็บข้อมูลขนาดเล็ก โดยข้อมูลโปรไฟล์ถูกเข้ารหัสเป็น JSON แล้วบันทึกเป็น Data ลงใน Library/Preferences ภายใน Sandbox การออกแบบให้มี UserDefaultsManager เป็นตัวกลางในการจัดการการอ่าน เขียน และล้างข้อมูล ช่วยให้เห็นหลักการแยกความรับผิดชอบของระบบ และทำให้โค้ดสามารถปรับเปลี่ยนวิธีการจัดเก็บในอนาคตได้โดยไม่กระทบส่วนอื่นของแอป

ในระดับสถาปัตยกรรมซอฟต์แวร์ ผู้อ่านได้เห็นการประยุกต์ใช้แนวคิด MVVM อย่างเป็นรูปธรรม โดย Model ทำหน้าที่กำหนดโครงสร้างข้อมูลของผู้ใช้ ViewModel ทำหน้าที่ควบคุมตรรกะการทำงาน การโหลด การบันทึก และการรีเซ็ตข้อมูล ขณะที่ View ทำหน้าที่แสดงผลและรับปฏิสัมพันธ์จากผู้ใช้เท่านั้น การแยกส่วนเช่นนี้ช่วยให้โครงสร้างแอปมีความชัดเจน สามารถทดสอบได้ง่าย และรองรับการขยายระบบในอนาคต

นอกจากนี้ ผู้อ่านยังได้เรียนรู้แนวทางการพัฒนา UI ด้วย SwiftUI แบบ declarative การใช้ state binding เพื่อให้ข้อมูลในหน้าจออัปเดตอัตโนมัติ การใช้ PhotosPicker เพื่อเลือกภาพจากอุปกรณ์ และการออกแบบประสบการณ์ผู้ใช้สำหรับการบันทึกและรีเซ็ตข้อมูล ซึ่งเป็นองค์ประกอบสำคัญของแอปในโลกจริง

โดยสรุป โปรเจกต์ UserProfile ไม่ได้สอนเพียงการสร้างหน้าจอหรือการเก็บข้อมูลเท่านั้น แต่ช่วยให้ผู้อ่านเข้าใจภาพรวมของการทำงานของแอป iOS ตั้งแต่โครงสร้างของระบบปฏิบัติการ วิธีการจัดเก็บข้อมูลภายใน Sandbox ไปจนถึงแนวคิดการออกแบบสถาปัตยกรรมแบบ MVVM ซึ่งเป็นพื้นฐานสำคัญสำหรับการพัฒนาแอปที่มีความมั่นคง ขยายต่อได้ และสอดคล้องกับแนวปฏิบัติของแพลตฟอร์ม

แนวทางในการจัดกิจกรรมการเรียนรู้

  • ดาวน์โหลดเอกสารประกอบการทำกิจกรรมในห้องปฏิบัติการ ที่นี่

  • ดาวน์โหลดสไลด์สำหรับใช้ประกอบการสอน ที่นี่arrow-up-right

circle-info

รายละเอียดเพื่อการอ้างอิง ผู้เขียน ธิติ ธีระเธียร วันที่เผยแพร่ วันที่ 22 กุมภาพันธ์ 2569 วันที่ปรุงปรุงล่าสุด วันที่ 23 กุมภาพันธ์ 2569 เข้าถึงได้จาก https://ajthiti.gitbook.io/develop-in-swift/swiftdata/userdefaultsarrow-up-right เงื่อนใขในการใช้งาน This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Licensearrow-up-right.

Last updated