다음을 통해 공유


Xamarin.iOS의 문서 선택기

문서 선택기를 사용하면 앱 간에 문서를 공유할 수 있습니다. 이러한 문서는 iCloud 또는 다른 앱의 디렉터리에 저장될 수 있습니다. 문서는 사용자가 디바이스에 설치한 문서 공급자 확장 집합을 통해 공유됩니다.

앱과 클라우드 간에 문서를 동기화하는 것이 어렵기 때문에 필요한 복잡성이 일정하게 발생합니다.

요구 사항

이 문서에 제시된 단계를 완료하려면 다음이 필요합니다.

  • Xcode 7 및 iOS 8 이상 – Apple의 Xcode 7 및 iOS 8 이상 API를 설치하고 개발자 컴퓨터에 구성해야 합니다.
  • Visual Studio 또는 Mac용 Visual Studio – 최신 버전의 Mac용 Visual Studio 설치해야 합니다.
  • iOS 디바이스 – iOS 8 이상을 실행하는 iOS 디바이스입니다.

iCloud 변경 내용

문서 선택기의 새로운 기능을 구현하기 위해 Apple의 iCloud 서비스가 다음과 같이 변경되었습니다.

  • iCloud 디먼은 CloudKit를 사용하여 완전히 다시 작성되었습니다.
  • 기존 iCloud 기능의 이름이 iCloud 드라이브로 바뀌었습니다.
  • Microsoft Windows OS에 대한 지원이 iCloud에 추가되었습니다.
  • Mac OS Finder에 iCloud 폴더가 추가되었습니다.
  • iOS 디바이스는 Mac OS iCloud 폴더의 콘텐츠에 액세스할 수 있습니다.

Important

Apple에서는 개발자가 유럽 연합의 GDPR(일반 데이터 보호 규정)을 제대로 처리하는 데 도움이 되는 도구를 제공합니다.

문서가란?

iCloud에서 문서를 참조하는 경우 단일 독립 실행형 엔터티이며 사용자가 인식해야 합니다. 사용자가 문서를 수정하거나 다른 사용자와 공유할 수 있습니다(예: 전자 메일 사용).

페이지, 키노트 또는 숫자 파일과 같이 사용자가 문서로 즉시 인식할 수 있는 여러 형식의 파일이 있습니다. 그러나 iCloud는 이 개념에 국한되지 않습니다. 예를 들어 게임의 상태(예: 체스 일치)는 문서로 처리되고 iCloud에 저장될 수 있습니다. 이 파일은 사용자의 디바이스 간에 전달될 수 있으며 다른 장치에서 중단된 게임을 선택할 수 있습니다.

문서 처리

Xamarin에서 문서 선택을 사용하는 데 필요한 코드를 살펴보기 전에 이 문서에서는 iCloud 문서 작업에 대한 모범 사례와 문서 선택기를 지원하는 데 필요한 기존 API에 대한 몇 가지 수정 사항을 설명합니다.

파일 조정 사용

여러 위치에서 파일을 수정할 수 있으므로 데이터 손실을 방지하기 위해 조정을 사용해야 합니다.

파일 조정 사용

위의 그림을 살펴보겠습니다.

  1. 파일 조정을 사용하는 iOS 디바이스는 새 문서를 만들어 iCloud 폴더에 저장합니다.
  2. iCloud는 모든 디바이스에 배포하기 위해 수정된 파일을 클라우드에 저장합니다.
  3. 연결된 Mac은 iCloud 폴더에서 수정된 파일을 보고 파일 조정을 사용하여 변경 내용을 파일에 복사합니다.
  4. 파일 조정을 사용하지 않는 디바이스는 파일을 변경하고 iCloud 폴더에 저장합니다. 이러한 변경 내용은 즉시 다른 디바이스에 복제본(replica).

원래 iOS 디바이스 또는 Mac이 파일을 편집했다고 가정합니다. 이제 변경 내용이 손실되고 정렬되지 않은 디바이스에서 파일 버전으로 덮어씁니다. 데이터 손실을 방지하기 위해 클라우드 기반 문서를 사용할 때는 파일 조정이 필수입니다.

UIDocument 사용

UIDocument 는 개발자를 위해 많은 작업을 수행하여 간단하게(또는 NSDocument macOS에서) 작업을 수행합니다. 애플리케이션의 UI를 차단하지 않도록 백그라운드 큐가 포함된 기본 제공 파일 조정을 제공합니다.

UIDocument 는 개발자가 필요로 하는 모든 목적을 위해 Xamarin 애플리케이션의 개발 노력을 용이하게 하는 여러 개 높은 수준의 API를 노출합니다.

다음 코드는 iCloud에서 텍스트를 저장하고 검색하는 데 사용할 수 있는 일반 텍스트 기반 문서를 구현하기 위한 하위 클래스 UIDocument 를 만듭니다.

using System;
using Foundation;
using UIKit;

namespace DocPicker
{
    public class GenericTextDocument : UIDocument
    {
        #region Private Variable Storage
        private NSString _dataModel;
        #endregion

        #region Computed Properties
        public string Contents {
            get { return _dataModel.ToString (); }
            set { _dataModel = new NSString(value); }
        }
        #endregion

        #region Constructors
        public GenericTextDocument (NSUrl url) : base (url)
        {
            // Set the default document text
            this.Contents = "";
        }

        public GenericTextDocument (NSUrl url, string contents) : base (url)
        {
            // Set the default document text
            this.Contents = contents;
        }
        #endregion

        #region Override Methods
        public override bool LoadFromContents (NSObject contents, string typeName, out NSError outError)
        {
            // Clear the error state
            outError = null;

            // Were any contents passed to the document?
            if (contents != null) {
                _dataModel = NSString.FromData( (NSData)contents, NSStringEncoding.UTF8 );
            }

            // Inform caller that the document has been modified
            RaiseDocumentModified (this);

            // Return success
            return true;
        }

        public override NSObject ContentsForType (string typeName, out NSError outError)
        {
            // Clear the error state
            outError = null;

            // Convert the contents to a NSData object and return it
            NSData docData = _dataModel.Encode(NSStringEncoding.UTF8);
            return docData;
        }
        #endregion

        #region Events
        public delegate void DocumentModifiedDelegate(GenericTextDocument document);
        public event DocumentModifiedDelegate DocumentModified;

        internal void RaiseDocumentModified(GenericTextDocument document) {
            // Inform caller
            if (this.DocumentModified != null) {
                this.DocumentModified (document);
            }
        }
        #endregion
    }
}

위에서 설명한 클래스는 GenericTextDocument Xamarin.iOS 8 애플리케이션에서 문서 선택기 및 외부 문서로 작업할 때 이 문서 전체에서 사용됩니다.

비동기 파일 조정

iOS 8은 새로운 파일 조정 API를 통해 몇 가지 새로운 비동기 파일 조정 기능을 제공합니다. iOS 8 이전에는 모든 기존 파일 조정 API가 완전히 동기식이었습니다. 즉, 개발자는 파일 조정이 애플리케이션의 UI를 차단하지 못하도록 자체 백그라운드 큐를 구현할 책임이 있습니다.

