Praca z systemem plików jest podstawą każdej aplikacji mobilnej. Każda platforma oferuje własny model dostępu: Sandbox na iOS izoluje aplikacje w osobnych kontenerach, podczas gdy Scoped Storage na Androidzie ogranicza bezpośredni dostęp do współdzielonego miejsca przechowywania. Według Google Developer Documentation (2026), wprowadzenie Scoped Storage z Androidem 10 wymagało całkowitego przeglądu architektury przechowywania danych. W tym przewodniku omówimy FileManager, MediaStore API, Storage Access Framework i DocumentProvider dla obu platform.
Najważniejsze informacje
System plików w aplikacjach mobilnych to zestaw API, reguł bezpieczeństwa i ograniczeń, które definiują przechowywanie danych i dostęp do nich na urządzeniach iOS i Android. W przeciwieństwie do systemów operacyjnych dla komputerów stacjonarnych, platformy mobilne izolują każdą aplikację, aby chronić dane użytkownika przed nieautoryzowanym odczytem przez inne programy.
iOS korzysta z modelu Sandbox, gdzie każda aplikacja istnieje we własnym kontenerze ze ściśle ograniczonymi uprawnieniami. Przed wersją 10 Android zapewniał pełny dostęp do zewnętrznego miejsca przechowywania, ale wraz z wprowadzeniem Scoped Storage podejście zbliżyło się do iOS. Kluczowa różnica polega na tym, że iOS całkowicie izoluje system plików, podczas gdy Android oferuje kilka poziomów dostępu: prywatny katalog, publiczny MediaStore i tymczasowy dostęp przez SAF.
Aplikacje mobilne używają trzech typów przechowywania danych. Prywatne przechowywanie — katalog dostępny tylko dla aplikacji dla plików wewnętrznych i pamięci podręcznej. Współdzielone przechowywanie — pliki multimedialne przez MediaStore (Android) lub Files App (iOS). Przechowywanie w chmurze — iCloud Drive i Google Drive do synchronizacji między urządzeniami. Każdy typ ma własne ograniczenia dotyczące rozmiaru, czasu życia plików i warunków dostępu.
| Typ przechowywania | iOS | Android |
|---|---|---|
| Prywatne | Documents, Library, Caches | getFilesDir(), getCacheDir() |
| Multimedia współdzielone | PHPhotoLibrary przez selektor | MediaStore API (ContentResolver) |
| Dokumenty współdzielone | Files App przez UIDocumentPicker | Storage Access Framework (SAF) |
| Chmura | iCloud Drive (UIDocument) | Google Drive API |
| Pamięć podręczna | Katalog Caches, czyszczony przez system | getCacheDir(), getExternalCacheDir() |
Sandbox to architektura bezpieczeństwa iOS, która izoluje każdą aplikację. Aplikacja może czytać i pisać tylko w obrębie własnej piaskownicy. Aby uzyskać dostęp do kontaktów, zdjęć lub plików innych aplikacji, należy użyć selektorów systemowych: UIImagePickerController lub UIDocumentPickerViewController. Dostęp do Files App jest konfigurowany za pomocą flagi UIFileSharingEnabled w Info.plist. System plików w programowaniu mobilnym na iOS wymaga zrozumienia struktury katalogów i wyboru odpowiedniego miejsca dla każdego typu danych.
Piaskownica iOS składa się z kilku standardowych katalogów. Documents — dla plików użytkownika, uwzględniony w iCloud Backup. Caches — dla danych tymczasowych, które system może usunąć przy braku miejsca. Temporary — dla plików bieżącej sesji, czyszczony przy ponownym uruchomieniu. Application Support — dla wewnętrznych danych aplikacji ukrytych przed użytkownikiem. Wybór niewłaściwego katalogu prowadzi do problemów: zapisywanie pamięci podręcznej w Documents marnuje miejsce w iCloud i narusza wytyczne Apple dotyczące systemu plików.
import Foundation
let fileManager = FileManager.default
guard let documentsURL = fileManager.urls(
for: .documentDirectory,
in: .userDomainMask
).first else { return }
let fileURL = documentsURL.appendingPathComponent("notes.txt")
let text = "Содержимое файла"
// Atomowy zapis z szyfrowaniem
try text.write(
to: fileURL,
atomically: true,
encoding: .utf8
)
Klasa FileManager zapewnia pełny zestaw metod do zarządzania plikami na iOS. FileManager.default to bezpieczny wątkowo singleton odpowiedni dla większości operacji. Metody takie jak fileExists(atPath:), createDirectory(at:withIntermediateDirectories:attributes:), copyItem(at:to:) i removeItem(at:) pokrywają podstawowe scenariusze. Operacje na plikach większych niż 1 MB powinny być wykonywane w wątku tła przez DispatchQueue.global(). Do strumieniowania dużych woluminów używaj FileHandle zamiast ładować cały plik do pamięci.
func readDocumentsFile(named fileName: String) -> String? {
guard let docsURL = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask
).first else { return nil }
let fileURL = docsURL.appendingPathComponent(fileName)
return try? String(contentsOf: fileURL)
}
Wraz z wydaniem Androida 10 Google wprowadził Scoped Storage — model ograniczonego dostępu do systemu plików. Aplikacja może swobodnie czytać i pisać tylko w swoich prywatnych katalogach. Dla plików multimedialnych (zdjęcia, wideo, audio) używane jest MediaStore API przez ContentResolver. Dla dowolnych dokumentów używany jest Storage Access Framework przez Intent ACTION_OPEN_DOCUMENT. Na Androidzie 11+ bezpośredni dostęp do katalogu głównego zewnętrznego miejsca przechowywania jest całkowicie zabroniony i wszyscy deweloperzy muszą używać nowych API.
MediaStore to systemowy ContentProvider do uzyskiwania dostępu do plików multimedialnych na urządzeniu. Przez ContentResolver aplikacja żąda Uri plików zamiast bezpośrednich ścieżek. MediaStore.Files — dla wszystkich typów plików, Images — dla obrazów, Video — dla wideo, Audio — dla nagrań audio. Zapis do współdzielonych katalogów odbywa się przez insert() z DISPLAY_NAME, MIME_TYPE i RELATIVE_PATH. Po wstawieniu aplikacja otrzymuje Uri, przez które zapisywane są bajty. Typy MIME odgrywają kluczową rolę — nieprawidłowy typ spowoduje błąd przy otwieraniu pliku.
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "report.pdf")
put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
put(MediaStore.MediaColumns.RELATIVE_PATH, "Documents/Reports")
}
val uri = contentResolver.insert(
MediaStore.Files.getContentUri("external"),
contentValues
)
uri?.let {
contentResolver.openOutputStream(it)?.use { stream ->
stream.write(pdfBytes)
}
}
SAF zapewnia ujednolicony interfejs do wybierania i tworzenia plików bez uprawnień wykonawczych. Intent ACTION_OPEN_DOCUMENT otwiera systemowy menedżer plików na Androidzie. Po wyborze aplikacja otrzymuje Uri content:// z tymczasowym dostępem przez FLAG_GRANT_READ_URI_PERMISSION. ACTION_CREATE_DOCUMENT umożliwia zapisywanie plików w dowolnym zewnętrznym miejscu przechowywania wybranym przez użytkownika. SAF działa na Androidzie 5+ i zapewnia dostęp do plików od dostawców chmury podłączonych przez DocumentsProvider.
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(
"application/pdf",
"text/plain"
))
}
startActivityForResult(intent, REQUEST_CODE)
Obie platformy zapewniają wbudowane mechanizmy umożliwiające użytkownikom wybór plików. Dokumenty w aplikacjach mobilnych są przekazywane przez selektory systemowe, które przyznają tymczasowy dostęp do pliku bez stałych uprawnień. Na iOS jest to UIDocumentPickerViewController, na Androidzie — ACTION_OPEN_DOCUMENT. Dokumenty w aplikacjach mobilnych można wybierać zarówno z lokalnego przechowywania, jak i z usług chmurowych. Użytkownik jawnie określa plik, a aplikacja otrzymuje Uri lub URL z ograniczonym okresem ważności.
UIDocumentPickerViewController otwiera Files App i umożliwia wybór jednego lub więcej dokumentów. Tryby: import (kopiowanie do piaskownicy) i otwarcie (dostęp przez URL z zakresem bezpieczeństwa). Do filtrowania plików przekazywana jest tablica typów UTType — na przykład .pdf i .plainText. Po otrzymaniu URL aplikacja musi wywołać startAccessingSecurityScopedResource() przed odczytem i stopAccessingSecurityScopedResource() po zakończeniu. Niewywołanie stopAccessing prowadzi do wycieku zasobów systemowych. Dokumenty w aplikacjach mobilnych na iOS wymagają obowiązkowego zwolnienia tymczasowych uprawnień po zakończeniu pracy z plikiem.
let picker = UIDocumentPickerViewController(
forOpeningContentTypes: [.pdf, .plainText]
)
picker.allowsMultipleSelection = true
picker.delegate = self
present(picker, animated: true)
// Zwolnienie dostępu w delegacie
func documentPicker(
_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]
) {
guard let url = urls.first else { return }
url.startAccessingSecurityScopedResource()
defer { url.stopAccessingSecurityScopedResource() }
}
FileProvider to podklasa ContentProvider do bezpiecznego udostępniania plików między aplikacjami. Generuje tymczasowe Uri content:// oparte na plikach z określonych katalogów XML. Inne aplikacje uzyskują dostęp przez Intent z FLAG_GRANT_READ_URI_PERMISSION. DocumentProvider, w przeciwieństwie do FileProvider, publikuje pliki w SAF i umożliwia innym aplikacjom przeglądanie zawartości Twojej aplikacji jako części systemu plików. Aby zaimplementować DocumentsProvider, musisz zastąpić queryRoots(), queryChildDocuments() i openDocument(), a następnie zarejestrować go w AndroidManifest.xml
Synchronizacja w chmurze daje użytkownikom dostęp do dokumentów na wszystkich urządzeniach. System plików w aplikacjach mobilnych jest rozszerzony o warstwę chmurową: UIDocument na iOS automatycznie śledzi zmiany i synchronizuje je przez iCloud. Na Androidzie podobną funkcjonalność buduje się przez Google Drive API lub DocumentsProvider z korzeniami chmurowymi. Zrozumienie systemu plików w programowaniu mobilnym jest kluczowe dla budowania niezawodnej synchronizacji między urządzeniami.
UIDocument to abstrakcyjna klasa do pracy z dokumentami iCloud. Automatycznie zapisuje zmiany, odczytuje dane i powiadamia delegata o aktualizacjach. W przypadku konfliktu zapisu NSFileVersion udostępnia listę dostępnych wersji — deweloper może wybrać najnowszą lub pokazać użytkownikowi opcje rozwiązania konfliktu. Konfiguracja Ubiquity Container w Capabilities projektu jest obowiązkowa dla iCloud Drive. NSFileCoordinator i NSFilePresenter zapobiegają wyścigom danych podczas jednoczesnego dostępu z wielu wątków lub urządzeń.
iOS automatycznie uwzględnia katalog Documents w iCloud Backup. Android działa z Auto Backup for Apps — system zapisuje dane z getFilesDir(), SharedPreferences i baz SQLite do Google Drive. Pamięć podręczna i pliki zewnętrzne nie są uwzględniane w kopiach zapasowych. Obie platformy umożliwiają konfigurowanie wyjątków: na iOS przez NSURLIsExcludedFromBackupKey, na Androidzie przez konfigurację XML reguł tworzenia kopii zapasowych. Szyfrowanie plików z danymi osobowymi jest obowiązkowe — na iOS używaj NSDataWritingFileProtectionComplete, na Androidzie używaj EncryptedFile z biblioteki security-crypto.
| Parametr | iOS | Android |
|---|---|---|
| Domyślna kopia zapasowa | Documents i Library | getFilesDir(), SharedPreferences, BD |
| Wykluczanie plików | isExcludedFromBackupKey | Reguły XML kopii zapasowej (fullBackupContent) |
| Szyfrowanie | NSDataWritingFileProtectionComplete | EncryptedFile (security-crypto) |
| Sync w chmurze | UIDocument + iCloud | Google Drive API + SAF |
| Automatyczne przywracanie | iCloud Restore po instalacji | Auto Backup przy ponownej instalacji |
Często zadawane pytania
Sandbox to izolowane środowisko dla każdej aplikacji na iOS. Aplikacja nie może uzyskać dostępu do plików innych aplikacji bez użycia selektorów systemowych, takich jak UIDocumentPickerViewController.
Scoped Storage to model ograniczonego dostępu do systemu plików na Androidzie 10+. Aplikacja odczytuje bezpośrednio tylko własne pliki, używa MediaStore API dla multimediów i Storage Access Framework dla dokumentów.
Użyj UIDocumentPickerViewController — selektora systemowego do wybierania dokumentów z Files App lub iCloud Drive. Po wyborze otrzymasz URL z zakresem bezpieczeństwa i tymczasowym dostępem.
Dla plików multimedialnych użyj MediaStore API przez ContentResolver z określeniem typu MIME. Dla dowolnych dokumentów użyj Storage Access Framework z Intent ACTION_OPEN_DOCUMENT.
FileProvider to podklasa ContentProvider do bezpiecznego udostępniania plików między aplikacjami przez tymczasowe Uri content:// z FLAG_GRANT_READ_URI_PERMISSION.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.