1 điểm bởi GN⁺ 2025-05-24 | 1 bình luận | Chia sẻ qua WhatsApp
  • Ngay cả vào năm 2025, việc phát nhạc MP3 tự sở hữu một cách tự do trên iPhone vẫn còn nhiều hạn chế
  • Apple và các ứng dụng bên thứ ba phần lớn либо là dịch vụ trả phí, либо thiếu tính tiện dụng cho người dùng
  • Ứng dụng tự phát triển cung cấp tìm kiếm toàn văn, hỗ trợ iCloud, môi trường ưu tiên cục bộ v.v.
  • Cách tiếp cận đa nền tảng như React Native có giới hạn do ràng buộc hệ thống tệp và vấn đề ổn định
  • Với thiết kế dựa trên SwiftUI và SQLite, ứng dụng hiện thực hóa trải nghiệm quản lý nhạc độc lập và có khả năng mở rộng cao

Tổng quan

Để tự mình giải quyết sự bất tiện rằng ngay cả năm 2025, người dùng vẫn khó phát tự do các tệp MP3 do chính họ sở hữu trên iPhone, tác giả đã tự xây dựng một ứng dụng phát nhạc và giới thiệu quá trình cùng kết quả. Trong khi dịch vụ âm nhạc của Apple và các ứng dụng bên ngoài đều có nhiều giới hạn và mô hình thu phí, ứng dụng do tác giả tự triển khai mang lại trải nghiệm được tối ưu cho việc quản lý và phát các tệp nhạc của người dùng.

Vì sao tôi tự làm trình phát nhạc

  • Các tính năng đồng bộ hóa dựa trên đám mây như Apple Music và iCloud Music Library chỉ hoạt động khi đăng ký dịch vụ trả phí
  • Khi ngừng thuê bao, đồng bộ tự động và quyền truy cập thư mục nhạc đều bị chặn hoàn toàn
  • Tác giả cảm nhận rõ sự thiếu vắng quyền làm chủ đối với việc tích hợp thư viện nhạc hiện có và khả năng tận dụng thiết bị đa dụng
  • Mục tiêu là hiện thực hóa quyền tự quyết rằng “với chiếc smartphone đã mua, mình cũng có thể tự làm ra các tính năng thật sự cần thiết”

Phân tích các giải pháp phát nhạc của Apple và bên thứ ba

Ứng dụng mặc định của Apple

  • Có thể dùng ứng dụng Files để phát tệp nhạc trong thư mục iCloud, nhưng các chức năng cơ bản như quản lý playlist, sắp xếp metadata, thao tác hàng chờ còn rất thiếu
  • Trải nghiệm người dùng không được tối ưu cho việc nghe nhạc

Ứng dụng bên thứ ba

  • App Store có nhiều ứng dụng ngoài, nhưng nhiều ứng dụng dùng mô hình tính phí theo thuê bao, và mức độ tính năng cũng khác nhau
  • Có một số ứng dụng trả phí mua đứt như Doppler, nhưng trải nghiệm tìm kiếm và import trong các thư mục iCloud lớn chưa mượt mà

Hành trình kỹ thuật tiến tới “builder mode”

  • Tác giả quyết định phải tự làm một ứng dụng phát nhạc
  • Các tính năng yêu cầu: tìm kiếm toàn văn nhanh trên toàn bộ thư mục iCloud, các chức năng quản lý nhạc ở mức ứng dụng nghe nhạc chính thức (queue, playlist, sắp xếp, v.v.) cùng giao diện trực quan

Thử nghiệm đầu tiên với React Native

  • Do từng thấy bất tiện khi dùng Swift (trước đây chẳng hạn chưa hỗ trợ async/await), tác giả ưu tiên React Native/Expo
  • Việc làm UI khá ổn nhờ tận dụng template mã nguồn mở, nhưng khi xử lý truy cập hệ thống tệp (thư mục iCloud) và đồng bộ hóa, ứng dụng gặp crash và bộc lộ giới hạn chức năng
  • Sau khi nhận ra rằng theo chính sách sandbox của iOS, không thể truy cập thư mục ngoài nếu không có quyền rõ ràng, tác giả đã chuyển sang Swift

