prosource

iCloud 기본 사항 및 코드 샘플

probook 2023. 7. 27. 22:05
반응형

iCloud 기본 사항 및 코드 샘플

초보자로서, 저는 아이클라우드에 어려움을 겪고 있습니다.일부 샘플이 있지만 대개 매우 상세합니다(개발자 포럼에는 대규모 iCloud 및 CoreData용 샘플이 있습니다).사과 문서는 괜찮지만, 저는 여전히 큰 그림을 볼 수 없습니다.그래서 이 질문들 중 일부는 꽤 근본적이지만, 아마도 대답하기 쉬울 것입니다.

컨텍스트:매우 간단한 iCloud 앱을 실행하고 있습니다(아래 전체 샘플 코드).사용자에게 표시되는 UITextView는 하나뿐이며 사용자의 입력은 text라는 파일에 저장됩니다.txt의

enter image description here

txt 파일이 클라우드로 푸시되어 모든 장치에서 사용할 수 있게 됩니다.완벽하게 작동하지만, 다음과 같습니다.

주요 문제:아이클라우드를 사용하지 않는 사용자는 어떻습니까?

앱을 실행할 때(아래 코드 참조) 사용자가 iCloud를 활성화했는지 확인합니다.iCloud를 사용하면 모든 것이 정상입니다.앱이 계속해서 텍스트를 찾습니다.클라우드에서 txt.발견되면 해당 파일을 로드하여 사용자에게 표시합니다.if text.txt는 클라우드에서 찾을 수 없으며 새 텍스트를 만들 뿐입니다.txt를 입력하면 해당 정보가 사용자에게 표시됩니다.

사용자가 iCloud를 사용하도록 설정하지 않으면 아무 일도 일어나지 않습니다.iCloud 사용자가 아닌 사용자가 내 텍스트 앱으로 작업할 수 있도록 하려면 어떻게 해야 합니까?아니면 그냥 무시하는 건가요?비 iCloud 사용자를 위해 별도의 기능을 작성해야 합니까?즉, 단순히 텍스트를 로드하는 기능입니다.문서 폴더에서 txt?

Apple이 작성한 내용:

앱 샌드박스의 다른 모든 파일과 동일한 방식으로 iCloud의 파일을 처리합니다.

하지만 저의 경우에는 더 이상 '정상적인' 앱 샌드박스가 없습니다.클라우드에 있습니다.아니면 제가 항상 제 문자를 읽습니다.디스크에서 먼저 txt를 실행한 다음 iCloud에서 더 최신 버전이 있는지 확인하십시오.

관련 문제:파일 구조 - 샌드박스 vs.구름

아마도 저의 주된 문제는 iCloud의 작동 방식에 대한 근본적인 오해일 것입니다.UID 문서의 새 인스턴스를 만들 때 두 가지 방법을 덮어써야 합니다. 첫째번▁first- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError클라우드에서 파일을 가져온 다음-(id)contentsForType:(NSString *)typeName error:(NSError **)outError파일을 클라우드로 가져올 수 있습니다.

텍스트의 로컬 복사본도 저장할 별도의 기능을 통합해야 합니까?내 샌드박스에 txt?이것이 비 iCloud 사용자에게 효과가 있습니까?제가 알기로는 아이클라우드는 텍스트의 로컬 복사본을 저장할 것입니다.txt 자동으로 입력합니다.따라서 앱의 '이전' 샌드박스에 저장할 필요가 없습니다(예: iCloud 이전 시절처럼).지금은 제 샌드박스가 완전히 비어있는데 이게 맞는지 모르겠어요.텍스트를 한 부 더 보관해야 합니까?저 안에 txt?제 데이터 구조를 혼란스럽게 하는 것 같습니다.텍스트가 하나 있기 때문에.클라우드에서 txt, 내 장치의 iCloud 샌드박스에 하나(오프라인에 있더라도 작동함), 내 앱의 good old 샌드박스에 세 번째 하나...


MY CODE: 간단한 iCloud 샘플 코드

이것은 제가 개발자 포럼과 WWDC 세션 비디오에서 찾은 예를 대략적으로 기반으로 합니다.저는 그것을 최소한으로 줄였습니다.저는 제 MVC 구조가 괜찮은지 잘 모르겠습니다.이 모델은 AppDelegate에 포함되어 있어 이상적이지 않습니다.그것을 개선하기 위한 어떤 제안도 환영합니다.


편집: 주요 질문을 추출하려고 시도하여 [여기]에 게시했습니다.4


개요:

Overview

텍스트를 로드하는 가장 중요한 비트입니다.클라우드에서 txt:

//  AppDelegate.h
//  iCloudText

#import <UIKit/UIKit.h>