NSFileAccessIntent 클래스에는 파일을 가리키는 URL과 필요한 조정 유형을 제어하는 몇 가지 옵션이 포함되어 있습니다. 다음 코드에서는 의도를 사용하여 한 위치에서 다른 위치로 파일을 이동하는 방법을 보여 줍니다.

// Get source options
var srcURL = NSUrl.FromFilename ("FromFile.txt");
var srcIntent = NSFileAccessIntent.CreateReadingIntent (srcURL, NSFileCoordinatorReadingOptions.ForUploading);

// Get destination options
var dstURL = NSUrl.FromFilename ("ToFile.txt");
var dstIntent = NSFileAccessIntent.CreateReadingIntent (dstURL, NSFileCoordinatorReadingOptions.ForUploading);

// Create an array
var intents = new NSFileAccessIntent[] {
    srcIntent,
    dstIntent
};

// Initialize a file coordination with intents
var queue = new NSOperationQueue ();
var fileCoordinator = new NSFileCoordinator ();
fileCoordinator.CoordinateAccess (intents, queue, (err) => {
    // Was there an error?
    if (err!=null) {
        Console.WriteLine("Error: {0}",err.LocalizedDescription);
    }
});

문서 검색 및 나열

문서를 검색하고 나열하는 방법은 기존 NSMetadataQuery API를 사용하는 것입니다. 이 섹션에서는 문서 작업을 이전보다 훨씬 쉽게 수행할 수 있도록 추가 NSMetadataQuery 된 새로운 기능에 대해 설명합니다.

기존 동작

iOS 8 이전에는 삭제, NSMetadataQuery 만들기 및 이름 바꾸기와 같은 로컬 파일 변경 내용의 픽업 속도가 느렸습니다.

NSMetadataQuery 로컬 파일 변경 개요

위의 다이어그램:

  1. 애플리케이션 컨테이너 NSMetadataQuery 에 이미 있는 파일의 경우 애플리케이션에서 즉시 사용할 수 있도록 기존 NSMetadata 레코드가 미리 만들어지고 스풀링됩니다.
  2. 애플리케이션은 애플리케이션 컨테이너에 새 파일을 만듭니다.
  3. 애플리케이션 컨테이너에 대한 수정 사항을 확인하고 필요한 NSMetadata 레코드를 만들기 전에 NSMetadataQuery 지연이 발생합니다.

레코드 만들기 NSMetadata 가 지연되었기 때문에 애플리케이션에는 두 개의 데이터 원본이 열려 있어야 했습니다. 하나는 로컬 파일 변경용이고 다른 하나는 클라우드 기반 변경용입니다.

바느질

iOS 8 NSMetadataQuery 에서는 스티칭이라는 새 기능과 함께 직접 사용하기가 더 쉽습니다.

스티칭이라는 새로운 기능이 있는 NSMetadataQuery

위의 다이어그램에서 연결 사용:

  1. 이전과 마찬가지로 애플리케이션 컨테이너 NSMetadataQuery 에 이미 있는 파일의 경우 기존 NSMetadata 레코드가 미리 만들어지고 스풀링됩니다.
  2. 애플리케이션은 파일 조정을 사용하여 애플리케이션 컨테이너에 새 파일을 만듭니다.
  3. 애플리케이션 컨테이너의 후크는 필요한 NSMetadata 레코드를 만들기 위한 수정 및 호출 NSMetadataQuery 을 확인합니다.
  4. 레코드는 NSMetadata 파일 바로 후에 만들어지고 애플리케이션에서 사용할 수 있게 됩니다.

스티치를 사용하면 더 이상 로컬 및 클라우드 기반 파일 변경 내용을 모니터링하기 위해 데이터 원본을 열 필요가 없습니다. 이제 애플리케이션이 직접 사용할 NSMetadataQuery 수 있습니다.

Important

연결은 애플리케이션이 위의 섹션에 설명된 대로 파일 조정을 사용하는 경우에만 작동합니다. 파일 조정을 사용하지 않는 경우 API는 기본적으로 기존 iOS 8 이전 동작으로 설정됩니다.

새 iOS 8 메타데이터 기능

iOS 8에는 다음과 같은 새로운 기능이 추가 NSMetadataQuery 되었습니다.

  • NSMetatadataQuery 이제 클라우드에 저장된 로컬이 아닌 문서를 나열할 수 있습니다.
  • 클라우드 기반 문서의 메타데이터 정보에 액세스하기 위해 새 API가 추가되었습니다.
  • 로컬에서 콘텐츠를 사용할 수 있거나 사용할 수 없는 파일의 파일 특성에 액세스하는 새 NSUrl_PromisedItems API가 있습니다.
  • 메서드를 GetPromisedItemResourceValue 사용하여 지정된 파일에 대한 정보를 얻거나 메서드를 GetPromisedItemResourceValues 사용하여 한 번에 둘 이상의 파일에 대한 정보를 가져옵니다.

메타데이터를 처리하기 위해 두 개의 새 파일 조정 플래그가 추가되었습니다.

  • NSFileCoordinatorReadImmediatelyAvailableMetadataOnly
  • NSFileCoordinatorWriteContentIndependentMetadataOnly

위의 플래그를 사용하면 문서 파일의 내용을 로컬로 사용할 필요가 없습니다.

다음 코드 세그먼트는 특정 파일이 있는지 쿼리하고 파일이 없는 경우 파일을 빌드하는 데 사용하는 NSMetadataQuery 방법을 보여줍니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Foundation;
using UIKit;
using ObjCRuntime;
using System.IO;

#region Static Properties
public const string TestFilename = "test.txt";
#endregion

#region Computed Properties
public bool HasiCloud { get; set; }
public bool CheckingForiCloud { get; set; }
public NSUrl iCloudUrl { get; set; }

public GenericTextDocument Document { get; set; }
public NSMetadataQuery Query { get; set; }
#endregion

#region Private Methods
private void FindDocument () {
    Console.WriteLine ("Finding Document...");

    // Create a new query and set it's scope
    Query = new NSMetadataQuery();
    Query.SearchScopes = new NSObject [] {
                NSMetadataQuery.UbiquitousDocumentsScope,
                NSMetadataQuery.UbiquitousDataScope,
                NSMetadataQuery.AccessibleUbiquitousExternalDocumentsScope
            };

    // Build a predicate to locate the file by name and attach it to the query
    var pred = NSPredicate.FromFormat ("%K == %@"
        , new NSObject[] {
            NSMetadataQuery.ItemFSNameKey
            , new NSString(TestFilename)});
    Query.Predicate = pred;

    // Register a notification for when the query returns
    NSNotificationCenter.DefaultCenter.AddObserver (this,
            new Selector("queryDidFinishGathering:"),             NSMetadataQuery.DidFinishGatheringNotification,
            Query);

    // Start looking for the file
    Query.StartQuery ();
    Console.WriteLine ("Querying: {0}", Query.IsGathering);
}

