[Xamarin] iOS 파일시스템

2016. 4. 7. 21:58Mobile

iOS의 파일시스템은 System.IO의 Class를 통해서 접근하실 수 있습니다. File Class를 통해 파일을 생성할 수 있으며, Directory를 통해 Directory를 탐색하실 수 있습니다. Stream을 통해서 파일 Data을 읽거나 쓸수도 있습니다.

App이 접근할 수 있는 파일과 폴더에 대한 제한이 있는데, 예를들어 Application Sandbox(Application이 파일, 네트워크, 하드웨어에 접근하는데 사용되는 접근제한 규칙)를 통해 다른 앱들이 접근하지 못하게 할 수 있습니다. Application이 설치된 폴더를 Home Directory라고 하는데 이 Home Directory에 대해서는 다른 앱들이 접근할 수 없습니다.

파일을 읽고 쓰는 것 외에도 파일의 백업과 업그레이드에 대한 특별한 기능을 제공하기도 하며, iTunes에 의해 공유되도록 할 수도 있습니다.

일반적인 파일접근

디렉터리 접근

디렉터리 접근은 System.IO.Directory Class를 통해 진행할 수 있습니다. 다음과 같이 App에서 “./”를 통한 접근은, Application이 설치된 Directory를 말합니다. 이 폴더의 파일을 살펴보면 배포된 App의 Contents와 실행파일이 있는 것을 알 수 있습니다.

var directories = Directory.EnumerateDirectories("./");
foreach (var directory in directories) 
{
   Console.WriteLine(directory);
}

파일 읽기

파일 읽기는 다음과 같이 간단히 진행할 수 있습니다.

var text = File.ReadAllText("TestData/ReadMe.txt");

XML Serialization

다음은 파일에 저장된 XML을 StreamReader를 통해 Deserialize하는 예제입니다.

using (TextReader reader = new StreamReader("./TestData/test.xml")) 
{
      XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
      var xml = (Account)serializer.Deserialize(reader);
}

XML로 Serializer하거나 Deserializer하는 Class는 iOS Linker에 의해서 제외되지 않도록 [Preserve] Attribute로 수식하는 것이 필요할 수 있습니다.

[Preserve]
public class Account
{
    public string Email { get; set; }
    public bool Active { get; set; }
    public DateTime CreatedDate { get; set; }
    public List<string> Roles { get; set; }

    public Account() 
	{

    }
}

File, Directory 생성하기

File을 통해서 파일을 쓸 수 있습니다. 다만 파일이 저장되는 경로는 반드시 접근권한이 있는 곳이어야 합니다.

var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); 
var directoryName = Path.Combine(documents, "SaveDir");
Directory.CreateDirectory(directoryName);

var filename = Path.Combine(directoryName, "Write.txt");
File.WriteAllText(filename, "저장할 내용");

고려사항

Build 후에 파일이 App에 포함되도록 하는 방법

Project에 파일을 포함하였다고 하더라도, Application Build 과정에서 해당 파일이 Application에 포함되지 않을 수 있습니다. 따라서 Build 후에도 해당파일이 포함되도록 하려면 Build Action을 Content로 지정해주세요.

대소문자 구분

Windows와 다르게 iOS파일 시스템은 대소문자를 구분합니다. iOS Simulator는 대소문자를 구분하지 않기 때문에, 테스트 때는 잘 동작하던 것이 앱스토어 배포 후에 문제가 될 수 도 있습니다. 이 것은 Simulator가 아닌 실제 장치에서 앱을 꼼꼼하게 테스트해야하는 이유이기도 합니다.

Path 구분자

iOS에서는 Path 구분자로 ‘/’를 사용합니다. (Windows에서는 역슬래쉬(‘\’)도 허용하지만 iOS는 지원하지 않습니다.)

Application Sandbox

Application은 파일시스템, 네트워크, 하드웨어 등의 자원에 대한 접근제약 규칙들이 있으며 이를 Application Sandbox라고 합니다. 파일시스템 측면만 보면 Application은 Home Directory에만 파일을 쓰거나 접근할 수 있습니다.

Home DIrectory란 Application이 설치되고 저장된 폴더입니다. 이 Home Directory의 위치는 개발자에 의해 수정될 수 없습니다.

Application Bundle

Application이 Build 되면 App파일이 생성되며 이 파일은 Device의 특정 폴더에 설치됩니다. 이를 Application Bundle라고 합니다. Application Bundle 폴더는 .app라는 접미사를 가진 것으로 구분할 수 있습니다. Application Bundle에는 실행파일, 이미지, 기타 실행에 필요한 파일들이 존재합니다.

Mac OS에서 이 폴더에 접근해보면 특수한 아이콘으로 나타나며 .app 확장자는 감추어져 나타나게 됩니다. 이 폴더의 내용은 Project/bin/Debug와 동일합니다.
Application Bundle 파일