@class ViewController;
@class MyTextDocument;

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
    NSMetadataQuery *_query;
}

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) ViewController *viewController;
@property (strong, nonatomic) MyTextDocument *document;

@end

//  AppDelegate.m
//  iCloudText

#import "AppDelegate.h"
#import "MyTextDocument.h"
#import "ViewController.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize viewController = _viewController;
@synthesize document = _document;

- (void)dealloc
{
    [_window release];
    [_viewController release];
    [super dealloc];
}

- (void)loadData:(NSMetadataQuery *)query {

    // (4) iCloud: the heart of the load mechanism: if texts was found, open it and put it into _document; if not create it an then put it into _document

    if ([query resultCount] == 1) {
        // found the file in iCloud
        NSMetadataItem *item = [query resultAtIndex:0];
        NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];

        MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:url];
        //_document = doc;
        doc.delegate = self.viewController;
        self.viewController.document = doc;

        [doc openWithCompletionHandler:^(BOOL success) {
            if (success) {
                NSLog(@"AppDelegate: existing document opened from iCloud");
            } else {
                NSLog(@"AppDelegate: existing document failed to open from iCloud");
            }
        }];
    } else {
        // Nothing in iCloud: create a container for file and give it URL
        NSLog(@"AppDelegate: ocument not found in iCloud.");

        NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
        NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:@"Documents"] URLByAppendingPathComponent:@"text.txt"];

        MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:ubiquitousPackage];
        //_document = doc;
        doc.delegate = self.viewController;
        self.viewController.document = doc;

        [doc saveToURL:[doc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
            NSLog(@"AppDelegate: new document save to iCloud");
            [doc openWithCompletionHandler:^(BOOL success) {
                NSLog(@"AppDelegate: new document opened from iCloud");
            }];
        }];
    }
}

- (void)queryDidFinishGathering:(NSNotification *)notification {

    // (3) if Query is finished, this will send the result (i.e. either it found our text.dat or it didn't) to the next function

    NSMetadataQuery *query = [notification object];
    [query disableUpdates];
    [query stopQuery];

    [self loadData:query];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query];
    _query = nil; // we're done with it
}

-(void)loadDocument {

    // (2) iCloud query: Looks if there exists a file called text.txt in the cloud

    NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
    _query = query;
    //SCOPE
    [query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
    //PREDICATE
    NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, @"text.txt"];
    [query setPredicate:pred];
    //FINISHED?
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
    [query startQuery];

}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSLog(@"AppDelegate: app did finish launching");
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];

    // Override point for customization after application launch.
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil] autorelease];
    } else {
        self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil] autorelease];
    }

    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];

    // (1) iCloud: init

    NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    if (ubiq) {
        NSLog(@"AppDelegate: iCloud access!");
        [self loadDocument];
    } else {
        NSLog(@"AppDelegate: No iCloud access (either you are using simulator or, if you are on your phone, you should check settings");
    }


    return YES;
}

@end

UID 문서

//  MyTextDocument.h
//  iCloudText

#import <Foundation/Foundation.h>
#import "ViewController.h"

@interface MyTextDocument : UIDocument {

    NSString *documentText;
    id delegate;

}

@property (nonatomic, retain) NSString *documentText;
@property (nonatomic, assign) id delegate;

@end

//  MyTextDocument.m
//  iCloudText

#import "MyTextDocument.h"
#import "ViewController.h"

@implementation MyTextDocument

@synthesize documentText = _text;
@synthesize delegate = _delegate;

// ** READING **

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
{
    NSLog(@"UIDocument: loadFromContents: state = %d, typeName=%@", self.documentState, typeName);

    if ([contents length] > 0) {
        self.documentText = [[NSString alloc] initWithBytes:[contents bytes] length:[contents length] encoding:NSUTF8StringEncoding];
    }
    else {
        self.documentText = @"";
    }

    NSLog(@"UIDocument: Loaded the following text from the cloud: %@", self.documentText);


    // update textView in delegate...
    if ([_delegate respondsToSelector:@selector(noteDocumentContentsUpdated:)]) {
        [_delegate noteDocumentContentsUpdated:self];
    }

    return YES;

}

// ** WRITING **

-(id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
    if ([self.documentText length] == 0) {
        self.documentText = @"New Note";
    }

    NSLog(@"UIDocument: Will save the following text in the cloud: %@", self.documentText);

    return [NSData dataWithBytes:[self.documentText UTF8String] length:[self.documentText length]];
}
@end

뷰 컨트롤러

//
//  ViewController.h
//  iCloudText

#import <UIKit/UIKit.h>

@class MyTextDocument;

@interface ViewController : UIViewController <UITextViewDelegate> {

    IBOutlet UITextView *textView;

}

@property (nonatomic, retain) UITextView *textView;
@property (strong, nonatomic) MyTextDocument *document;