[Export("queryDidFinishGathering:")]
public void DidFinishGathering (NSNotification notification) {
    Console.WriteLine ("Finish Gathering Documents.");

    // Access the query and stop it from running
    var query = (NSMetadataQuery)notification.Object;
    query.DisableUpdates();
    query.StopQuery();

    // Release the notification
    NSNotificationCenter.DefaultCenter.RemoveObserver (this
        , NSMetadataQuery.DidFinishGatheringNotification
        , query);

    // Load the document that the query returned
    LoadDocument(query);
}

private void LoadDocument (NSMetadataQuery query) {
    Console.WriteLine ("Loading Document...");    

    // Take action based on the returned record count
    switch (query.ResultCount) {
    case 0:
        // Create a new document
        CreateNewDocument ();
        break;
    case 1:
        // Gain access to the url and create a new document from
        // that instance
        NSMetadataItem item = (NSMetadataItem)query.ResultAtIndex (0);
        var url = (NSUrl)item.ValueForAttribute (NSMetadataQuery.ItemURLKey);

        // Load the document
        OpenDocument (url);
        break;
    default:
        // There has been an issue
        Console.WriteLine ("Issue: More than one document found...");
        break;
    }
}
#endregion

#region Public Methods
public void OpenDocument(NSUrl url) {

    Console.WriteLine ("Attempting to open: {0}", url);
    Document = new GenericTextDocument (url);

    // Open the document
    Document.Open ( (success) => {
        if (success) {
            Console.WriteLine ("Document Opened");
        } else
            Console.WriteLine ("Failed to Open Document");
    });

    // Inform caller
    RaiseDocumentLoaded (Document);
}

public void CreateNewDocument() {
    // Create path to new file
    // var docsFolder = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
    var docsFolder = Path.Combine(iCloudUrl.Path, "Documents");
    var docPath = Path.Combine (docsFolder, TestFilename);
    var ubiq = new NSUrl (docPath, false);

    // Create new document at path
    Console.WriteLine ("Creating Document at:" + ubiq.AbsoluteString);
    Document = new GenericTextDocument (ubiq);

    // Set the default value
    Document.Contents = "(default value)";

    // Save document to path
    Document.Save (Document.FileUrl, UIDocumentSaveOperation.ForCreating, (saveSuccess) => {
        Console.WriteLine ("Save completion:" + saveSuccess);
        if (saveSuccess) {
            Console.WriteLine ("Document Saved");
        } else {
            Console.WriteLine ("Unable to Save Document");
        }
    });

    // Inform caller
    RaiseDocumentLoaded (Document);
}

public bool SaveDocument() {
    bool successful = false;

    // Save document to path
    Document.Save (Document.FileUrl, UIDocumentSaveOperation.ForOverwriting, (saveSuccess) => {
        Console.WriteLine ("Save completion: " + saveSuccess);
        if (saveSuccess) {
            Console.WriteLine ("Document Saved");
            successful = true;
        } else {
            Console.WriteLine ("Unable to Save Document");
            successful=false;
        }
    });

    // Return results
    return successful;
}
#endregion

#region Events
public delegate void DocumentLoadedDelegate(GenericTextDocument document);
public event DocumentLoadedDelegate DocumentLoaded;

internal void RaiseDocumentLoaded(GenericTextDocument document) {
    // Inform caller
    if (this.DocumentLoaded != null) {
        this.DocumentLoaded (document);
    }
}
#endregion

문서 축소판 그림

Apple은 애플리케이션에 대한 문서를 나열할 때 최상의 사용자 환경은 미리 보기를 사용하는 것이라고 생각합니다. 이렇게 하면 최종 사용자에게 컨텍스트가 제공되므로 작업하려는 문서를 빠르게 식별할 수 있습니다.

iOS 8 이전에는 문서 미리 보기를 표시하려면 사용자 지정 구현이 필요했습니다. iOS 8은 개발자가 문서 축소판 그림으로 빠르게 작업할 수 있도록 하는 파일 시스템 특성입니다.

문서 축소판 그림 검색

또는 GetPromisedItemResourceValues 메서드를 GetPromisedItemResourceValueNSUrl_PromisedItems 호출하면 API, aNSUrlThumbnailDictionary가 반환됩니다. 현재 이 사전에 있는 유일한 키는 일치하는 키와 일치하는 UIImage키뿐입니다NSThumbnial1024X1024SizeKey.

문서 축소판 그림 저장

썸네일을 저장하는 가장 쉬운 방법은 을 사용하는 UIDocument것입니다. 썸네일의 메서드를 UIDocument 호출 GetFileAttributesToWrite 하고 설정하면 문서 파일이 있을 때 자동으로 저장됩니다. iCloud 디먼은 이 변경 내용을 보고 iCloud에 전파합니다. Mac OS X에서는 빠른 보기 플러그 인을 통해 개발자를 위해 미리 보기가 자동으로 생성됩니다.

iCloud 기반 문서 작업의 기본 사항과 기존 API 수정 사항으로 Xamarin iOS 8 모바일 애플리케이션에서 문서 선택기 보기 컨트롤러를 구현할 준비가 완료되었습니다.

Xamarin에서 iCloud 사용

Xamarin.iOS 애플리케이션에서 문서 선택을 사용하려면 애플리케이션과 Apple을 통해 iCloud 지원을 사용하도록 설정해야 합니다.

다음 단계에서는 iCloud에 대한 프로비전 프로세스를 연습합니다.

  1. iCloud 컨테이너를 만듭니다.
  2. iCloud App Service를 포함하는 앱 ID를 만듭니다.
  3. 이 앱 ID를 포함하는 프로비저닝 프로필을 만듭니다.

기능 작업 가이드는 처음 두 단계를 안내합니다. 프로비저닝 프로필을 만들려면 프로비전 프로필 가이드의 단계를 따릅니다.

다음 단계에서는 iCloud용 애플리케이션을 구성하는 프로세스를 연습합니다.

다음을 수행하십시오:

  1. Mac용 Visual Studio 또는 Visual Studio에서 프로젝트를 엽니다.

  2. 솔루션 탐색기 프로젝트를 마우스 오른쪽 단추로 클릭하고 옵션을 선택합니다.

  3. 옵션 대화 상자에서 iOS 애플리케이션을 선택하고 번들 식별자가 애플리케이션에 대해 위에서 만든 앱 ID정의된 것과 일치하는지 확인합니다.

  4. iOS 번들 서명을 선택하고, 위에서 만든 개발자 ID프로비저닝 프로필을 선택합니다.

  5. 확인 단추를 클릭하여 변경 내용을 저장하고 대화 상자를 닫습니다.

  6. 솔루션 탐색기 마우스 오른쪽 단추를 클릭하여 Entitlements.plist편집기에서 엽니다.

    Important

    Visual Studio에서 권한 편집기를 마우스 오른쪽 단추로 클릭하고 [시작]선택하고 속성 목록 편집기를 선택하여 권한 편집기를 열어야 할 수 있습니다.

  7. iCloud, iCloud 문서, 키-값 스토리지CloudKit 사용 확인

  8. 컨테이너가 애플리케이션에 대해 존재하는지 확인합니다(위에서 만든 대로). 예: iCloud.com.your-company.AppName

  9. 변경 내용을 파일에 저장합니다.

