Swift - 북마크(문서 접근 저장)
참고
UIDocumentBrowserViewController(documentation)
Providing access to directories(documentation)
bookmarkData(documentation)
What is the correct way to handle stale NSURL bookmarks?(StackOverflow)
URL Bookmarks: yes and no(Blog-krzyzanowskim)
About Apple File System(documentation)
Changes To App Containers In iOS 8(technotes)
서론
악보 앱을 만들다가, 다른 앱들처럼 pdf 파일들을 열어둔 상태를 유지하고 싶었다
그래서 url을 저장해 놓고, 앱을 다시 열 때 해당 문서들을 열고 상태를 복구하니까 잘 됐다
시뮬레이터에서는…
실제 기기에서 돌려보니까 파일을 전혀 찾지 못하는 문제가 있었다.
대체 왤까
이유
UIDocumentBrowserViewController
를 통해 문서를 열었다.- Documents 디렉토리의 모든 문서에 대한 액세스 권한을 부여받음
- 하지만 그렇게 문서를 열고 URL을 저장해 주고 앱을 껐다가, 다시 앱을 켜서 해당 URL로 문서를 열게 되면??
- ⇒ 올바르지 않은 방식으로 문서에 접근하려는 것
- ⇒ 현재 내 상황에서 접근할 수 없는 파일이므로 없는 파일로 인식, “파일을 찾을 수 없다”는 에러가 반환 됨
파일 참조 유지?? ⇒ Bookmark
bookmarkData(options:includingResourceValuesForKeys:relativeTo:)
위 메소드는 파일에 대한 지속적인 참조를 유지하는 책갈피를 반환한다.
즉 사용자가
- 파일을 이동하거나
- 이름을 바꿔거나
- 앱을 다시 시작하거나
- 시스템을 다시 시작해도
해당 파일을 가리킴.
액세스 권한 등의 추가 정보도 같이 저장할 수 있기 때문에, 이걸 사용하면 된다
let bookmarkData = try url.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil)
와 같이 .minimalBookmark
로 저장해 주면 됨
문서에서는 책갈피 만들기 전에 url.startAccessingSecurityScopedResource()
를 쓰던데, 현재 나는 UIDocumentBrowserViewController
를 사용하기 때문에 보안 범위가 아니다. 패스
UIDocumentBrowserViewController
- 로컬 및 클라우드에 저장한 문서를 탐색하고 작업을 수행하기 위한
ViewController
- 로컬 및 클라우드에 저장한 문서를 탐색하고 작업을 수행하기 위한
UIDocumentPickerViewController
- 앱의 샌드박스 외부에 있는 문서 또는 대상에 대한 액세스를 제공하기 위한
ViewController
- 앱의 샌드박스 외부에 있는 문서 또는 대상에 대한 액세스를 제공하기 위한
오래된 책갈피
그런데 책갈피가 파일을 이동하거나 이름을 바꾼 경우, 오래될 수도 있다
이럴 때는 갱신을 해줘야 함
do {
var isStale = false
var url = try URL(resolvingBookmarkData: bookmark, bookmarkDataIsStale: &isStale)
if isStale {
print("Bookmark(\(url.lastPathComponent)) is stale")
let updatedBookmark = try url.bookmarkData()
}
} catch let error {
print(error.localizedDescription)
}
이렇게 resolve해서 얻은 url로 한 번 더 북마크를 만들어 주면 된다.
갱신이 필요한 경우를 보면
- 이미 액세스 권한이 있는 디렉토리 안에서 이동/이름 바꾸기
- 갱신 성공
- 액세스 권한이 없는 폴더로 이동하기
- URL을 확인할 수 있지만, 책갈피에 액세스나 갱신은 실패
- 액세스 권한이 없는 폴더에 있는데 이름 바꾸기
- URL을 확인할 수 있지만, 책갈피에 액세스나 갱신은 실패
- 다른 볼륨으로 이동하기
- 갱신 실패
라고 함.
책갈피는
- 절대 경로
- iOS8부터 시스템이 재부팅되면 파일의 ID가 변경될 수 있음(보안을 위해) → 절대 경로는 매번 바뀔 수 있음
- 파일의 메타데이터인 inode
- 파일을 다른 곳으로 이동했을 때, 파일 시스템은 실제 데이터는 이동하지 않고 메타데이터를 업데이트함
두 정보를 조합해서 참조를 유지한다네요
파일 시스템 말 나온 김에 살짝 알아봄
APFS(Apple File System)
Clone으로 복사 비용 절감
try FileManager.default.copyItem(at: origin, to: destination)
clone은 디스크에서 추가 공간을 차지하지 않는 파일 또는 디렉토리의 복사본이다(같은 볼륨 내).
데이터 수정 사항은 다른 곳에 기록하고 두 파일 모두 수정되지 않은 블록을 계속 공유한다고 함
볼륨 간 여유 공간 공유
모든 볼륨은 독립적으로 확장/축소할 수 있고, 한 볼륨이 축소될 때 확보되는 공간은 다른 볼륨이 커질 때 사용 가능하다.
if let attributes = try? FileManager.default.attributesOfFileSystem(forPath: "/") {
let availableFreeSpace = attributes[.systemFreeSize]
}
이렇게 여유 공간을 알 수 있는데, 여기에도 당연히 공유하는 여유 공간이 포함됨
(따라서 각 볼륨의 사용 가능한 여유 공간을 다 더하면 파티션 내의 실제 총 여유 공간보다 클 수 있음)
스파스 파일 지원
스파스 파일(Sparse File)?
큰 파일이 있는데, 빈 바이트가 많은 경우 이걸 전부 저장하는 건 비효율적
→ 빈 부분을 설명하는 메타 데이터를 대신 저장하자
즉 파일 전체의 크기를 할당할 필요 없이, 실제 데이터가 존재하는 영역만 생성한다.
스파스 파일의 경우 빈 블록을 할당하지 않아 효율적으로 동작한다
만약에 파일 수정으로 빈 자리에 쓰게 된다면?? → 다른 블록에 써주고 읽을 때 알아서 순서 맞춤
굿
댓글남기기