-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument;

@end

//  ViewController.m
//  iCloudText

#import "ViewController.h"
#import "MyTextDocument.h"

@implementation ViewController

@synthesize textView = _textView;
@synthesize document = _document;

-(IBAction)dismissKeyboard:(id)sender {

    [_textView resignFirstResponder];

}

-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument
{
    NSLog(@"VC: noteDocumentsUpdated");
    _textView.text = noteDocument.documentText;
}

-(void)textViewDidChange:(UITextView *)theTextView {

     NSLog(@"VC: textViewDidChange");
    _document.documentText = theTextView.text;
    [_document updateChangeCount:UIDocumentChangeDone];

}

방금 서류를 다시 읽었는데 제 일반적인 접근 방식이 잘못된 것 같습니다.먼저 샌드박스에 파일을 생성한 다음 클라우드로 이동해야 합니다.즉, Apple은 앱 디렉터리에 하나, 장치의 iCloud demon 디렉터리에 하나(오프라인일 경우에도 액세스 가능), 클라우드에 하나씩, 세 가지 버전의 동일한 파일을 항상 보유해야 한다고 제안하는 것 같습니다.

앱은 로컬 파일 및 디렉터리에 대해 수행하는 것과 동일한 기술을 사용하여 iCloud의 파일 및 디렉터리를 관리합니다.iCloud의 파일 및 디렉터리는 여전히 파일 및 디렉터리에 불과합니다.파일을 열거나, 만들거나, 이동하거나, 복사하거나, 파일을 읽고 쓰거나, 삭제하거나, 다른 작업을 수행할 수 있습니다.로컬 파일 및 디렉터리와 iCloud 파일 및 디렉터리의 차이점은 액세스하는 데 사용하는 URL뿐입니다.앱의 샌드박스와 관련된 URL 대신 iCloud 파일 및 디렉터리의 URL은 해당 iCloud 컨테이너 디렉터리와 관련됩니다.

파일 또는 디렉터리를 iCloud로 이동하는 방법

앱 샌드박스에 로컬로 파일 또는 디렉터리를 만듭니다.사용 중에는 UID 문서 객체와 같은 파일 발표자가 파일 또는 디렉토리를 관리해야 합니다.

URLForUbiquityContainerIdentifier: 메서드를 사용하여 항목을 저장할 iCloud 컨테이너 디렉토리의 URL을 검색합니다.컨테이너 디렉토리 URL을 사용하여 iCloud에서 항목의 위치를 지정하는 새 URL을 작성합니다.NSFileManager의 setUbiquitous:itemAtURL:destinationURL:error:method를 호출하여 항목을 iCloud로 이동합니다.앱의 메인 스레드에서 이 메서드를 호출하지 마십시오. 이렇게 하면 메인 스레드가 장시간 차단되거나 앱의 자체 파일 발표자 중 하나와 교착 상태가 발생할 수 있습니다.파일 또는 디렉터리를 iCloud로 이동하면 시스템이 해당 항목을 앱 샌드박스에서 개인 로컬 디렉터리로 복사하여 iCloud 데몬에서 모니터링할 수 있습니다.파일이 더 이상 샌드박스에 없더라도 앱은 여전히 파일에 대한 전체 액세스 권한을 가집니다.파일의 복사본이 현재 장치에 로컬로 남아 있지만 다른 장치로 배포할 수 있도록 파일도 iCloud로 전송됩니다.iCloud 데몬은 로컬 복사본이 동일한지 확인하는 모든 작업을 처리합니다.따라서 앱의 관점에서 파일은 iCloud에 있습니다.

iCloud에서 파일 또는 디렉터리를 변경할 때는 모두 파일 코디네이터 개체를 사용해야 합니다.이러한 변경에는 항목 이동, 삭제, 복사 또는 이름 변경이 포함됩니다.파일 코디네이터는 iCloud 데몬이 파일이나 디렉터리를 동시에 변경하지 않도록 하고 다른 관계자에게 사용자가 변경한 내용을 알리도록 합니다.

하지만 set Ubiquitous에 관한 문서를 조금 더 깊이 파고들면 다음을 발견할 수 있습니다.

이 방법을 사용하여 파일을 현재 위치에서 iCloud로 이동합니다.응용 프로그램의 샌드박스에 위치한 파일의 경우 이 작업은 샌드박스 디렉터리에서 파일을 물리적으로 제거하는 작업을 포함합니다. (시스템은 응용 프로그램이 iCloud로 이동하는 파일에 액세스할 수 있도록 응용 프로그램의 샌드박스 권한을 확장합니다.)이 방법을 사용하여 iCloud에서 파일을 이동한 후 다시 로컬 디렉터리로 이동할 수도 있습니다.