자격에 대한 자세한 내용은 권한 사용 가이드를 참조 하세요 .

위의 설정이 적용되면 애플리케이션은 이제 클라우드 기반 문서 및 새 문서 선택기 뷰 컨트롤러를 사용할 수 있습니다.

일반 설정 코드

문서 선택 보기 컨트롤러를 시작하기 전에 몇 가지 표준 설정 코드가 필요합니다. 먼저 애플리케이션의 AppDelegate.cs 파일을 수정하고 다음과 같이 표시합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Foundation;
using UIKit;
using ObjCRuntime;
using System.IO;

namespace DocPicker
{

    [Register ("AppDelegate")]
    public partial class AppDelegate : UIApplicationDelegate
    {
        #region Static Properties
        public const string TestFilename = "test.txt";
        #endregion

        #region Computed Properties
        public override UIWindow Window { get; set; }
        public bool HasiCloud { get; set; }
        public bool CheckingForiCloud { get; set; }
        public NSUrl iCloudUrl { get; set; }

        public GenericTextDocument Document { get; set; }
        public NSMetadataQuery Query { get; set; }
        public NSData Bookmark { get; set; }
        #endregion

        #region Private Methods
        private void FindDocument () {
            Console.WriteLine ("Finding Document...");

            // Create a new query and set it's scope
            Query = new NSMetadataQuery();
            Query.SearchScopes = new NSObject [] {
                NSMetadataQuery.UbiquitousDocumentsScope,
                NSMetadataQuery.UbiquitousDataScope,
                NSMetadataQuery.AccessibleUbiquitousExternalDocumentsScope
            };

            // Build a predicate to locate the file by name and attach it to the query
            var pred = NSPredicate.FromFormat ("%K == %@",
                 new NSObject[] {NSMetadataQuery.ItemFSNameKey
                , new NSString(TestFilename)});
            Query.Predicate = pred;

            // Register a notification for when the query returns
            NSNotificationCenter.DefaultCenter.AddObserver (this
                , new Selector("queryDidFinishGathering:")
                , NSMetadataQuery.DidFinishGatheringNotification
                , Query);

            // Start looking for the file
            Query.StartQuery ();
            Console.WriteLine ("Querying: {0}", Query.IsGathering);
        }

        [Export("queryDidFinishGathering:")]
        public void DidFinishGathering (NSNotification notification) {
            Console.WriteLine ("Finish Gathering Documents.");

            // Access the query and stop it from running
            var query = (NSMetadataQuery)notification.Object;
            query.DisableUpdates();
            query.StopQuery();

            // Release the notification
            NSNotificationCenter.DefaultCenter.RemoveObserver (this
                , NSMetadataQuery.DidFinishGatheringNotification
                , query);

            // Load the document that the query returned
            LoadDocument(query);
        }

        private void LoadDocument (NSMetadataQuery query) {
            Console.WriteLine ("Loading Document...");    

            // Take action based on the returned record count
            switch (query.ResultCount) {
            case 0:
                // Create a new document
                CreateNewDocument ();
                break;
            case 1:
                // Gain access to the url and create a new document from
                // that instance
                NSMetadataItem item = (NSMetadataItem)query.ResultAtIndex (0);
                var url = (NSUrl)item.ValueForAttribute (NSMetadataQuery.ItemURLKey);

                // Load the document
                OpenDocument (url);
                break;
            default:
                // There has been an issue
                Console.WriteLine ("Issue: More than one document found...");
                break;
            }
        }
        #endregion

        #region Public Methods

        public void OpenDocument(NSUrl url) {

            Console.WriteLine ("Attempting to open: {0}", url);
            Document = new GenericTextDocument (url);

            // Open the document
            Document.Open ( (success) => {
                if (success) {
                    Console.WriteLine ("Document Opened");
                } else
                    Console.WriteLine ("Failed to Open Document");
            });

            // Inform caller
            RaiseDocumentLoaded (Document);
        }

        public void CreateNewDocument() {
            // Create path to new file
            // var docsFolder = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
            var docsFolder = Path.Combine(iCloudUrl.Path, "Documents");
            var docPath = Path.Combine (docsFolder, TestFilename);
            var ubiq = new NSUrl (docPath, false);

            // Create new document at path
            Console.WriteLine ("Creating Document at:" + ubiq.AbsoluteString);
            Document = new GenericTextDocument (ubiq);

            // Set the default value
            Document.Contents = "(default value)";

            // Save document to path
            Document.Save (Document.FileUrl, UIDocumentSaveOperation.ForCreating, (saveSuccess) => {
                Console.WriteLine ("Save completion:" + saveSuccess);
                if (saveSuccess) {
                    Console.WriteLine ("Document Saved");
                } else {
                    Console.WriteLine ("Unable to Save Document");
                }
            });

            // Inform caller
            RaiseDocumentLoaded (Document);
        }

        /// <summary>
        /// Saves the document.
        /// </summary>
        /// <returns><c>true</c>, if document was saved, <c>false</c> otherwise.</returns>
        public bool SaveDocument() {
            bool successful = false;

            // Save document to path
            Document.Save (Document.FileUrl, UIDocumentSaveOperation.ForOverwriting, (saveSuccess) => {
                Console.WriteLine ("Save completion: " + saveSuccess);
                if (saveSuccess) {
                    Console.WriteLine ("Document Saved");
                    successful = true;
                } else {
                    Console.WriteLine ("Unable to Save Document");
                    successful=false;
                }
            });

            // Return results
            return successful;
        }
        #endregion

        #region Override Methods
        public override void FinishedLaunching (UIApplication application)
        {

            // Start a new thread to check and see if the user has iCloud
            // enabled.
            new Thread(new ThreadStart(() => {
                // Inform caller that we are checking for iCloud
                CheckingForiCloud = true;

                // Checks to see if the user of this device has iCloud
                // enabled
                var uburl = NSFileManager.DefaultManager.GetUrlForUbiquityContainer(null);

                // Connected to iCloud?
                if (uburl == null)
                {
                    // No, inform caller
                    HasiCloud = false;
                    iCloudUrl =null;
                    Console.WriteLine("Unable to connect to iCloud");
                    InvokeOnMainThread(()=>{
                        var okAlertController = UIAlertController.Create ("iCloud Not Available", "Developer, please check your Entitlements.plist, Bundle ID and Provisioning Profiles.", UIAlertControllerStyle.Alert);
                        okAlertController.AddAction (UIAlertAction.Create ("Ok", UIAlertActionStyle.Default, null));
                        Window.RootViewController.PresentViewController (okAlertController, true, null);
                    });
                }
                else
                {    
                    // Yes, inform caller and save location the Application Container
                    HasiCloud = true;
                    iCloudUrl = uburl;
                    Console.WriteLine("Connected to iCloud");

                    // If we have made the connection with iCloud, start looking for documents
                    InvokeOnMainThread(()=>{
                        // Search for the default document
                        FindDocument ();
                    });
                }

                // Inform caller that we are no longer looking for iCloud
                CheckingForiCloud = false;

            })).Start();

        }