Lựa chọn SwiftUI và lý do

  • Tận dụng mô hình UI khai báo của SwiftUI cùng async/await hiện đại và Swift Actor
  • Nhờ tách biệt triệt để UI và logic dữ liệu, có thể hiện thực luồng dữ liệu sạch và xử lý đồng thời ở tầng domain
  • Cũng tối ưu cho việc tận dụng LLM (OpenAI o1, DeepSeek, v.v.), giúp giảm phụ thuộc trong mã UI được sinh ra

Kiến trúc ứng dụng và mô hình dữ liệu

  • Sử dụng SQLite làm kho dữ liệu; vì có thể dùng ngay tìm kiếm toàn văn (FTS5) nên được chọn thay cho CoreData

  • 3 màn hình/chế độ chính của ứng dụng:

    1. Import thư viện: lưu hàng loạt đường dẫn tệp audio theo từng thư mục iCloud vào DB để hỗ trợ tìm kiếm/quản lý linh hoạt
    2. Quản lý thư viện: playlist, phân loại bài hát, chỉnh sửa, v.v.
    3. Phát nhạc: quản lý queue (lặp lại, shuffle, v.v.) và điều khiển bài hát cơ bản
  • Luồng người dùng khi import thư viện:

    • Từ trạng thái trống ban đầu, chọn thư mục qua "Add iCloud Source" và quét cây thư mục
    • Khi lập chỉ mục hoàn tất, chuyển sang tab thư viện, nơi có danh sách theo từ khóa và mini player
    • Khi thêm bài hát mới, dữ liệu được tự động hợp nhất trong nền

Tầng logic kiểu backend

  • Dựa trên kinh nghiệm phát triển startup web/cloud, tác giả tách biệt triệt để tầng logic với UI/ViewModel
  • Tầng domain (Swift actors) giữ các business logic chính như import, tìm kiếm, queue, qua đó đảm bảo an toàn luồng
  • Việc cập nhật UI được phân tách gọn gàng thông qua ViewModel, giảm tối đa sự phụ thuộc giữa đồng bộ tệp, phát nhạc và UI để cải thiện khả năng bảo trì

Triển khai tìm kiếm toàn văn bằng SQLite

  • Từ iOS 11 trở lên có thể dùng SQLite hỗ trợ FTS5 mà không cần cấu hình riêng
  • Hỗ trợ tìm kiếm nhanh mà không cần chỉ mục hoặc máy chủ tìm kiếm bên ngoài
  • Dùng thư viện SQLite.swift để xử lý truy vấn thông thường, còn truy vấn FTS thì viết SQL trực tiếp

Cấu trúc bảng FTS

  • songs_fts: lập chỉ mục các trường artist, title, album, albumArtist của bài hát
  • source_paths_fts: lập chỉ mục đường dẫn tệp và tên tệp
  • Tồn tại song song với bảng chính, và ở UI chỉ dùng cho tìm kiếm (READ)
  • Việc cập nhật chỉ mục được xử lý bằng transaction DB để duy trì tính nhất quán dữ liệu

Tìm kiếm mờ (Fuzzy) và xếp hạng

  • Tự động thêm wildcard vào sau giá trị nhập, sắp xếp theo độ liên quan dựa trên BM25
  • Tổng thể, ứng dụng hiện thực hóa cấu trúc dữ liệu có thể dự đoán được, không phụ thuộc mạng cùng môi trường tìm kiếm cục bộ mạnh mẽ

Hệ thống tệp iOS và việc tận dụng bookmark

  • Khác với macOS, iOS không hỗ trợ security-scoped bookmarks, nên thiếu khả năng duy trì truy cập mở rộng tới tệp bên ngoài
  • chỉ lưu thông tin đường dẫn, khi truy cập lại người dùng phải cấp quyền lần nữa
  • Giải pháp là: tại thời điểm được cấp quyền, sao chép chính tệp đó vào sandbox của ứng dụng
  • Việc sao chép tự động trong nền cũng cải thiện tốc độ lập chỉ mục tệp
  • Dù sau khi thiết bị khởi động lại, việc phát trực tiếp tệp bên ngoài vẫn khó khăn, điều này cho thấy các hạn chế truy cập tệp của iOS là rất nghiêm ngặt

Phát nhạc bằng AVFoundation và thiết kế giao diện

  • Sử dụng framework AVFoundationAVURLAsset để phân tích metadata của tệp audio
  • Một số trường như track number được phân tích thủ công (dùng thẻ ID3)
  • Để phát audio, dùng AVAudioPlayer; đồng thời triển khai MPRemoteCommandCenter và giao thức Delegate để tích hợp với Control Center

