În 2025, redarea propriei muzici pe un iPhone este surprinzător de dificilă, cu excepția cazului în care plătiți Apple sau navigați într-un labirint de limitări.
În 2025, joacă pe cont propriumusic on an iPhone is surprisingly hard, cu excepția cazului în care plătiți Apple sau navigați într-un labirint de limitări.full text search,iCloud supportşi alocal-first experience. .Linkuri de pe GitHub
De ce am construit propriul meu player audio
Ca mulți oameni, am luat prea multe abonamente, unele prin intermediul Apple (iCloud, Apple Music), altele s-au pierdut în platforme aleatorii (cum ar fi Netflix, pe care am uitat că încă plătesc). am folosit de fapt Apple Music în mod regulat (și anterior Spotify), dar streamingul sa dovedit a fi mai mult confort decât necesitate.
Inițial m-am gândit că voi continua să folosesc iCloud Music Library pentru sincronizarea muzicii pe dispozitive, dar odată ce am anulat abonamentul la Apple Music, sincronizarea a încetat să funcționeze.behind a paywallPuteți obține înapoi prin intermediuliTunes meciuri(24 de dolari pe an)Match doar stochează online copii AAC de 256 kbps; fișierele dvs. originale rămân plasate dacă nu alegeți să le înlocuiți. Pe un Mac modern, faceți toate acestea în aplicația Muzică.
iTunes meciuriFrustrat de lipsa de opțiuni, am mers labuilder routeDacă am cumpărat un dispozitiv de calculator (iPhone în acest caz), ce mă împiedică să construiesc exact ceea ce am nevoie cu codul pentru a-l folosi?În acest articol, vreau să împărtășesc întreaga mea călătorie de frustrări spre crearea unei funcționalități de bază a player-ului muzical: încărcarea fișierelor audio, organizarea și redarea acestora, dar mai ales, am vrut să îmi reamintesc,este încă un calculator cu scop general, ar trebui să-l pot face să facă ceea ce vreau.
Ce oferă Apple (și alții) astăzi
Înainte de a-mi scrie propria aplicație, am explorat opțiunile oficiale și terțe părți pentru redarea muzicii offline.
Aplicațiile Apple încorporate
Apple vă permite din punct de vedere tehnic să redați muzică direct din iCloud prin intermediul aplicației Fișiere, dar funcționalitatea sa nu este concepută pentru ascultarea muzicii.lacks essential featurescum ar fi gestionarea listelor de redare, sortarea metadatelor sau cozile de redare.În timp ce acceptă redarea muzicii, este foarte limitată și generalăNu este o experiență de utilizator bună.
Nu este o experiență de utilizator bunăAplicații terțe
Am mers la magazinul de aplicații pentru a căuta aplicații cool care să-mi rezolve problema, în timp ce există multe dintre ele, multe se bazează pesubscription-based pricing, un model discutabil pentru o aplicație care joacă pur și simplu fișiere deja deținute de utilizatori.DopplerulAm jucat cu ea în timpul unei încercări, dar UX-ul este construit în jurul gestionării albumelor. Căutarea nu a fost atât de bună, iar funcționalitatea de import de la iCloud a fost lentă și dificil de utilizat pe un număr mare de foldere încorporate.
Going Builder Mode: Călătoria mea tehnică
Cu toate acestea, am decis să-mi creez propriul player muzical ideal care să-mi rezolve punctele de durere:
- Căutare flexibilă în întreg textul în toate folderele iCloud, astfel încât să pot selecta și importa rapid un folder cu muzică sau fișiere specifice.
- Funcționalitate în gestionarea muzicii cel puțin la fel cu aplicația oficială de muzică: coadă, gestionarea listelor de redare și sortarea după albume etc.
- Interfață familiară și prietenoasă.
Încercați să reacționați mai întâi nativ
În urmă cu câțiva ani, mi-a plăcut sintaxa (s-a simțit mai aproape de TypeScript) și a apreciat securitatea memoriei ca Rust, dar fără nativasync
/await
În acel moment, scrierea codului concomitent în comparație cu Go sau JS/TS s-a simțit clunky și boilerplate-gros. Această experiență m-a lăsat frustrat, așa că atunci când am revizuit acest proiect, am ajuns inițial pentru ceva mai familiar.
Acestea fiind spuse, am mers cu React Native sau Expo, sperând să-mi reutilizez experiența de dezvoltare web și să conectez un player UI din șabloanele existente. Construirea UI-ului de redare a fost simplă; există numeroase exemple open-source și videoclipuri tutorial despre construirea de player-uri de muzică care să arate bine care să se potrivească nevoilor mele.Proiectul template de Gionatha Sturba, pentru că părea să aibă toate caracteristicile de care am nevoie pentru aplicația mea.
Accesarea sistemului de fișiere și sincronizarea fișierelor cloud a lovit blocajele majore: bibliotecile precumexpo-filesystem
Suportată selectarea de fișiere de bază, dar traversarea recursivă peste directorii iCloud adânc înnăscuțioften failed or even caused app crashesAcest lucru a clarificat faptul că aJavaScript-based approach introduced more complexitydecât doar lucrul cu API-urile native ale Apple, chiar dacă a însemnat o curbă de învățare mai abruptă.
Sandboxing-ul iOS împiedică aplicațiile să citească fișiere fără permisiunea explicită a utilizatorului, ceea ce a însemnat că React Native nu putea accesa în mod fiabil foldere externe.
Schimbare la SwiftUI
Am mers cuSwiftUIîn loc de UIKit sau storyboards pentru că am vrut unclean and declarative UIstrat care ar rămâne în afara drumului în timp ce m-am concentrat pe logica domeniului și sincronizarea datelor. Cu caracteristici moderne, cum ar fi async/await și integrarea cuSwift ActorsSwiftUI a facilitat, de asemenea, structurarea aplicației în componente ViewModel izolate, ceea ce, la rândul său, mi-a ajutat să obțin rezultate mai bune de la LLM-uri cum ar fi OpenAI o1 și DeepSeek.
Arhitectura aplicațiilor și modelul de date
Să trecem la arhitectura aplicației pe care am creat-o: am folosit SQLite pentru stocarea de date persistente și am abordat arhitectura aplicației ca o aplicație de server simplă. am evitat CoreData pentru că aveam nevoie de un control strict asupra schemei, a interogărilor brute și mai ales a căutării textului complet.
Trei ecrane principale
The app consists of 3 screen/modes:
- Import de bibliotecă. Acesta este locul în care adăugați folderul bibliotecii iCloud. Aplicația scanează fiecare folder pentru fișiere audio și introduce fiecare cale într-o bază de date SQLite. În acest fel, puteți avea flexibilitate deplină în căutarea, adăugarea de foldere și subfoldere. selectorul de fișiere nativ al Apple este foarte clunky; nu puteți selecta mai multe directoare pe care le-ați căutat după cuvânt cheie și apoi, de asemenea, o grămadă de fișiere într-o singură mișcare.
- Gestionarea bibliotecii.Acesta este locul unde puteți gestiona piesele adăugate și puteți organiza listele de redare.În cea mai mare parte, am reflectat modul în care Apple a făcut asta în aplicația lor de muzică și a fost suficient de bun pentru nevoile mele.
- Această parte a aplicației gestionează gestionarea cozii (repeat, shuffle), etc., precum și funcționalitatea de redare, oprire și următorul cântec.
O diagramă simplă a fluxului de utilizator este prezentată aici:
User flow in practice:Atunci când aplicația pornește cu o bibliotecă goală, aceasta ajunge pe fila Sync, afișând un buton mare "Add iCloud Source". Alegeți un folder acolo, iar ecranul Sync afișează o bară de progres în timp ce merge pe copac. De îndată ce indexarea se termină, vă trece la fila Bibliotecă, a cărei primă listă de ecranPlaylists / Artists / Albums / SongsPlonjați în orice listă, atingeți o piesă și un Mini-Player apare de-a lungul fundului; atingeți acea mini-bară pentru a deschide playerul pe ecran complet cu shuffle, repetare, reordonare în coadă și volum. Swipe sau atingeți pictograma de închidere și vă întoarceți direct la Biblioteca în timp ce redarea continuă. În orice moment aveți nevoie de mai multă muzică, săriți înapoi la sincronizare, atingeți "+" în bara nav, selectați un alt folder, iar serviciul de import fuzionează cântece noi în fundal, fără a fi necesară redeschiderea.
Backend-Like strat logic
Având un fundal web / cloud și livrat o mulțime de cod server în timp ce lucrați în start-up-uri, am mers cu unbackend-like architecturepentru aplicaţia mobilă. întregul nivel de domeniu/logică a fost separat deView and View-Model layerPentru că a trebuit să-i pun pecloud syncing, metadata parsingaspectul aplicației și să aibă acces la date curate la un SQLite DB.Iată o diagramă de arhitectură stratată aproximativă pe care am folosit-o aici:
How the layers talk:SQLite se află în partea de jos, stocând rânduri brute de melodii și indici FTS. Apoi, depozitele înfășoară baza de date și expun API-urile async.domain actorsActori Swift care dețin toate regulile de afaceri (import, căutare, logică de coadă), astfel încât mutațiile de stat să rămânăthread-safeViewModels se abonează la actori, transformă datele în structuri gata pentru UI, iar vizualizările SwiftUI reflectă pur și simplu ceea ce primesc.nicely decoupled.
Cautarea textului complet cu SQLite
Așa cum am menționat mai devreme, este norocos că puteți importa o versiune SQLite cu capacități FTS: începând cu iOS 11, este disponibilă în afara cutiei.without extra setupAcest lucru a făcut ușor să integrezi căutarea fuzzy în biblioteca mea de muzicăwithout any third-party dependenciesÎn plus, am folosit biblioteca SQLite.swift pentru interogări regulate (care funcționează ca un fel de constructor de interogări cu securitate în timp de compilare); cu toate acestea, pentru interogările FTS, a trebuit să recurg la declarații SQL regulate.
SculpturăFT5Extensia a ajuns să fie una dintre cele mai valoroase piese ale arhitecturii.Mi-a permis să interogez nume de fișiere și metadate, cum ar fi artistul, albumul și titlul, fără o infrastructură suplimentară de indexare.
Stabilirea tabelelor FTS
Domain |
Swift actor / repo |
FTS5 table |
Columns that get indexed |
---|---|---|---|
Library songs |
|
|
|
Source-browser paths |
|
|
|
Cântece de bibliotecă
SQLiteSongRepository
songs_fts
artist
,title
,album
,albumArtist
Sursă-browser căi
SQLiteSourcePathSearchRepository
source_paths_fts
fullPath
,fileName
Am folosit două tabele FTS5: una pentru cântece indexate (artist/titlu/album) și una pentru căile de fișiere în timpul importului de foldere.songs
,source_paths
FTS esteread-only for the UIToate scrierile se petrec în interiorul depozitelor, astfel încât nimic să nu treacă prin crăpături.
Crearea indexului de căutare
FTS5 încorporat de SQLite face ca căutările rapide să fie ușoare.Aici este o definiție simplă a tabelului pe care am folosit-o:
try db.execute("""
CREATE VIRTUAL TABLE IF NOT EXISTS songs_fts USING fts5(
songId UNINDEXED,
artist, title, album, albumArtist,
tokenize='unicode61'
);
""")
Am folositunicode61
Tokenizer pentru a se asigura că o mare varietate de caractere sunt manipulate. cheile non-searchable sunt marcate cuUNINDEXED
De aceea, nu ar trebui să blocheze dicționarul cuvântului.
Actualizarea datelor în mod fiabil
Pentru a păstra lucrurile simple și sigure, am înfășurat actualizări și inserări în tranzacții. Acest lucru asigură că indexul de căutare nu iese niciodată din sincronizare, chiar dacă aplicația se prăbușește sau este întreruptă.
func upsertSong(_ song: Song) async throws {
db.transaction {
// insert or update main song data
// insert or update search index data
}
}
În căutarea fuzzy
Pentru căutări ușor de utilizat, adaug suport wildcard automat. Dacă tastați "lumine", acesta caută "lumine*" intern, oferind rezultate instantanee chiar și cu interogări parțiale.
De asemenea, folosesc clasamentul inteligent încorporat al SQLite (bm25
Pentru a returna rezultate mai relevante, fără complexitate suplimentară:
SELECT s.*
FROM songs s JOIN songs_fts fts ON s.id = fts.songId
WHERE songs_fts MATCH ?
ORDER BY bm25(songs_fts)
LIMIT ? OFFSET ?;
În general, utilizarea SQLite brut mi-a oferit flexibilitatea de care aveam nevoie: schema previzibilă, accesul local-first și căutarea puternică a textului complet, fără a introduce dependențe de rețea sau servicii externe.
Lucrul cu fișierele și marcajele iOS
Pe iOS, aplicațiile pot stoca marcajele persistente pentru locațiile fișierelor, darsecurity-scoped bookmarks, care acordă acces extins la fișiere în afara sandbox-ului aplicației, sunt disponibile numai pemacOSAplicațiile iOS pot utiliza marcajele obișnuite pentru a-și aminti căile de fișier și pentru a solicita din nou accesul prin intermediul selectorului de documente, dar acest acces nu este garantat să persiste în tăcere.Documentarea bookmark-ului Apple.
Pentru a atenua acest lucru, am implementat un mecanism de retrogradare care copiază fișierele înapp's own sandboxed containerAcest lucru evită ciclul de viață fragil al marcajelor de securitate care se pot rupe în tăcere dacă iOS resetă permisiunile.Prin copierea proactivă a fișierelor în fundal, în timp ce marcajul este valabil, nu există niciun risc de accesare a referințelor de fișiere audio nevalide.
Această abordare îmbunătățește, de asemenea, viteza de indexare.Pot să scan o dată structura folderului (în timp ce accesul este activ), să import doar fișierele audio relevante și să traversez în siguranță directorii adânci.rămâne o problemă nerezolvată pentru mineAcest lucru evidențiază modul în careunder-supportedacest caz de utilizare este, chiar și pentru aplicațiile native, și cât de complex este încă săhandle file access reliably on iOS.
Crearea playback-ului și a UI
Metodă Parsing
Pentru a analiza metadatele din fișierele audio, am folositAVFoundation frameworkÎn special, laAVURLAssetclasa, care permite inspecția metadatelor fișierelor media, cum ar fi titlul, artistul albumului etc. În timp ce analizarea metadatelor este gestionată de SDK-ul nativ, anumite câmpuri, cum ar fi numerele de urmărire, trebuie să căutați manual din etichetele ID3.Căutare pe GitHubpentru a găsi exemple, deoarece documentația oficială lipsea de acoperire pentru cazurile de margine.
Redare audio cu AVFoundation
După ce biblioteca este indexată, implementarea unui player audio pare destul de simplă: trebuie doar să inițiați o instanță deAVAudioPlayer
În plus, pentru caracteristicile de calitate a vieții: redarea muzicii de la centrul de control, a trebuit să pun în aplicareAVAudioPlayerDelegate
Protocol și, de asemenea, atașat la AppleMPRemoteCommandCenter
, care permite dezvoltatorilor să răspundă la controlele de redare la nivel de sistem.
Reflecții: Apple, Lock-In pentru dezvoltatori și viitorul
Iată ce a ieșit în evidență în timpul dezvoltării:
Răul
Xcode's limitations remain frustrating.Preview-urile SwiftUI în timp real sunt cu siguranță un pas înainte, dar experiența generală de dezvoltare nu este încă la fel cu ceea ce Flutter a oferit în urmă cu cinci ani: integrarea strânsă a VSCode, reîncărcările simulatorului în timp real și instrumentele de debugging familiare.
Lack of editor flexibility.Configurarea suportului Language Server Protocol (LSP) pentru Swift în Neovim sau VSCode necesită instrumente suplimentare, cum ar fixcode-build-server
, și încă nu se potrivește pe deplin cu experiența de dezvoltare a ecosistemelor web-first.
Some corners of Apple's SDK still live in Objective-C land.Căutarea fișierului Spotlight, de exemplu, este expusă numai prinNSMetadataQuery
, care utilizează Key-Value Observing (KVO) și chei de șir, nu există încă un înveliș prietenos cu Swift.
SwiftUI's declarative UI is great, but debugging iCloud interactions still requires manual mocks.Preview-urile SwiftUI nu pot emula comportamentele complete ale aplicațiilor care implică drepturi iCloud, deci trebuie să glumiți interacțiunile cloud manual, un pic enervant, dar remarcabil.
Bunul
Async/await.În cele din urmă, pot scrie codul concomitent I/O-bound ca un imperativ fără apeluri enervante. care este o mare victorie, și apreciez foarte mult cât de ușor este de a scrie chiar și codul de sincronizare în Actors și să-l numească ca și în ecosistemele JavaScript.
Plethora of native libs.Da, nu sunteți limitat de legăturile cu sursă deschisă, cum ar fi în ecosistemele React Native/Flutter. Aici aveți mult mai multă libertate în a dezvolta ceva "mai serios" decât înlocuirea site-ului companiei / produsului (datorită experienței slabe mobile-first).
SwiftUIDa, abordarea în stil React pentru construirea UI-urilor oferă mai multă productivitate și spațiu pentru explorări.
Etichetă: constructia ar trebui sa fie mai usoara
După 1,5 săptămâni de hacking, am reușit să obțin piesa de software care îmi satisface exact nevoile: olocal/offline music playercare poate importa fișiere audio din stocarea în cloud.
Dar dezvoltatorii își dau seama repede că nu pot implementa cu ușurință aplicații pe dispozitivele lor în aceste zile și uită de asta: aplicațiile rulează doar pentru7 days without a dev certificate, și după aceea, trebuie să o reconstruiți, cu excepția cazului în care ați plătit 99 de dolari Apple pentru a vă înscrie în programul de dezvoltare.
7 zile fără certificat DEVChiar şi dupăDMA Act in the EUUtilizatorii din UE pot acum să instaleze aplicații de pe piețele terțe direct de pe site-ul unui dezvoltator, dar numai dacă acest dezvoltator este încă înscris în programul Apple de 99 USD/an și este de acord cu Termenii alternativi ai Apple.
O companie inovatoare de tehnologie pune în mod activ bariere în dezvoltarea democratizată a aplicațiilor.Confruntarea cu limitări semnificative pe iOS: chiar și după actualizările 16-18.x ale Apple, PWA-urile iOS rulează încă în sandbox-ul Safari. Ei primesc WebGL2 și web-push, dar nu primesc Web Bluetooth / USB / NFC, Background Sync sau mai mult de ~50MB de stocare garantată. WebGL rulează prin Metal shim, astfel încât ratele cadrelor din lumea reală urmăresc adesea aplicațiile native Metal; acest lucru este suficient pentru UI, dar nu pentru jocurile 3D AAA.
Astăzi, AI a redus complexitatea dezvoltării software-ului modern, permițând oricui să abordeze tehnologiile necunoscute, oferind toate cunoștințele necesare într-un mod accesibil.Puteți vedea clar cum dezvoltarea web a câștigat mai mult interes de la oamenii non-tehnici care au o modalitate de a-și construi ideile fără a se specializa într-o multitudine de tehnologii.Chiar dacă ați construit-o singuri, pentru voi înșivă, Apple încă are ultimul cuvântînainte de a putea rula mai mult de o săptămână. aceeași companie care a împuternicit odată dezvoltatorii independenți impune acumtight restrictions that hinder personal app developmentAI a făcut mai ușor ca niciodată să construiești noi instrumente, cu excepția cazului în care construiești pentru iOS, unde poarta este încă încuiată.
Legăturile de stânga
- iTunes Match – Asistență Apple
- Etichetă: Apple Docs
- FTS5 - Documentația SQLite
- Doppler Music Player – App Store pentru utilizatori
- EXPO FileSystem Documentare
- Informații despre programul de dezvoltare al Apple (7-day builds)
- Comunitatea Apple: Aplicație pentru fișiere și redare MP3