이는 파일/디렉토리가 로컬 샌드박스에서 삭제되고 클라우드로 이동됨을 의미하는 것으로 보입니다.

저는 당신의 사례를 사용해 왔으며, iCloud의 기본 사항을 파악하는 데 도움이 되는 사례가 좋습니다.현재 저는 iCloud를 사용하거나 사용하지 않는 기존 사용자가 로컬에 저장된 콘텐츠를 사용하여 이러한 사례를 생성할 수 있도록 지원해야 하는 제 자신의 앱에 대한 귀하의 질문과 논쟁을 벌이고 있습니다.

사례:

  1. 새 사용자
    • has icloud - icloud에서 문서 생성
    • no icloud - 로컬로 문서 작성
  2. 기존 사용자
    • 아이클라우드가 있습니까?
      • 방금 추가됨 - 로컬 문서를 i클라우드로 마이그레이션
      • 단순히 추가된 것이 아님 - icloud에 문서 열기/저장
    • no icloud
      • 방금 제거됨 - 이전 icloud 문서를 로컬로 마이그레이션
      • 제거되지 않음 - 로컬에 문서 열기/저장

누군가 아이클라우드를 제거하면 - 유비쿼터스 URL에 대한 호출이 0으로 돌아가지 않을까요?그렇다면 문서를 로컬 스토리지로 다시 마이그레이션하려면 어떻게 해야 합니까?우선 사용자 기본 설정을 만들겠지만 해결 방법이 있을 것 같습니다.

제가 여기서 뭔가 분명한 것을 놓치고 있는 것 같으니 누구라도 볼 수 있으면 차를 불러주세요.

사용자가 iOS 5.0 이전 장치 간에 텍스트를 공유할 수 있도록 하려면 iCloud 이전에 모든 사용자가 수행해야 했던 작업을 수행하고 정보를 자신의 서버로 이동해야 합니다.

정말 필요한 것은 앱이 텍스트 파일을 저장하고 사용자 계정과 연결할 수 있는 서버입니다.

계정을 생성하려면 사용자가 필요하며 한 장치의 새 정보를 자신의 '클라우드'로 이동하는 프로세스를 직접 관리해야 합니다.

사용자는 다른 장치에서 동일한 계정으로 등록하며, 다른 장치가 사용자의 클라우드로 데이터를 이동한 경우 이를 감지하고 현재 장치를 새 정보로 업데이트해야 합니다.

분명히 iOS 5.0 장치의 경우, 당신은 아마도 당신의 클라우드에서 iOS 5.0 이전 장치에 대해 변경된 파일을 감지하고 iCloud와 대화할 수 있기를 원할 것입니다.

iCloud를 사용하는 데 어려움을 겪고 있는 것 같지는iOS5만큼 클라우드 문제가 발생하지 않음IOS5 문제.

배포 대상이 iOS5인 경우에는 항상 UID 문서 구조를 사용합니다.어디에나 있는 경우 NSMetaDataQuery는 클라우드에서 찾을 수 있으며, 그렇지 않은 경우 장치에서 찾을 수 있습니다.

반면 앱에 5.0 이전 액세스를 제공하려면 실행 중인 iOS가 5.0 이상인지 조건부로 확인해야 합니다.그런 다음 UID 문서를 사용하고 그렇지 않은 경우 기존 방식으로 데이터를 읽거나 씁니다.

제 접근 방식은 iOS5를 확인하는 조건부 saveData 메서드를 작성하는 것이었습니다.변경 횟수가 있는 경우 변경 횟수를 업데이트하거나 실행 취소 관리자를 사용합니다.이 경우 ViewDidChange 텍스트가 이 메서드를 호출합니다.그렇지 않으면 기존 방식으로 디스크에 저장됩니다.로드 시에는 반대 현상이 발생합니다.

"앱 샌드박스의 다른 모든 파일을 처리하는 것과 동일한 방식으로 iCloud의 파일을 처리합니다."라는 질문에 혼란스러워 합니다.키노트와 숫자와 같은 경우에는 파일을 많이 보관하고 iCloud를 사용하면 마법처럼 동기화되기 시작합니다.

하지만 당신은 iCloud와 같은 기능에 의존하는 무언가를 구축하고 있습니다.당신의 앱은 iCloud가 원하는 방식으로 작동하기 위해 존재하는 모든 것에 의존하기 때문에 당신은 그 진술서를 보유할 수 없습니다.앱을 닫고 "아이클라우드를 설정해 주세요"라고 말하거나, 항상 사용할 수 있는 아이클라우드와 유사한 기능(사용자 또는 다른 사용자의 기능)을 복제해야 합니다.

언급URL : https://stackoverflow.com/questions/7795629/icloud-basics-and-code-sample

반응형