.app으로 끝나는 Application bundle 아이콘을 마우스 오른쪽 클릭 후 View Package Contents를 클릭하면 해당 파일의 내용을 확인하실 수 있습니다.
Application Bundle 파일의 내용

Application Directory

Home Directory의 파일은 쓰거나 변경할 수 없습니다. 왜냐하면 Home Directory에 포함된 Application File들은 서명된 상태이며 이를 수정하는 것은 서명값이 달라 지도록 하는 것입니다. 이 경우 앱은 실행되지 않게 됩니다.

그럼에도 iOS 7 이하 버전은 Home DIrectyory에 파일을 쓰고 저장할 수 있었습니다. iOS 8부터는 Home Directory에 파일을 위치시킬 수 없습니다.

iOS에서는 사용자가 접근할 수 있는 특정한 폴더규칙을 제공하는데 다음과 같습니다.

Directory 설명
[ApplicationName].app/ ApplicationBundle DIrectory로써 실행파일이 저장된 곳입니다. 앱에 필요한 모든 파일과 이미지은 여기에 위치하게 됩니다. Application Bundle에 포함된 파일에 접근하려면 NSBundle.MainBundle.BundlePath Property를 통해 이 경로에 접근할 수 있습니다.
Documents/ 사용자의 문서나 Application의 Data는 여기에 저장해주세요. 여기에 저장된 파일은 iTunes에 의해서 공유될 수 있습니다. 이 기능은 기본적으로 Disabled되어 있으나 Info.plist에 UIFileSharingEnabled key로 true로 설정할 경우 활성화 되며 사용자는 이 파일에 접근할 수 있게 됩니다.

따라서 사용자에 의해 숨겨져야 하는 파일은 이 폴더에  저장하는 것을 피해야 합니다. 파일을 숨겨두는 경우 iTune에 의해서 이 파일이 노출되거나 수정되는 것을 막을 수 있습니다.

이 폴더의 경로는 Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)를 통해서 경로를 확인하실 수 있습니다.

이 폴더의 파일은 iTune에 의해서 백업되게 됩니다.
Library/ 이 폴더의 파일은 사용자에 의해 생성되지 않는 파일이 위치하기 좋은 곳입니다. 이곳에 위치한 DB나 다른 민감한 Application파일 iTune에 의해서 절대 노출되지 않을 것입니다.

이 폴더에는 System이 생성한 폴더(Preference, Caches 등)가 있을 수 도 있으니, 파일이나 폴더를 생성하기 전에 확인을 해야 합니다.

Caches 폴더를 제외하고는 iTunes에 의해서 자동으로 백업되게 됩니다.
Library/Preferences/ Application의 설정 파일은 여기에 저장됩니다. 이 폴더에 파일을 직접 생성하지 마시고, NSUserDefaults Class를 통해서 작업을 진행해주세요. 이 폴더의 내용은 iTunes에 의해 자동으로 백업됩니다.
Library/Caches/ Application 실행에 도움이 되는 파일들은 여기에 저장해주세요. 이 폴더의 파일들은 용량이 낮을 경우 삭제될 수 있습니다. 하지만 Application이 실행 중인 경우에는 어떤 경우에도 삭제되지 않습니다.

이 폴더의 파일은 iTunes에 의해서 백업되지 않습니다.

이 폴더의 사용 예는 네트워크에 접근할 수 없을 때, 일시적으로 파일을 여기에 저장해 둔 뒤에 추후 네트워크가 연결되면 이 파일을 통해 복원하는 경우가 있습니다.
tmp/ 아주 잠시 동안 임시적으로 생성되는 파일 폴더라면 여기에 저장해주세요. Application이 실행 중이 아니라면 iOS는 이 폴더에 포함된 파일을 삭제할 수 있습니다.

이 폴더는 iTunes에 의해 백업되지 않습니다.

이 폴더의 사용 예는 임시적으로 인터넷에서 다운로드 받은 Twitter의 인물사진처럼 일시적으로 저장이 필요한 파일 등이 있습니다.

Finder에서 나타나는 Application Directory는 아래와 같습니다.
Application Directory Structure

프로그래밍을 통해 폴더에 접근하기

앞서서 설명한 예제들의 파일들은 Environment.SpecialFolder.MyDocuments에 의해서 접근하실 수 있습니다. 아래는 “..”를 통해 상위 디렉터리로 이동하여 Library, tmp 폴더에 접근하는 예입니다.

var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var library = Path.Combine(documents, "..", "Library");
var filename = Path.Combine(library, "WriteToLibrary.txt");
File.WriteAllText(filename, "Write this text into a file in Library");

iTunes로 사용자 파일 공유하기

Info.plist에서 Application that supports iTunes sharing (UIFileSharingEnabled) 값을 Yes(true)로 설정하면 Documents 폴더의 내용은 사용자가 iTunes로 접근할 수 있게 됩니다.