        // This method is invoked when the application is about to move from active to inactive state.
        // OpenGL applications should use this method to pause.
        public override void OnResignActivation (UIApplication application)
        {
        }

        // This method should be used to release shared resources and it should store the application state.
        // If your application supports background execution this method is called instead of WillTerminate
        // when the user quits.
        public override void DidEnterBackground (UIApplication application)
        {
            // Trap all errors
            try {
                // Values to include in the bookmark packet
                var resources = new string[] {
                    NSUrl.FileSecurityKey,
                    NSUrl.ContentModificationDateKey,
                    NSUrl.FileResourceIdentifierKey,
                    NSUrl.FileResourceTypeKey,
                    NSUrl.LocalizedNameKey
                };

                // Create the bookmark
                NSError err;
                Bookmark = Document.FileUrl.CreateBookmarkData (NSUrlBookmarkCreationOptions.WithSecurityScope, resources, iCloudUrl, out err);

                // Was there an error?
                if (err != null) {
                    // Yes, report it
                    Console.WriteLine ("Error Creating Bookmark: {0}", err.LocalizedDescription);
                }
            }
            catch (Exception e) {
                // Report error
                Console.WriteLine ("Error: {0}", e.Message);
            }
        }

        // This method is called as part of the transition from background to active state.
        public override void WillEnterForeground (UIApplication application)
        {
            // Is there any bookmark data?
            if (Bookmark != null) {
                // Trap all errors
                try {
                    // Yes, attempt to restore it
                    bool isBookmarkStale;
                    NSError err;
                    var srcUrl = new NSUrl (Bookmark, NSUrlBookmarkResolutionOptions.WithSecurityScope, iCloudUrl, out isBookmarkStale, out err);

                    // Was there an error?
                    if (err != null) {
                        // Yes, report it
                        Console.WriteLine ("Error Loading Bookmark: {0}", err.LocalizedDescription);
                    } else {
                        // Load document from bookmark
                        OpenDocument (srcUrl);
                    }
                }
                catch (Exception e) {
                    // Report error
                    Console.WriteLine ("Error: {0}", e.Message);
                }
            }

        }

        // This method is called when the application is about to terminate. Save data, if needed.
        public override void WillTerminate (UIApplication application)
        {
        }
        #endregion

        #region Events
        public delegate void DocumentLoadedDelegate(GenericTextDocument document);
        public event DocumentLoadedDelegate DocumentLoaded;

        internal void RaiseDocumentLoaded(GenericTextDocument document) {
            // Inform caller
            if (this.DocumentLoaded != null) {
                this.DocumentLoaded (document);
            }
        }
        #endregion
    }
}

Important

위의 코드에는 위의 문서 검색 및 나열 섹션의 코드가 포함되어 있습니다. 실제 애플리케이션에 표시되는 것처럼 전체적으로 여기에 표시됩니다. 간단히 하기 위해 이 예제는 하드 코딩된 단일 파일(test.txt)에서만 작동합니다.

위의 코드는 애플리케이션의 나머지 부분을 더 쉽게 사용할 수 있도록 몇 가지 iCloud 드라이브 바로 가기를 노출합니다.

다음으로 문서 선택기를 사용하거나 클라우드 기반 문서 작업을 수행할 모든 보기 또는 보기 컨테이너에 다음 코드를 추가합니다.

using CloudKit;
...

#region Computed Properties
/// <summary>
/// Returns the delegate of the current running application
/// </summary>
/// <value>The this app.</value>
public AppDelegate ThisApp {
    get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
}
#endregion

이렇게 하면 위에서 만든 iCloud 바로 가기에 AppDelegate 액세스하고 액세스하는 바로 가기가 추가됩니다.

이 코드를 사용하여 Xamarin iOS 8 애플리케이션에서 문서 선택기 보기 컨트롤러를 구현하는 것을 살펴보겠습니다.

문서 선택 보기 컨트롤러 사용

iOS 8 이전에는 앱 내에서 애플리케이션 외부에서 문서를 검색할 방법이 없었기 때문에 다른 애플리케이션에서 문서에 액세스하기가 매우 어려웠습니다.

기존 동작

기존 동작 개요

iOS 8 이전의 외부 문서에 액세스하는 방법을 살펴보겠습니다.

  1. 먼저 사용자가 원래 문서를 만든 애플리케이션을 열어야 합니다.
  2. 문서가 선택되어 있으며 UIDocumentInteractionController 문서를 새 응용 프로그램에 보내는 데 사용됩니다.
  3. 마지막으로 원본 문서의 복사본이 새 애플리케이션의 컨테이너에 배치됩니다.

여기에서 두 번째 응용 프로그램에서 문서를 열고 편집할 수 있습니다.

앱 컨테이너 외부에서 문서 검색

iOS 8에서 애플리케이션은 자체 애플리케이션 컨테이너 외부의 문서에 쉽게 액세스할 수 있습니다.

앱 컨테이너 외부에서 문서 검색

iOS 애플리케이션은 새 iCloud 문서 선택기( UIDocumentPickerViewController)를 사용하여 애플리케이션 컨테이너 외부에서 직접 검색하고 액세스할 수 있습니다. 사용자가 UIDocumentPickerViewController 권한을 통해 검색된 문서에 대한 액세스 권한을 부여하고 편집할 수 있는 메커니즘을 제공합니다.

애플리케이션은 iCloud 문서 선택기에서 해당 문서를 표시하도록 옵트인해야 하며 다른 응용 프로그램에서 해당 문서를 검색하고 작업할 수 있도록 해야 합니다. Xamarin iOS 8 애플리케이션이 해당 애플리케이션 컨테이너를 공유하도록 하려면 표준 텍스트 편집기에서 파일을 편집 Info.plist 하고 다음 두 줄을 사전의 맨 아래에 추가합니다(태그 사이 <dict>...</dict> ).

<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>

사용자가 UIDocumentPickerViewController 문서를 선택할 수 있는 훌륭한 새 UI를 제공합니다. Xamarin iOS 8 애플리케이션에서 문서 선택 보기 컨트롤러를 표시하려면 다음을 수행합니다.

using MobileCoreServices;
...

