Swift - 북마크(문서 접근 저장)

2 분 소요


참고

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:)

위 메소드는 파일에 대한 지속적인 참조를 유지하는 책갈피를 반환한다.

즉 사용자가

  1. 파일을 이동하거나
  2. 이름을 바꿔거나
  3. 앱을 다시 시작하거나
  4. 시스템을 다시 시작해도

해당 파일을 가리킴.
액세스 권한 등의 추가 정보도 같이 저장할 수 있기 때문에, 이걸 사용하면 된다

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로 한 번 더 북마크를 만들어 주면 된다.

갱신이 필요한 경우를 보면

  1. 이미 액세스 권한이 있는 디렉토리 안에서 이동/이름 바꾸기
    • 갱신 성공
  2. 액세스 권한이 없는 폴더로 이동하기
    • URL을 확인할 수 있지만, 책갈피에 액세스나 갱신은 실패
  3. 액세스 권한이 없는 폴더에 있는데 이름 바꾸기
    • URL을 확인할 수 있지만, 책갈피에 액세스나 갱신은 실패
  4. 다른 볼륨으로 이동하기
    • 갱신 실패

라고 함.

책갈피는

  1. 절대 경로
    • iOS8부터 시스템이 재부팅되면 파일의 ID가 변경될 수 있음(보안을 위해) → 절대 경로는 매번 바뀔 수 있음
  2. 파일의 메타데이터인 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)?

큰 파일이 있는데, 빈 바이트가 많은 경우 이걸 전부 저장하는 건 비효율적
→ 빈 부분을 설명하는 메타 데이터를 대신 저장하자
즉 파일 전체의 크기를 할당할 필요 없이, 실제 데이터가 존재하는 영역만 생성한다.

스파스 파일의 경우 빈 블록을 할당하지 않아 효율적으로 동작한다
만약에 파일 수정으로 빈 자리에 쓰게 된다면?? → 다른 블록에 써주고 읽을 때 알아서 순서 맞춤



굿


태그: ,

카테고리:

업데이트:

댓글남기기