이 파일들은 Device가 시스템에 연결되었을 때 iTunes를 통해서 접근할 수 있게 됩니다. 그러나 사용자는 최상위 폴더만 접근하실 수 있습니다. 하위 폴더에는 접근할 수 없습니다. 그러나 사용자는 이러한 하위 폴더를 다른 곳에 복사하거나 여전히 삭제할 수도 있습니다.

만약 PDF나 EPUB파일을 여기에 저장해둘 경우, iTunes에 의해 사용자의 모든 iOS기기들에 의해서 이 파일을 공유하게 됩니다.

Info.plist의 UIFileSharingEnabled 값이 없을 경우 기본적으로 false로 인식합니다.

iCloud의 Backup 제약사항

iCloud Backup 기능은 iOS5에 소개되었습니다. 위에서 언급한 예외를 제외하고 Application의 Home Directry의 하위 파일들은 iCloud Server로 백업되게 됩니다.

그러나 iCloud Server에서 제공하는 무료 용량은 5GB이고, 불필요하게 용량이 큰 파일들이 Network를 통해서 전송되지 않도록 하기 위해서 Application은 꼭 필요한 파일만을 백업되게 해야 합니다. Apple는 큰 파일을 저장하는 Applciation에게 메일을 보내 꼭 필요한 파일만을 저장하도록 요구하기도 합니다. iOS Data Storage Guideline은 다음과 같이 제한하고 있습니다.

  • 사용자가 생성한 정보, 혹은 재생성할 수 없는 정보만을 Documents 폴더에 저장하여 백업되도록 해주세요.
  • 쉽게 재생성하거나 다운로드할 수 있는 파일은 Library/caches나 tmp에 저장해주세요.
  • Tmp나 Caches폴더의 파일은 iOS에 의해서 삭제될 수 있기 때문에, 만약 삭제되지 않기를 원하는 파일은 ‘Library/하위폴더명’에다가 저장하신 후, Do not back up’ 속성을 적용해 주세요. 그러면 iTunes에 의해서 백업되지 않습니다.

“Do not back up”속성은 Foundation.NSFileManager  class를 통해서 진행할 수 있습니다. SetSkipBackupAttribute Method에 해당 경로를 지정해주세요.

var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(documents, "LocalOnly.txt");
File.WriteAllText(filename, "This file will never get backed-up. It would need to be re-created after a restore or re-install");
NSFileManager.SetSkipBackupAttribute(filename, true); // backup will be skipped for this file

위와 같이 true와 함께 경로를 넘겨주게 되면 해당 폴더는 더 이상 iTuens에 의해 백업되지 않게 됩니다. 심지어 Documents 폴더도 백업되지 않게 할 수 있으며, false를 통해서 다시 해제할 수 도 있습니다. GetSkipBackupAttribute() Method를 사용하시면 이 속성이 지정된 폴더만을 확인하실 수 도 있습니다.

공유 데이터

Application Home Directory 외부에 공유폴더를 생성하여 접근하실 수 있습니다. 여러 App들은 이 폴더를 통해서 데이터를 공유하실 수 있습니다. 이는 App Group이라는 기능을 통해서 지원됩니다.

App Group 설정하기

App간에 공유할 수 있는 공유폴더는 App Group을 통해서 가능합니다. 이는 iOS개발자 센터의 Certificates, Identifiers & Profiles 섹션에서 설정하실 수 있습니다. 또한 폴더를 공유하는 프로젝트 간의 Entitlements.plist에도 설정이 추가되어야 합니다.

Provisioning

iOS개발자 센터 홈페이지에서 아래와 같이 AppGroup 설정을 추가해 합니다. App Group은 유일한 식별자를 가지게 되는데, 보통은 group. 접미사와 함께 Bundle ID를 조합하여 생성합니다. 예를들어 Bundle ID가 com.xamarin.WatchSettings라면 group.com.xamarin.WatchSettings가 Group Id가 되게 됩니다.
Regeistering an App Group

Entitlements.plist

Entitlements.plit에 아래와 같이 Enable App Groups를 체크하신 후 App Group Id를 입력해주세요.
Enable app Groups 설정

Files

설정이 끝나면, 이제 파일을 공유해봅시다.

var FileManager = new NSFileManager();
var appGroupContainer = FileManager.GetContainerUrl("group.com.xamarin.WatchSettings");
var appGroupContainerPath = appGroupContainer.Path;

Console.WriteLine("Group Path: " + appGroupContainerPath);

만약 Group이 null값으로 반환되면, 설정이 제대로 되지 않은 것이니 다시 확인해보세요.

App Update

App이 업데이트 되면 iOS는 새로운 Application Bundle 폴더를 생성하고 업데이트 된 App을 설치합니다. 그리고 과거 App의 폴더에서 다음 폴더를 그대로 옮겨 옵니다.

  • Documents
  • Library

Home Directory의 다른 파일들도 복사될 수 있으나, 반드시 복사된다는 보장이 없습니다.