Cảm nhận sau khi phát triển và suy ngẫm về chính sách của Apple

Những điểm bất tiện

  • Môi trường hạn chế của Xcode: preview thời gian thực của SwiftUI có tiến bộ, nhưng vẫn chưa bằng độ tiện của VSCode hay Flutter
  • Thiếu độ linh hoạt của editor: nếu muốn dùng Swift LSP trong Neovim hoặc VSCode thì cần cấu hình thêm và độ hoàn thiện còn thấp
  • Một số góc của SDK vẫn thiên về Objective-C: chẳng hạn ở phần truy vấn metadata còn thiếu API thân thiện với Swift hiện đại
  • Việc debug tích hợp iCloud khá phiền: không thể triển khai đầy đủ tính năng cloud trong SwiftUI preview, nên phải tự dựng mock trực tiếp

Những điểm tích cực

  • Async/await giúp việc viết mã đồng thời thiên về I/O trở nên dễ dàng hơn rõ rệt
  • Thư viện native phong phú: vượt khỏi giới hạn của các binding mã nguồn mở để phát triển đa dạng tính năng hơn
  • Thiết kế UI khai báo của SwiftUI: cảm nhận rõ ưu điểm kiểu React và năng suất cao

Kết luận: có lẽ việc phát triển nên dễ hơn chứ?

  • Mục tiêu tạo trình phát nhạc cục bộ/offline đã đạt được sau 1,5 tuần phát triển
  • Trên thực tế, việc phân phối chính ứng dụng cũng có giới hạn: nếu không có chứng chỉ nhà phát triển thì sau 7 ngày sẽ không dùng được, và cần đăng ký nhà phát triển $99/năm
  • Ngay cả sau EU DMA Act, sideloading vẫn chưa được mở hoàn toàn, nên các nhà phát triển cá nhân hay làm vì sở thích vẫn tiếp tục bị ràng buộc
  • PWA trên iOS cũng còn nhiều hạn chế, như không hỗ trợ các API quan trọng (Web Bluetooth/USB/NFC, Background Sync, v.v.)
  • AI đã hạ thấp rào cản phát triển, nhưng riêng iOS vẫn có rào cản gia nhập cao do các quy định mang tính áp đặt
  • Các hạn chế đối với nhà phát triển độc lập và quyền của người dùng vẫn còn nguyên, và tính đóng của iOS vẫn là một yếu tố cản trở đổi mới