// Allow the Document picker to select a range of document types
        var allowedUTIs = new string[] {
            UTType.UTF8PlainText,
            UTType.PlainText,
            UTType.RTF,
            UTType.PNG,
            UTType.Text,
            UTType.PDF,
            UTType.Image
        };

        // Display the picker
        //var picker = new UIDocumentPickerViewController (allowedUTIs, UIDocumentPickerMode.Open);
        var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Open);
        pickerMenu.DidPickDocumentPicker += (sender, args) => {

            // Wireup Document Picker
            args.DocumentPicker.DidPickDocument += (sndr, pArgs) => {

                // IMPORTANT! You must lock the security scope before you can
                // access this file
                var securityEnabled = pArgs.Url.StartAccessingSecurityScopedResource();

                // Open the document
                ThisApp.OpenDocument(pArgs.Url);

                // IMPORTANT! You must release the security lock established
                // above.
                pArgs.Url.StopAccessingSecurityScopedResource();
            };

            // Display the document picker
            PresentViewController(args.DocumentPicker,true,null);
        };

pickerMenu.ModalPresentationStyle = UIModalPresentationStyle.Popover;
PresentViewController(pickerMenu,true,null);
UIPopoverPresentationController presentationPopover = pickerMenu.PopoverPresentationController;
if (presentationPopover!=null) {
    presentationPopover.SourceView = this.View;
    presentationPopover.PermittedArrowDirections = UIPopoverArrowDirection.Down;
    presentationPopover.SourceRect = ((UIButton)s).Frame;
}

Important

개발자는 외부 문서에 액세스하기 전에 메서드를 호출 StartAccessingSecurityScopedResourceNSUrl 해야 합니다. StopAccessingSecurityScopedResource 문서가 로드되는 즉시 보안 잠금을 해제하려면 메서드를 호출해야 합니다.

샘플 출력

i전화 디바이스에서 실행할 때 위의 코드에서 문서 선택기를 표시하는 방법의 예는 다음과 같습니다.

  1. 사용자가 애플리케이션을 시작하고 기본 인터페이스가 표시됩니다.

    기본 인터페이스가 표시됩니다.

  2. 사용자는 화면 맨 위에 있는 작업 단추를 탭하고 사용 가능한 공급자 목록에서 문서 공급자선택하라는 메시지가 표시됩니다.

    사용 가능한 공급자 목록에서 문서 공급자 선택

  3. 선택한 문서 공급자에 대한 문서 선택 보기 컨트롤러표시됩니다.

    문서 선택 보기 컨트롤러가 표시됩니다.

  4. 사용자가 문서 폴더탭하여 해당 내용을 표시합니다.

    문서 폴더 내용

  5. 사용자가 문서를 선택하면 문서선택기가 닫힙니다.

  6. 기본 인터페이스가 다시 표시되고 문서가 외부 컨테이너에서 로드되고 해당 내용이 표시됩니다.

문서 선택 보기 컨트롤러의 실제 표시는 사용자가 디바이스에 설치한 문서 공급자와 구현된 문서 선택기 모드에 따라 달라집니다. 위의 예제에서는 열기 모드를 사용하며, 다른 모드 형식은 아래에서 자세히 설명합니다.

외부 문서 관리

위에서 설명한 것처럼 iOS 8 이전에는 애플리케이션이 애플리케이션 컨테이너의 일부인 문서에만 액세스할 수 있었습니다. iOS 8에서 애플리케이션은 외부 원본의 문서에 액세스할 수 있습니다.

외부 문서 관리 개요

사용자가 외부 원본에서 문서를 선택하면 원본 문서를 가리키는 참조 문서가 애플리케이션 컨테이너에 기록됩니다.

기존 애플리케이션에 이 새로운 기능을 추가하는 데 도움이 되도록 API에 NSMetadataQuery 몇 가지 새로운 기능이 추가되었습니다. 일반적으로 애플리케이션은 유비쿼터스 문서 범위를 사용하여 애플리케이션 컨테이너 내에 있는 문서를 나열합니다. 이 범위를 사용하면 애플리케이션 컨테이너 내의 문서만 계속 표시됩니다.

새 유비쿼터스 외부 문서 범위를 사용하면 애플리케이션 컨테이너 외부에 있는 문서를 반환하고 해당 문서에 대한 메타데이터를 반환합니다. 문서가 NSMetadataItemUrlKey 실제로 있는 URL을 가리킵니다.

애플리케이션이 참조에서 가리키는 문서로 작업하지 않으려는 경우가 있습니다. 대신 앱은 참조 문서로 직접 작업하려고 합니다. 예를 들어 앱은 UI의 애플리케이션 폴더에 문서를 표시하거나 사용자가 폴더 내에서 참조를 이동할 수 있도록 할 수 있습니다.

iOS 8에서는 참조 문서에 직접 액세스하기 위한 새 NSMetadataItemUrlInLocalContainerKey 문서가 제공되었습니다. 이 키는 애플리케이션 컨테이너의 외부 문서에 대한 실제 참조를 가리킵니다.

문서가 NSMetadataUbiquitousItemIsExternalDocumentKey 애플리케이션의 컨테이너 외부에 있는지 여부를 테스트하는 데 사용됩니다. 외부 NSMetadataUbiquitousItemContainerDisplayNameKey 문서의 원본 복사본을 포함하는 컨테이너의 이름에 액세스하는 데 사용됩니다.

문서 참조가 필요한 이유

iOS 8에서 참조를 사용하여 외부 문서에 액세스하는 기본 이유는 보안입니다. 애플리케이션에 다른 애플리케이션의 컨테이너에 대한 액세스 권한이 부여되지 않습니다. Out-of-process가 실행되고 시스템 전체 액세스 권한이 있으므로 문서 선택기만 이 작업을 수행할 수 있습니다.

애플리케이션 컨테이너 외부에서 문서를 가져오는 유일한 방법은 문서 선택기를 사용하는 것이며 선택기에서 반환된 URL이 보안 범위인 경우입니다. 보안 범위 URL에는 애플리케이션에 문서에 대한 액세스 권한을 부여하는 데 필요한 범위가 지정된 권한과 함께 문서를 선택할 수 있는 충분한 정보가 포함되어 있습니다.

보안 범위 URL이 문자열로 직렬화된 다음 직렬화 해제된 경우 보안 정보가 손실되고 URL에서 파일에 액세스할 수 없다는 점에 유의해야 합니다. 문서 참조 기능은 이러한 URL이 가리키는 파일로 돌아가는 메커니즘을 제공합니다.

따라서 애플리케이션이 참조 문서 중 하나에서 인수 NSUrl 하는 경우 이미 보안 범위가 연결되어 있으며 파일에 액세스하는 데 사용할 수 있습니다. 이러한 이유로 개발자는 이 모든 정보와 프로세스를 처리하므로 개발자가 사용하는 UIDocument 것이 좋습니다.

책갈피 사용

예를 들어 상태 복원을 수행할 때 애플리케이션의 문서를 열거하여 특정 문서로 돌아가는 것이 항상 가능한 것은 아닙니다. iOS 8은 지정된 문서를 직접 대상으로 하는 책갈피를 만드는 메커니즘을 제공합니다.

다음 코드는 's FileUrl 속성에서 책갈피를 UIDocument만듭니다.

