เครื่องเล่นเสียงที่ฉันสร้างขึ้นเอง
(nexo.sh)- แม้ในปี 2025 ก็ยังมีข้อจำกัดมากในการ เล่นเพลง MP3 บน iPhone ได้อย่างอิสระ
- แอปของ Apple และแอปจากผู้พัฒนาภายนอก ส่วนใหญ่เป็นบริการแบบเสียเงินหรือใช้งานไม่สะดวกนัก
- แอปที่สร้างเองมี การค้นหาแบบข้อความเต็ม, รองรับ iCloud และ สภาพแวดล้อมที่เน้นการทำงานแบบ local ก่อน
- แนวทางแบบ cross-platform อย่าง React Native มีข้อจำกัดจากระบบไฟล์และปัญหาด้านเสถียรภาพ
- ด้วย SwiftUI และสถาปัตยกรรมบน SQLite จึงสร้างประสบการณ์ จัดการเพลงที่เป็นอิสระและขยายต่อได้สูง
ภาพรวม
บทความนี้แนะนำทั้งกระบวนการและผลลัพธ์ของการที่ผู้พัฒนาสร้างแอปเครื่องเล่นเพลงขึ้นมาเอง เพื่อแก้ปัญหาความ ไม่สะดวก ที่แม้แต่ในปี 2025 ผู้ใช้ก็ยังเล่นไฟล์ MP3 ที่ตนเป็นเจ้าของบน iPhone ได้อย่างอิสระยากอยู่ดี ขณะที่ทั้งบริการเพลงของ Apple และแอปภายนอกต่างก็มีข้อจำกัดและโมเดลเก็บเงินหลายแบบ แอปที่สร้างขึ้นเองกลับมอบ ประสบการณ์ที่เหมาะกับการจัดการและเล่นไฟล์เพลงของผู้ใช้โดยเฉพาะ
เหตุผลที่สร้างเครื่องเล่นเพลงขึ้นมาเอง
- ฟังก์ชันซิงก์บนคลาวด์อย่าง Apple Music และ iCloud Music Library จะใช้งานได้ก็ต่อเมื่อสมัคร บริการแบบเสียเงิน
- หากยกเลิกการสมัคร การซิงก์อัตโนมัติและการเข้าถึงโฟลเดอร์เพลง จะถูกปิดกั้นทั้งหมด
- ทำให้รู้สึกได้ถึงการขาด สิทธิ์ความเป็นเจ้าของ ในการผสานคลังเพลงเดิมและใช้งานข้ามอุปกรณ์ทั่วไป
- ต้องการทำให้เกิด การตัดสินใจด้วยตนเอง ตามแนวคิดว่า “เมื่อซื้อสมาร์ตโฟนมาแล้ว ฟังก์ชันที่จำเป็นจริง ๆ ก็ควรสร้างใช้เองได้”
วิเคราะห์โซลูชันเล่นเพลงของ Apple และผู้ให้บริการรายอื่น
แอปพื้นฐานของ Apple
- แม้จะใช้แอป Files เล่นไฟล์เพลงจากโฟลเดอร์ iCloud ได้ แต่ฟังก์ชันพื้นฐานอย่าง การจัดการเพลย์ลิสต์ การเรียงข้อมูลเมตา และการจัดการคิว ยังอ่อนมาก
- มอบ ประสบการณ์ผู้ใช้ที่ไม่ได้ออกแบบมาเพื่อการฟังเพลงโดยเฉพาะ
แอปจากผู้พัฒนาภายนอก
- ใน App Store มีแอปภายนอกหลากหลาย แต่จำนวนมากใช้ โมเดลเก็บเงินแบบสมัครสมาชิก และความสามารถของแต่ละแอปก็แตกต่างกันมาก
- แม้จะมีแอปเสียเงินแบบซื้อขาดบางตัว เช่น Doppler แต่ ประสบการณ์การค้นหาและนำเข้าข้อมูล จากโฟลเดอร์ iCloud ขนาดใหญ่ก็ยังไม่ลื่นไหลนัก
เส้นทางทางเทคนิคสู่ “โหมดผู้สร้าง”
- ตัดสินใจว่าต้องสร้างแอปเครื่องเล่นเพลงขึ้นมาเอง
- ฟังก์ชันที่ต้องการ: การค้นหาแบบข้อความเต็มที่รวดเร็วทั่วทั้งโฟลเดอร์ iCloud, ความสามารถจัดการเพลง ระดับแอปเพลงทางการ (คิว เพลย์ลิสต์ การเรียงลำดับ ฯลฯ) และ อินเทอร์เฟซที่เข้าใจง่าย
ความพยายามครั้งแรกด้วย React Native
- เนื่องจากเคยรู้สึกไม่สะดวกกับการใช้ Swift มาก่อน (เช่น ในอดีตยังไม่รองรับ async/await) จึงเอนเอียงไปทาง React Native/Expo
- การทำ UI ค่อนข้างราบรื่นด้วยการใช้เทมเพลตโอเพนซอร์ส แต่เมื่อจัดการ การเข้าถึงระบบไฟล์ (โฟลเดอร์ iCloud) และการซิงก์ ก็พบทั้งแอปแครชและข้อจำกัดด้านฟังก์ชัน
- เมื่อเข้าใจว่านโยบาย sandbox ของ iOS ไม่อนุญาตให้เข้าถึงโฟลเดอร์ภายนอกโดยไม่มีสิทธิ์อย่างชัดเจน จึง เปลี่ยนไปใช้ Swift
การเลือก SwiftUI และเหตุผล
- ใช้ประโยชน์จาก กระบวนทัศน์ UI แบบ declarative ของ SwiftUI รวมถึง async/await และ Swift Actor แบบสมัยใหม่
- แยก UI ออกจากตรรกะข้อมูลอย่างเด็ดขาด เพื่อสร้าง data flow ที่สะอาดและการจัดการ concurrency ในระดับโดเมน
- ยังช่วยให้ใช้ LLM (เช่น OpenAI o1, DeepSeek) ได้อย่างเหมาะสม และลดการพึ่งพาของโค้ด UI ที่สร้างขึ้น
สถาปัตยกรรมแอปและโมเดลข้อมูล
-
ใช้ SQLite เป็นที่เก็บข้อมูล และเลือกแทน CoreData เพราะสามารถใช้ การค้นหาแบบข้อความเต็ม (FTS5) ได้ทันที
-
แอปมี 3 หน้าจอ/โหมดหลัก:
- นำเข้าคลังเพลง: บันทึกพาธไฟล์เสียงจากแต่ละโฟลเดอร์ iCloud ลง DB แบบชุดเดียว เพื่อรองรับการค้นหา/จัดการที่ยืดหยุ่น
- จัดการคลังเพลง: เพลย์ลิสต์ การจัดหมวดหมู่เพลง การแก้ไข ฯลฯ
- เล่นเพลง: จัดการคิว (เล่นซ้ำ สุ่มเพลง ฯลฯ) และการควบคุมเพลงพื้นฐาน
-
ลำดับการใช้งานของผู้ใช้เมื่อ import คลังเพลง:
- จากสถานะเริ่มต้นที่ว่าง เลือกโฟลเดอร์และสแกนโครงสร้างผ่าน "Add iCloud Source"
- เมื่อทำดัชนีเสร็จ จะย้ายไปยังแท็บไลบรารี พร้อมลิสต์ตามคีย์เวิร์ดและมินิเพลเยอร์
- เมื่อมีการเพิ่มเพลงใหม่ ระบบจะรวมเข้ามาให้อัตโนมัติในเบื้องหลัง
ชั้นตรรกะแบบสไตล์แบ็กเอนด์
- อิงจากประสบการณ์พัฒนาเว็บ/สตาร์ตอัปสายคลาวด์ จึงแยกชั้นตรรกะออกจาก UI/ViewModel อย่างชัดเจน
- ชั้นโดเมน (Swift actors) รับผิดชอบตรรกะธุรกิจหลัก (import, search, queue ฯลฯ) เพื่อให้ได้ ความปลอดภัยด้านเธรด
- การอัปเดต UI ถูกแยกอย่างเป็นระเบียบผ่าน ViewModel และลดการพึ่งพากันระหว่างการซิงก์ไฟล์ การเล่นเพลง และ UI เพื่อให้ดูแลรักษาได้ง่ายขึ้น
การทำ Full-text search ด้วย SQLite
- ตั้งแต่ iOS 11 ขึ้นไป สามารถใช้ SQLite ที่รองรับ FTS5 ได้โดยไม่ต้องตั้งค่าเพิ่ม
- รองรับ การค้นหาที่รวดเร็วโดยไม่ต้องมีดัชนีค้นหาภายนอกหรือเซิร์ฟเวอร์
- ใช้ไลบรารี SQLite.swift สำหรับ query ทั่วไป และใช้คำสั่ง SQL โดยตรงสำหรับ FTS query
โครงสร้างตาราง FTS
songs_fts: ทำดัชนี artist, title, album, albumArtist ของเพลงsource_paths_fts: ทำดัชนีพาธไฟล์และชื่อไฟล์- อยู่คู่กับตารางหลัก และใน UI ใช้สำหรับ การค้นหา (READ) เท่านั้น
- การอัปเดตดัชนีจัดการผ่าน DB transaction เพื่อ รักษาความสอดคล้องของข้อมูล
การค้นหาแบบ Fuzzy และการจัดอันดับ
- เติม wildcard อัตโนมัติ ต่อท้ายค่าที่ป้อนเข้า และจัดเรียงตาม ความเกี่ยวข้องด้วย BM25
- โดยรวมแล้วทำให้เกิดทั้ง โครงสร้างข้อมูลที่คาดเดาได้โดยไม่พึ่งพาเครือข่าย และสภาพแวดล้อมการค้นหาแบบ local ที่ทรงพลัง
ระบบไฟล์ของ iOS และการใช้ Bookmarks
- iOS ต่างจาก macOS ตรงที่ไม่รองรับ security-scoped bookmarks จึงขาดความต่อเนื่องในการเข้าถึงไฟล์ภายนอกแบบขยายสิทธิ์
- มีเพียงข้อมูลพาธที่ถูกบันทึกไว้ และเมื่อจะเข้าถึงอีกครั้งผู้ใช้ต้องอนุมัติสิทธิ์ใหม่
- วิธีแก้คือ: เมื่ออนุญาตให้เข้าถึงแล้ว ให้ คัดลอกไฟล์นั้นมาเก็บไว้ใน sandbox ของแอป
- คัดลอกอัตโนมัติในเบื้องหลังเพื่อเพิ่มความเร็วในการทำดัชนีไฟล์
- แม้หลังรีบูตเครื่องก็ยังเล่นไฟล์ภายนอกโดยตรงได้ยาก แต่สิ่งนี้สะท้อนว่า ข้อจำกัดการเข้าถึงไฟล์ของ iOS นั้นรุนแรงมาก
การเล่นเพลงด้วย AVFoundation และการออกแบบอินเทอร์เฟซ
- ใช้ เฟรมเวิร์ก AVFoundation และ AVURLAsset เพื่อวิเคราะห์ metadata ของไฟล์เสียง
- ฟิลด์บางส่วน เช่น track number ต้อง parse เองด้วยมือ (อาศัย ID3 tag)
- สำหรับการเล่นเสียงใช้ AVAudioPlayer และเพื่อเชื่อมกับ Control Center จึงมีการ implement MPRemoteCommandCenter และ Delegate protocol
สิ่งที่รู้สึกหลังพัฒนา และข้อคิดต่อแนวนโยบายของ Apple
สิ่งที่ไม่สะดวก
- สภาพแวดล้อมที่จำกัดของ Xcode: แม้ SwiftUI live preview จะพัฒนาไปมาก แต่ยังไม่สะดวกเท่า VSCode หรือ Flutter
- ความยืดหยุ่นของเอดิเตอร์ต่ำ: หากจะใช้ Swift LSP บน Neovim หรือ VSCode ต้องตั้งค่าเพิ่มและคุณภาพยังไม่สมบูรณ์นัก
- บางมุมของ SDK ยังเน้น Objective-C: เช่น งานค้นหา metadata ที่ยังขาด API สมัยใหม่ที่เป็นมิตรกับ Swift
- การดีบัก iCloud ยุ่งยาก: ไม่สามารถทำฟีเจอร์คลาวด์ให้สมบูรณ์ใน SwiftUI preview ได้ ต้องทำ mock ขึ้นมาเองโดยตรง
สิ่งที่เป็นบวก
- Async/await ทำให้การเขียนโค้ด concurrency ที่เป็น I/O-bound ง่ายขึ้นอย่างชัดเจน
- ไลบรารีเนทีฟที่หลากหลาย: หลุดพ้นจากข้อจำกัดของ open-source bindings ทำให้พัฒนาฟังก์ชันได้กว้างขึ้น
- การออกแบบ UI แบบ declarative ของ SwiftUI: สัมผัสได้ถึงข้อดีแบบ React และประสิทธิภาพการพัฒนาที่สูง
บทสรุป: การพัฒนาไม่น่าจะง่ายกว่านี้หรือ
- ใช้เวลา 1.5 สัปดาห์ก็ทำเป้าหมายของ เครื่องเล่นเพลงแบบ local/offline ได้สำเร็จ
- แต่ในความเป็นจริง แม้แต่ การแจกจ่ายตัวแอปเองก็ยังมีข้อจำกัด: หากไม่มี developer certificate จะใช้งานไม่ได้หลัง 7 วัน และต้องสมัครนักพัฒนารายปี $99
- แม้หลัง EU DMA Act ก็ยัง ไม่ได้เปิด sideloading อย่างสมบูรณ์ และข้อจำกัดยังคงอยู่สำหรับนักพัฒนารายบุคคลและสายงานอดิเรก
- PWA บน iOS ก็ยังมีข้อจำกัด เช่น API สำคัญหลายตัวไม่รองรับ (Web Bluetooth/USB/NFC, Background Sync ฯลฯ)
- แม้ AI จะลดกำแพงในการพัฒนา แต่เฉพาะ iOS กลับยังมีข้อกำหนดเชิงประดิษฐ์ที่ทำให้ต้นทุนการเริ่มต้นสูง
- ข้อจำกัดต่อสิทธิ์ของนักพัฒนาอิสระและผู้ใช้ ยังคงอยู่ และความปิดของ iOS ก็ยังเป็นอุปสรรคต่อการสร้างนวัตกรรม
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
yt-dlpดาวน์โหลดลงเซิร์ฟเวอร์แล้วสตรีมจากตรงนั้นได้ และมันจำตำแหน่งเล่นไว้เสมอ ทำให้ฟังต่อจากในรถไปยังโน้ตบุ๊กที่ทำงานได้ทันที อีกทั้งยังเพิ่มมิกซ์จากแหล่งอื่นอย่าง NTS Radio ได้ดีมากด้วย