1 bình luận

 
GN⁺ 2025-05-24
Ý kiến Hacker News
  • Tôi là người đã xây dựng bộ sưu tập nhạc của mình ở định dạng FLAC suốt 25 năm, và năm ngoái tôi đã mua một điện thoại Android cùng thẻ MicroSD 1TB nên rất hài lòng vì có thể mang toàn bộ âm nhạc trong túi. Chắc chắn tôi không phải người duy nhất không muốn đi thuê nhạc, mất quyền kiểm soát, nghe stream những bài mà ngành công nghiệp đẩy lên, hay phải xử lý quảng cáo. Thật tuyệt khi thấy có người tự phát triển ứng dụng của mình.
    • Theo tôi, công nghệ đã theo kịp từ nhiều năm trước rồi, chỉ là bạn đang cố chấp với một định dạng không phù hợp. Nếu dùng re-encode tốt thì có thể chứa toàn bộ nhạc trong dung lượng nhỏ hơn rất nhiều mà chất lượng vẫn trong suốt đến mức không thể nhận ra khác biệt. Tôi khuyên nên giữ file FLAC trên desktop để làm bản sao lưu.
    • Đúng là bạn thuộc kiểu người thực sự biết sưu tầm. Bộ sưu tập nhạc của tôi có khoảng 25% là các định dạng lossless như FLAC/APE/ALAC/WavePack, và tổng dung lượng đã vượt 3TB. Vì vậy tôi gặp khó khăn khi nghe nhạc lúc di chuyển — rất khó quyết định trước sẽ chép nhạc nào sang thiết bị di động.
    • Tôi liên tục gặp vấn đề trên Android khi ảnh bìa album hoặc thông tin tiêu đề không được phản ánh đúng hoặc thay đổi ngẫu nhiên. Có vẻ đây là lỗi Android, không biết có ai từng xử lý được chưa.
    • Tôi cũng chỉ sưu tập cá nhân bằng FLAC, dù chưa tới 25 năm. Nó đã vượt 1TB, và tôi đang rất hài lòng khi dùng server Navidrome cùng client Symfonium. Thẻ microSD 2TB giờ cũng bắt đầu xuất hiện, nên khi giá giảm thêm thì chắc tôi sẽ mua một cái.
  • Tôi nghe nhạc từ thời winamp, và đến giờ trong thời đại streaming tôi vẫn sắp xếp thư viện nhạc local theo từng thư mục. Giống như các bình luận khác, tôi cũng tự làm một trình phát nhạc kiểu old-school như một dự án sở thích để nghe nhạc offline. Nó là một ứng dụng html/js một trang, có điều khiển hoàn toàn bằng bàn phím và chức năng queue (danh sách phát) đơn giản. Mời xem https://nobsutils.com/mp
    • Tôi cũng là người đã nghĩ giao diện winamp thật sự rất xuất sắc từ 27 năm trước. Điểm mạnh cốt lõi là sự đơn giản: tập hợp file theo thư mục, phát ngẫu nhiên toàn bộ, và chỉ phát một thư mục cụ thể.
    • Phản hồi là ứng dụng tự làm này hoạt động rất tốt.
    • Với tôi, foobar2000 từng là trình phát nhạc yêu thích nhất. Hiện giờ tôi thay nó bằng ứng dụng Cog.
  • Tôi đã tự phát triển một webapp để nghe trọn album và có thể đổi thiết bị mà vẫn tiếp tục từ đúng vị trí đã dừng. Dù tôi nghe album từ đầu đến cuối, các dịch vụ như YouTube Music lại không nhớ vị trí phát cho tốt hoặc việc chuyển thiết bị rất bất tiện. Webapp tôi làm chỉ cần dán URL là sẽ tải về server bằng yt-dlp và có thể stream từ đó. Nó luôn nhớ vị trí phát, nên tôi có thể nghe tiếp trên laptop ở chỗ làm đúng từ đoạn đang nghe trong xe. Việc thêm các bản mix từ nguồn khác như NTS Radio cũng hoạt động cực kỳ tốt.
    • Tôi đồng cảm với việc YouTube Music không lưu queue và không chuyển đổi mượt giữa các thiết bị. Tôi rất muốn thử webapp mà bạn đã làm.
  • Tôi ước bài viết nói không chỉ về thiết bị vật lý mà còn về phần mềm để quản lý và phát chúng. Vài năm trước tôi muốn mua cho con trai 10 tuổi một máy nghe mp3 nhưng bị sốc vì hầu như không có sản phẩm phù hợp. Apple đã để lại một khoảng trống lớn trên thị trường khi ngừng iPod, nhưng đến giờ vẫn chưa ai thực sự lấp đầy. iPod shuffle (dạng USB stick) là máy nghe mp3 tốt nhất tôi từng dùng: nhỏ, cắm là chạy, pin lâu. Khái niệm không có màn hình và chỉ shuffle thậm chí còn là một ưu điểm. Những thiết bị đơn giản như vậy hiện cũng không còn được sao chép trên thị trường phần cứng. Có người xem đây là vấn đề phần mềm/DRM, nhưng tôi vẫn thấy tiếc và mong có một thiết bị rẻ, dễ mang theo, chỉ để phát nhạc.
    • Tôi nghĩ thay đổi lớn chủ yếu không phải do iPod biến mất mà là do Spotify và smartphone phổ biến. Hai thứ đó đã chiếm gần hết thị trường và đẩy lùi mọi lựa chọn khác.
    • Chia sẻ rằng Fiio đang bán nhiều sản phẩm trong phân khúc này, ví dụ 1 ví dụ 2
    • Tôi nghĩ đây không phải vấn đề phần cứng hay phần mềm mà là vấn đề nhu cầu. Các hãng Trung Quốc đang bán những thiết bị mini chứa chức năng của smartphone như Mini iPhone 16, Mini S24, đồng thời cũng phát nhạc được, với giá $50~$100. Phần lớn phụ huynh có lẽ sẽ mua những thiết bị như vậy cho con hơn là một máy mp3 player, và cũng không có nhiều người quyết tâm không đưa điện thoại cho con đến tận 14 tuổi, nên nhu cầu thị trường hình thành theo hướng đó.
    • Sony vẫn tiếp tục ra mắt các trình phát tốt dưới thương hiệu "walkman", liên kết chính thức. Với trẻ 10 tuổi thì có thể hơi đắt, nên khuyên tìm hàng đã qua sử dụng trên eBay.
    • Một làn ký ức được gọi về khi nhắc tới những máy mp3 player như SanDisk Clip chắc vẫn đang nằm đâu đó trong nhà.
  • Tôi đọc bài này rất thích và vẫn chưa đọc xong. Tôi đặc biệt thích phần đọc về quá trình tác giả đưa ra các quyết định nhỏ, tỉ mỉ và bối cảnh phía sau chúng. Hầu như mọi ứng dụng âm nhạc đều có UX và bố cục na ná nhau, nên tôi luôn có cảm giác đang “đấm bốc” với tất cả các app nhạc này. Tôi ủng hộ những người dám thử hướng đi mới.
  • Tôi vẫn chỉ dùng file local trong ứng dụng Apple Music. Tôi tắt dịch vụ streaming Apple Music, nạp toàn bộ nhạc vào ứng dụng Apple Music trên macOS, rồi nối điện thoại với laptop để sync như năm 2007. Vì nhạc của tôi không thay đổi thường xuyên nên cách này không có vấn đề gì, đồng thời tôi cũng cảm nhận được chút hoài niệm từ việc đồng bộ bằng dây.
    • Cũng nhắc rằng tự động đồng bộ qua wi-fi bằng iTunes vẫn hoạt động rất tốt.
  • Về câu hỏi “tại sao một công ty IT đổi mới lại đặt ra rào cản cho việc phát triển ứng dụng mang tính dân chủ”, có người dẫn lời cựu CEO Disney Michael Eisner để nói rằng bản chất doanh nghiệp rốt cuộc vẫn là theo đuổi lợi nhuận, và Apple không phải công ty đổi mới hay dân chủ mà là công ty thiên về kiếm tiền. Trừ khi có thể đảm bảo nhiều doanh thu hơn, việc hạ thấp rào cản cho nhà phát triển hay mở rộng tính dân chủ chẳng khác nào từ bỏ con "ngỗng đẻ trứng vàng" là nguồn thu từ cửa hàng chính thức, tức là lợi nhuận vẫn là ưu tiên cao nhất.
  • Với người dùng Android có thư viện nhạc offline, tôi cực kỳ đề xuất Musicolet, nó hoạt động hoàn hảo.
    • Cũng nhấn mạnh rằng Symfonium rất xuất sắc nhờ hỗ trợ rộng như Plex, Jellyfin, WebDAV, SMB, v.v.
  • Tôi đọc phần phân tích kỹ thuật chuyên sâu này rất thích, và khi chuyển từ React Native sang SwiftUI thì càng cảm nhận rõ native code giúp việc truy cập iCloud và tối ưu hóa dễ hơn nhiều thế nào. Mẹo tìm kiếm SQLite FTS5 cũng rất ấn tượng, tôi định tham khảo cho ứng dụng thư viện của mình.
  • Ban đầu tôi né Swift vì thấy khó, nhưng tôi không đồng ý với quan điểm rằng sau khi có async/await thì việc viết code đồng thời trở nên dễ hơn. async đúng là giúp tiện khi viết code, nhưng khi hệ thống lớn lên thì trải nghiệm của tôi là luồng code và trạng thái đồng thời trở nên khó nắm bắt hơn nhiều. Khi gặp những vấn đề chưa có lời giải, tôi nghĩ vẫn có các lựa chọn thay thế như green lightweight threads. Về lâu dài, tôi lo rằng cách tiếp cận dựa trên async lại làm tăng chi phí bảo trì.
    • Tôi cho rằng vấn đề nằm ở giới hạn của lớp trừu tượng async/await hơn là ở chính khái niệm concurrency. Concurrency tốt phải giúp code dễ hiểu và dễ quản lý hơn khi mở rộng, và việc đóng gói theo process/service mang lại lợi ích rất lớn.
    • Với mục tiêu chỉ là một trình phát nhạc đơn giản của người viết, có lẽ độ phức tạp tăng thêm do async sẽ gần như không thành vấn đề.