// Trap all errors
try {
    // Values to include in the bookmark packet
    var resources = new string[] {
        NSUrl.FileSecurityKey,
        NSUrl.ContentModificationDateKey,
        NSUrl.FileResourceIdentifierKey,
        NSUrl.FileResourceTypeKey,
        NSUrl.LocalizedNameKey
    };

    // Create the bookmark
    NSError err;
    Bookmark = Document.FileUrl.CreateBookmarkData (NSUrlBookmarkCreationOptions.WithSecurityScope, resources, iCloudUrl, out err);

    // Was there an error?
    if (err != null) {
        // Yes, report it
        Console.WriteLine ("Error Creating Bookmark: {0}", err.LocalizedDescription);
    }
}
catch (Exception e) {
    // Report error
    Console.WriteLine ("Error: {0}", e.Message);
}

기존 책갈피 API는 외부 파일에 대한 직접 액세스를 제공하기 위해 저장 및 로드할 수 있는 기존 NSUrl 책갈피를 만드는 데 사용됩니다. 다음 코드는 위에서 만든 책갈피를 복원합니다.

if (Bookmark != null) {
    // Trap all errors
    try {
        // Yes, attempt to restore it
        bool isBookmarkStale;
        NSError err;
        var srcUrl = new NSUrl (Bookmark, NSUrlBookmarkResolutionOptions.WithSecurityScope, iCloudUrl, out isBookmarkStale, out err);

        // Was there an error?
        if (err != null) {
            // Yes, report it
            Console.WriteLine ("Error Loading Bookmark: {0}", err.LocalizedDescription);
        } else {
            // Load document from bookmark
            OpenDocument (srcUrl);
        }
    }
    catch (Exception e) {
        // Report error
        Console.WriteLine ("Error: {0}", e.Message);
    }
}

열기 및 가져오기 모드 및 문서 선택기

문서 선택기 보기 컨트롤러에는 두 가지 작업 모드가 있습니다.

  1. 열기 모드 – 이 모드에서 사용자가 문서를 선택하고 외부 문서를 선택하면 문서 선택기가 애플리케이션 컨테이너에 보안 범위 책갈피를 만듭니다.

    애플리케이션 컨테이너의 보안 범위 책갈피

  2. 가져오기 모드 – 이 모드에서 사용자가 문서를 선택하고 외부 문서를 선택하면 문서 선택기가 책갈피를 만들지 않고 대신 임시 위치에 파일을 복사하고 이 위치에 있는 문서에 대한 응용 프로그램 액세스를 제공합니다.

    문서 선택기는 파일을 임시 위치에 복사하고 이 위치에 있는 문서에 대한 응용 프로그램 액세스를 제공합니다.
    어떤 이유로든 애플리케이션이 종료되면 임시 위치가 비워지고 파일이 제거됩니다. 애플리케이션이 파일에 대한 액세스를 기본 필요한 경우 복사본을 만들어 애플리케이션 컨테이너에 배치해야 합니다.

열기 모드는 애플리케이션이 다른 애플리케이션과 공동 작업하고 문서의 변경 내용을 해당 애플리케이션과 공유하려는 경우에 유용합니다. 가져오기 모드는 응용 프로그램이 문서에 대한 수정 내용을 다른 응용 프로그램과 공유하지 않으려는 경우에 사용됩니다.

문서를 외부로 만들기

위에서 설명한 것처럼 iOS 8 애플리케이션은 자체 애플리케이션 컨테이너 외부의 컨테이너에 액세스할 수 없습니다. 애플리케이션은 로컬 또는 임시 위치에 자체 컨테이너에 쓴 다음 특수 문서 모드를 사용하여 결과 문서를 응용 프로그램 컨테이너 외부의 사용자가 선택한 위치로 이동할 수 있습니다.

문서를 외부 위치로 이동하려면 다음을 수행합니다.

  1. 먼저 로컬 또는 임시 위치에 새 문서를 만듭니다.
  2. NSUrl 새 문서를 가리키는 해당 문서를 만듭니다.
  3. 새 문서 선택 보기 컨트롤러를 열고 모드 MoveToServiceNSUrl 전달합니다.
  4. 사용자가 새 위치를 선택하면 문서가 현재 위치에서 새 위치로 이동됩니다.
  5. 참조 문서는 응용 프로그램 만들기에서 파일에 계속 액세스할 수 있도록 앱의 애플리케이션 컨테이너에 기록됩니다.

다음 코드를 사용하여 문서를 외부 위치로 이동할 수 있습니다. var picker = new UIDocumentPickerViewController (srcURL, UIDocumentPickerMode.MoveToService);

위의 프로세스에서 반환된 참조 문서는 문서 선택기의 열기 모드에서 만든 것과 정확히 동일합니다. 그러나 응용 프로그램에서 문서에 대한 참조를 유지하지 않고 문서를 이동하려고 할 수 있는 경우가 있습니다.

참조를 생성하지 않고 문서를 이동하려면 모드를 ExportToService 사용합니다. 예: var picker = new UIDocumentPickerViewController (srcURL, UIDocumentPickerMode.ExportToService);

모드를 ExportToService 사용하는 경우 문서가 외부 컨테이너에 복사되고 기존 복사본이 원래 위치에 남아 있습니다.

문서 공급자 확장

iOS 8을 사용하는 Apple은 최종 사용자가 실제로 어디에 있든 클라우드 기반 문서에 액세스할 수 있기를 원합니다. 이 목표를 달성하기 위해 iOS 8은 새로운 문서 공급자 확장 메커니즘을 제공합니다.

문서 공급자 확장이란?

간단히 말해서 문서 공급자 확장은 개발자 또는 타사에서 기존 iCloud 스토리지 위치와 똑같은 방식으로 액세스할 수 있는 애플리케이션 대체 문서 스토리지를 제공하는 방법입니다.

사용자는 문서 선택기에서 이러한 대체 스토리지 위치 중 하나를 선택할 수 있으며 정확히 동일한 액세스 모드(열기, 가져오기, 이동 또는 내보내기)를 사용하여 해당 위치의 파일을 사용할 수 있습니다.

두 가지 다른 확장을 사용하여 구현됩니다.

  • 문서 선택 확장 - 사용자가 대체 스토리지 위치에서 문서를 선택할 수 있는 그래픽 인터페이스를 제공하는 하위 클래스를 제공합니다 UIViewController . 이 하위 클래스는 문서 선택 보기 컨트롤러의 일부로 표시됩니다.
  • 파일 제공 확장명 – 파일 콘텐츠를 실제로 제공하는 것을 처리하는 비 UI 확장입니다. 이러한 확장은 파일 조정( NSFileCoordinator )을 통해 제공됩니다. 파일 조정이 필요한 또 다른 중요한 경우입니다.

다음 다이어그램에서는 문서 공급자 확장으로 작업할 때 일반적인 데이터 흐름을 보여 줍니다.

