FileManager 공부

iOS 상에서 파일 관련 작업을 해주는 FileManager에 대해 공부하려 한다.

FileManager란?

iOSmacOS에서 파일을 다룰 수 있게 해주는 클래스로 iOS 2.0 이상, macOS 10.0 이상부터 사용 가능하다. 파일 시스템과 상호작용하게끔 해주며 파일과 디렉토리의 위치 결정(locate), 생성(create), 복사(copy), 옮기기(move)가 가능하다. 물론 특정 파일이나 디렉토리의 정보를 얻어오는 것도 지원된다.

특정 파일의 위치를 명시할 때, Apple은 NSString 객체 대신 NSURL객체를 사용하는 것을 권장하는데, 이는 시스템 내부적으로 URL 객체가 경로 정보를 더 효율적인 표현으로 바꿔주기 때문이다.

파일이나 디렉토리를 옮기거나, 복사하거나, 링크하거나, 삭제할 때 FileManagerDelegate 프로토콜을 따르는 delegate를 사용할 수 있다. 이 delegateFileManager의 동작을 확인하거나, 에러가 일어났을 때 진행할 것인지를 결정한다.

FileManager 클래스는 iOS 5.0, macOS 10.7 이상부터 iCloud에 저장된 아이템들을 관리할 수 있게 되었다. iCloud의 파일들을 조작하면 iCloud와 동기화된 모든 기기의 파일들이 바뀌게 된다.

FileManagerDelegate에 대해

FileManager의 동작들을 관리하는 옵셔널한 메서드들을 가지고 있다. NSURLNSString 객체 둘 다를 받는 메서드들을 가지고 있지만 FileManager와 마찬가지로 NSURL 객체를 선호한다.

이것을 사용할 때 주의할 것은 직접 생성한 FileManager의 인스턴스와 연결해야 한다는 것이다. shared한 FileManager.default를 사용하면 안 된다.

FileManager의 동작 별로 다음과 같은 메서드들을 가지고 있다.

  • 동작을 시작해도 되는지 확인: should(동작)ItemAt
  • 에러가 일어난 뒤 지속할지 확인: shouldProceedAfterError

How to use

FileManager 클래스는 싱글턴 객체를 제공하며 여러 스레드에서 안전하게 동작이 가능하다. 다음과 같이 접근한다.

1
FileManager.default

그러나 위에서 언급했듯이 delegate를 사용해 move, copy, remove, link 동작들의 상태를 체크하려면 직접 FileManager 인스턴스를 만들어야 한다. 그리고 인스턴스에 delegate를 연결한다.

1
2
let fileManager = FileManager()
fileManager.delegate = self

위치 지정

FileManager의 인스턴스 메서드인 urls(for:in:)로 특정 디렉토리의 URL의 배열을 얻어올 수 있다. 이 메서드는 두 가지 인자를 가진다.

여기서 중요한 것은 도메인이 디렉토리를 포함해야 한다는 것이다.

반환되는 NSURL의 배열은 Domain Mask의 상수의 순서대로 정렬되어 있는데, 이는 System 도메인부터 User 도메인의 순서이다.

1
2
guard let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let directoryPath = documentDirectory.appendingPathComponent("AlpacaDirectory")

쓰기

디렉토리

FileManager의 인스턴스 메서드인 createDirectory(at:withIntermediateDirectories:attributes:)로 특정 URL에 새로운 디렉토리를 생성할 수 있다. createIntermediatesattributes 인자는 디렉토리의 생성과 관련된 설정을 하는 것이다.

물론 I/O 작업이기 때문에 실패할 경우 에러를 반환하는 메서드이다. try-catch 문으로 에러에 대한 처리를 해주거나 아래와 같이 옵셔널하게 처리한다.

1
try? fileManager.createDirectory(at: directoryPath, withIntermediateDirectories: false, attributes: nil)

파일

파일의 생성은 createFile(atPath:contents:attributes:)로 할 수 있지만 지금은 String 파일로 txt 파일을 만들어보려고 한다.

1
2
3
4
5
6
let textFilePath = directoryPath.appendingPathComponent("Alpaca.text")
do {
try "Alpaca".write(to: textFilePath, atomically: true, encoding: .utf8)
} catch {
print(error.localizedDescription)
}

위의 에에서는 String Protocolwrite(to:atomically:encoding:) 메서드로 위에서 지정한 textFilePath에 텍스트 파일을 만든다.

읽기

디렉토리

디렉토리 안에 있는 컨텐츠를 알아보는 메서드는 contentsOfDirectory(at:includingPropertiesForKeys:options:)로 컨텐츠들의 URL 배열을 반환한다.

1
2
3
4
if let documentsURL = try? fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) {
print(documentsURL)
// [file:///private/var/mobile/Containers/Data/Application/4608E3A0-D298-4177-B7F9-5A6A62ACD130/Documents/AlpacaDirectory/]
}

파일

파일은 파일 경로인 URL을 인자값으로 갖는 Data 타입의 초기화 메서드 init(contentsOf:options:)로 받을 수 있다.

다음 예제에서는 String 타입의 초기화 메서드 init(contentsOf:)로 파일 경로로부터 문자열을 읽어올 것이다.

1
2
3
guard let alpacaText = try? String(contentsOf: textFilePath) else { return }
print(alpacaText)
// Alpaca

Reference