이 다이어그램은 문서 공급자 확장으로 작업할 때의 일반적인 데이터 흐름을 보여 줍니다.

다음 프로세스가 발생합니다.

  1. 애플리케이션은 사용자가 작업할 파일을 선택할 수 있도록 문서 선택 컨트롤러를 제공합니다.
  2. 사용자가 대체 파일 위치를 선택하고 사용자 인터페이스를 표시하기 위해 사용자 지정 UIViewController 확장 프로그램이 호출됩니다.
  3. 사용자가 이 위치에서 파일을 선택하면 URL이 문서 선택기로 다시 전달됩니다.
  4. 문서 선택기는 파일의 URL을 선택하고 사용자가 작업할 수 있도록 애플리케이션에 반환합니다.
  5. URL은 파일 콘텐츠를 애플리케이션에 반환하기 위해 파일 코디네이터에게 전달됩니다.
  6. 파일 코디네이터는 사용자 지정 파일 공급자 확장자를 호출하여 파일을 검색합니다.
  7. 파일의 내용은 파일 코디네이터에게 반환됩니다.
  8. 파일의 내용이 애플리케이션에 반환됩니다.

보안 및 책갈피

이 섹션에서는 책갈피를 통한 보안 및 영구 파일 액세스가 문서 공급자 확장 프로그램에서 작동하는 방식을 간단히 살펴봅니다. 보안 및 책갈피를 애플리케이션 컨테이너에 자동으로 저장하는 iCloud 문서 공급자와 달리 문서 공급자 확장은 문서 참조 시스템의 일부가 아니기 때문에 저장되지 않습니다.

예를 들어 회사 차원의 보안 데이터 저장소를 제공하는 엔터프라이즈 설정에서 관리자는 공용 iCloud 서버에서 기밀 회사 정보에 액세스하거나 처리하지 않도록 합니다. 따라서 기본 제공 문서 참조 시스템을 사용할 수 없습니다.

책갈피 시스템은 계속 사용할 수 있으며, 책갈피가 지정된 URL을 올바르게 처리하고 해당 URL이 가리키는 문서의 내용을 반환하는 것은 파일 공급자 확장명의 책임입니다.

보안을 위해 iOS 8에는 파일 공급자 내부의 식별자에 액세스할 수 있는 애플리케이션에 대한 정보를 유지하는 격리 계층이 있습니다. 모든 파일 액세스는 이 격리 계층에 의해 제어됩니다.

다음 다이어그램은 책갈피 및 문서 공급자 확장으로 작업할 때의 데이터 흐름을 보여 줍니다.

이 다이어그램은 책갈피 및 문서 공급자 확장으로 작업할 때의 데이터 흐름을 보여 줍니다.

다음 프로세스가 발생합니다.

  1. 애플리케이션이 백그라운드로 들어가려고 하며 상태를 유지해야 합니다. 대체 스토리지의 파일에 책갈피를 만들도록 호출 NSUrl 합니다.
  2. NSUrl 는 파일 공급자 확장자를 호출하여 문서에 대한 영구 URL을 가져옵니다.
  3. 파일 공급자 확장명은 URL을 문자열로 반환합니다 NSUrl .
  4. NSUrl URL을 책갈피에 번들로 묶어 애플리케이션에 반환합니다.
  5. 애플리케이션이 백그라운드에서 해제되고 상태를 복원해야 하는 경우 책갈피 NSUrl 를 전달합니다.
  6. NSUrl 는 파일의 URL을 사용하여 파일 공급자 확장자를 호출합니다.
  7. 파일 확장 프로그램 공급자가 파일에 액세스하고 파일의 위치를 반환합니다 NSUrl .
  8. 파일 위치는 보안 정보와 함께 번들로 제공되고 애플리케이션에 반환됩니다.

여기에서 애플리케이션은 파일에 액세스하고 정상적으로 작업할 수 있습니다.

파일 작성

이 섹션에서는 문서 공급자 확장을 사용하여 파일을 다른 위치에 쓰는 방법을 간단히 살펴봅니다. iOS 애플리케이션은 파일 조정을 사용하여 애플리케이션 컨테이너 내의 디스크에 정보를 저장합니다. 파일이 성공적으로 작성된 직후 파일 공급자 확장명은 변경 내용에 대한 알림을 받습니다.

이 시점에서 파일 공급자 확장명은 대체 위치에 파일 업로드를 시작할 수 있습니다(또는 파일을 더티 표시하고 업로드 필요).

새 문서 공급자 확장 만들기

새 문서 공급자 확장을 만드는 것은 이 소개 문서의 범위를 벗어납니다. 이 정보는 사용자가 iOS 디바이스에 로드한 확장에 따라 애플리케이션이 Apple 제공 iCloud 위치 외부의 문서 스토리지 위치에 액세스할 수 있음을 표시하기 위해 여기에 제공됩니다.

개발자는 문서 선택기를 사용하고 외부 문서로 작업할 때 이 사실을 알고 있어야 합니다. 해당 문서가 iCloud에서 호스트된다고 가정해서는 안 됩니다.

스토리지 공급자 또는 문서 선택기 확장을 만드는 방법에 대한 자세한 내용은 앱 확장 소개 문서를 참조하세요.

iCloud 드라이브로 마이그레이션

iOS 8에서 사용자는 iOS 7(및 이전 시스템)에서 사용되는 기존 iCloud 문서 시스템을 계속 사용하도록 선택하거나 기존 문서를 새 iCloud 드라이브 메커니즘으로 마이그레이션하도록 선택할 수 있습니다.

Mac OS X Yosemite에서 Apple은 이전 버전과의 호환성을 제공하지 않으므로 모든 문서를 iCloud 드라이브로 마이그레이션하거나 장치 간에 더 이상 업데이트되지 않습니다.

사용자의 계정이 iCloud 드라이브로 마이그레이션된 후에는 iCloud Drive를 사용하는 디바이스만 해당 장치에서 문서에 변경 내용을 전파할 수 있습니다.

Important

개발자는 이 문서에서 다루는 새로운 기능은 사용자의 계정이 iCloud 드라이브로 마이그레이션된 경우에만 사용할 수 있다는 점에 유의해야 합니다.

요약

이 문서에서는 iCloud 드라이브 및 새 문서 선택기 보기 컨트롤러를 지원하는 데 필요한 기존 iCloud API의 변경 내용을 설명했습니다. 파일 조정과 클라우드 기반 문서를 사용할 때 중요한 이유를 설명했습니다. Xamarin.iOS 애플리케이션에서 클라우드 기반 문서를 사용하도록 설정하는 데 필요한 설정을 다루었으며 문서 선택기 보기 컨트롤러를 사용하여 앱의 애플리케이션 컨테이너 외부에서 문서를 사용하는 방법에 대한 소개를 제공했습니다.

또한 이 문서에서는 클라우드 기반 문서를 처리할 수 있는 애플리케이션을 작성할 때 문서 공급자 확장과 개발자가 이를 인식해야 하는 이유를 간략하게 설명했습니다.