스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
FileProvider로 iOS에서 데스크탑급으로 동기화하기
File Provider 확장 프로그램을 생성하여 iPhone 및 iPad 앱 내에서 파일을 더 빠르고 효율적으로 동기화할 수 있는 방법을 알아보십시오. File Provider 팀으로부터 설명을 듣고 iOS용 최신 File Provider를 빌드하는 방법에 대해 알아보십시오. 원활한 파일 동기화, 업로드 및 다운로드를 지원하도록 앱을 설계하는 방법을 알려드립니다. 예기치 않은 상황에 대비하여 상태 비보전형을 유지하고 File Provider를 강화하는 방법도 살펴봅니다. 이 세션을 최대한 활용하려면 macOS에서 File Provider를 사용해 보는 것이 좋습니다.
리소스
- File Provider
- File Provider UI
- Sending notification requests to APNs
- Synchronizing files using file provider extensions
관련 비디오
WWDC21
-
다운로드
안녕하세요, Cloud File Provider 팀의 Johannes Fortmann입니다 오늘은 iOS 16에 도입된 새로운 File Provider API를 사용하여 iOS에서 데스크탑급으로 동기화하는 방법에 대해 알아보겠습니다 소개를 마친 다음에는 File Provider의 사용 목적을 간단히 살펴보겠습니다 앱 설계에 적용할 수 있는 최적의 접근 방식은 물론 점점 iOS에서 더 중요해지고 있는 모범 사례도 함께 알아보고 마지막으로 iOS에서 Provider를 실행하는 모습도 데모를 통해 보여드리도록 하겠습니다 Big Sur에서는 파일을 Mac에 동기화하는 데 선언적 API가 도입되었습니다 이 API는 성공적으로 많은 클라우드 공급업체에 의해 채택되었습니다 저희 팀에서는 API를 개선하기 위해 꾸준히 노력해 왔고 이제 iOS 16에서도 사용할 수 있게 되어 기쁩니다 해당 API를 사용하면 iOS 앱에서 이른바 ‘데스크탑급 동기화'를 제공할 수 있습니다 이게 무슨 뜻일까요? iOS 앱의 성능 점점 더 강력해지면서 파일 시스템의 공유 위치에 접근할 수 있는 기능이 더 중요해졌습니다 사람들은 이런 강력한 앱에서 다양한 유형의 파일 시스템 대상체에 접근할 수 있기를 원합니다 사용자가 하기로 결정하면 앱에서 폴더에 접근하고 새 파일을 생성하기를 원합니다 저희는 일관성을 보장하면서 이 모든 동작이 가능하기를 바랐습니다 일관성이란 무엇일까요? 지금까지 iOS에서 백그라운드 런타임은 전력 문제로 인해 제한되어 왔으며, 동시에 백그라운드에서 변경 사항이 업로드될 것으로 기대되어 왔습니다 이 문제를 해결하기 위해 최신 File Provider API가 도입되었습니다 항목 열거, 콘텐츠 가져오기 및 업로드, 원격으로 변경한 경우 항목 목록 업데이트 등을 담당하는 앱 확장 프로그램을 기본적인 수준에 구현합니다 시스템에는 사용자가 제공한 정보를 노출하고 일관성을 유지할 책임이 있습니다 시스템에서 해야 하는 중요한 작업은 오류를 추적하고 필요한 경우 다시 시도하는 것입니다 콘텐츠 가져오기와 같은 일부 작업의 경우 재시도는 자연스러운 것입니다 사용자는 다운로드를 적극적으로 기다리고 있으며 진행 막대를 면밀히 모니터링하고 있을 것입니다 반면에 업로드에는 스케줄링이 필요합니다 디스크에 있는 항목의 상태를 추적함으로써 시스템에서 업데이트된 콘텐츠가 업로드되도록 할 수 있습니다 필요한 경우 업로드를 다시 시도하기 위해 진행률 및 오류를 추적하게 됩니다 또 다른 복잡한 주제로는 업로드하는 동안 일관성을 유지하는 문제가 있습니다 시스템에서는 업로드를 하는 동안 차후에 파일에 성공적으로 접근하고 올바른 데이터를 표시하도록 파일 콘텐츠의 복제본을 관리합니다 이러한 작업 중에 시스템에서는 여러 앱에서 로컬 버전에 접근하더라도 로컬 버전이 일관되게 유지되도록 하며, 여기에는 원격 서버에서의 하향 동기화도 포함됩니다 이 동작은 APFS 기능 및 파일 조정을 사용하여 사용자에게 투명하게 보여주며 구현됩니다 저장 공간에 한계가 있다는 것은 모바일 기기에서는 중요한 제약 사항입니다 시스템에서는 APFS 기능을 사용하여 로컬 파일의 변경 상태를 원자적으로 추적합니다 이를 통해 디스크 사용량 및 최근 사용 상태에 따라 로컬 변경 사항이 없는 파일을 투명하게 제거할 수 있습니다 완전히 업로드된 파일은 설정에 있는 저장 공간 관리 패널에서 앱 개수에 반영되지 않습니다 지금까지 시스템과 확장 프로그램에 대해 살펴보았습니다 이제 앱에 어떻게 적용하는지 살펴보도록 하겠습니다 관심사를 엄격하게 분리하는 것을 추천합니다 시스템에서는 디스크 상의 구조 관리 및 작업 스케줄링을 담당합니다 확장 프로그램에서는 상향 및 하향 동기화를 수행하는 작업을 담당합니다 시스템에서는 파일 계층 구조에 대한 모든 상태와 동기화에 필요한 부분을 추적합니다 이는 확장 프로그램이 매우 가벼워질 수 있다는 뜻입니다 확장 프로그램에서 항목별 상태를 추적할 필요가 전혀 없습니다 응용 프로그램에서는 동기화를 책임지지 않아도 됩니다 이상적으로는 서버와 통신할 필요가 전혀 없습니다 대신 응용 프로그램에서 확장 프로그램과 상호 작용하며 시스템의 다른 응용 프로그램과 동일한 방식으로 확장 프로그램과 간접적으로 상호 작용할 수 있습니다 루트를 포함한 모든 관리 항목의 파일 URL을 가져올 수 있는 API가 있습니다 그런 다음 일반 파일 시스템 API를 사용하여 해당 위치에 접근할 수 있습니다 다른 방법으로는 앱에서 확장 프로그램에 직접 XPC 서비스 연결을 요청할 수 있습니다 이는 파일 공유 또는 충돌 해결과 같이 파일 시스템 조작으로 표현할 수 없는 작업을 처리하는 데 특히 유용합니다 이 두 메커니즘 모두 File Provider UI 확장 프로그램에서 파일 앱에 추가 통합 지점을 제공하는 데 사용할 수도 있습니다 상태를 저장하지 않는 제공자에게 특히 중요한 세 가지 사항을 말씀드리겠습니다 먼저 업로드에 대해 알아보겠습니다 앞서 언급했듯이 시스템에서는 업로드를 추적하고 업로드를 수행할 시간을 확장 프로그램에 부여합니다 이로 인해 발생하는 중요한 결과는 진행 상황을 보고하여 업로드가 실제로 진행되고 있음을 시스템에 계속 알려야 한다는 점입니다 업로드 작업이 진행되지 않으면 취소됩니다 시스템에서는 업로드를 깔끔히 마무리할 유예 기간을 제공하지만 취소 시간이 너무 오래 걸릴 경우 확장 프로그램이 종료됩니다 이제 코드를 확인해 보겠습니다 취소 처리기를 구현하려면 작업별 메서드에서 진행률 반환에 설정하기만 하면 됩니다 업로드의 경우에는 modifyItem입니다 처리기에서 수행하던 실제 업로드 작업을 취소합니다 물론 취소 오류가 발생했음을 알리기 위해 완료 처리기도 호출해야 합니다 이 코드 예시에서는 이를 편리하게 만들기 위해 비동기 작업 취소를 사용하지만 완료 처리기를 수동으로 호출할 수도 있습니다 다음으로 동기화 하향 경로에 대해 알아보겠습니다 사용자가 파일과 상호 작용할 때 서버로부터 변경 사항을 수신하기 위해 기본 앱이 실행되지 않습니다 원격으로 변경된 사항을 시스템에 계속 알리려면 푸시 알림을 구현해야 합니다 PushKit에서는 File Provider를 위해 특정 푸시 유형을 노출합니다 확장 프로그램에서 직접 이러한 푸시를 등록할 수 있습니다 서버에서 알맞게 정의된 페이 로드와 함께 푸시를 보냅니다 시스템에서 푸시를 수신하고 해당하는 경우 현재 상태를 새로 고칩니다 다른 유형의 작업과 마찬가지로 시스템에서는 배터리 상태 또는 사용자가 현재 파일을 보는 중인지와 같은 상황에 따라 실제 새로 고침을 지연시킬 수 있습니다 마지막은 주의를 기울여야 하는 기능입니다 시스템에서는 확장 프로그램에서 보고하는 폴더 계층 구조를 관리합니다 이를 통해 전체 폴더 계층 구조를 공급할 수 있습니다 확장 프로그램에서는 이에 대해 추가 작업이 필요없습니다 최신 File Provider에서는 기본적으로 활성화되어 있습니다 마지막 기능을 활용하면 어떤 종류의 작업 흐름이 가능한지 시연해 보겠습니다 이 세션의 샘플 코드로 여기에 기기를 설정했습니다 샘플 코드를 iOS로 포팅했습니다 서버 로그인을 처리하기 위해 iOS 앱을 빌드했지만 확장 프로그램은 macOS 버전에서 거의 변경되지 않았습니다 지금 iPad에서 샘플 코드를 실행하고 있습니다 오른쪽에는 파일 앱이 실행 중이며 이미 파일이 동기화되어 있습니다 그리고 폴더 선택을 활용한 앱도 제작했습니다 이 앱에서는 폴더의 모든 이미지에 세피아 필터를 적용합니다 이러한 유형의 응용 프로그램에는 개별 항목에 대한 상호 작용이 강제되지 않아도 폴더의 모든 항목에서 작동할 수 있기 때문에 폴더 접근에 이점이 있습니다 데스크탑급 동기화가 가능하므로 파일 앱에서 일괄 편집기로 폴더를 드래그하기만 하면 됩니다 진행 상황을 모니터링할 수 있도록 폴더와 파일을 불러오겠습니다 그런 다음 버튼을 누르면 모든 사진이 다운로드되고 수정됩니다 사진은 수정된 후 자동으로 업로드됩니다 확장 프로그램에서 보고한 업로드 진행률은 파일 앱 하단 표시되어 사용자에게 계속 보고됩니다 제 앱으로 이와 같은 동작을 구현하려 한다고 가정하겠습니다 먼저 항목 드래그를 구현해 보겠습니다 드래그 시작을 허용하려면 onDrag 메서드를 구현합니다 이 메서드는 NSItemProvider를 반환합니다 드래그할 파일 유형으로 itemProvider에 파일 표현을 등록합니다 이 경우에는 폴더입니다 getUserVisibleURL 메서드를 사용하여 URL을 가져옵니다 수신 측에서 onDrop을 구현하여 보기를 드롭 대상으로 표시합니다 그런 다음 적절한 항목 제공자에서 파일 URL을 로드할 수 있습니다 이것은 샌드박스 외부에 있는 파일이 됩니다 앱에서 여기에 접근하려면 URL의 보안 범위를 사용하고 해제해야 합니다 다음 단계를 알아볼까요? iOS 앱을 포함하도록 샘플 코드를 업데이트했습니다 이 코드를 다운로드하고 상태를 저장하지 않는 간단한 제공자를 설정하여 실험합니다 처음부터 시작하는 경우 업데이트된 Xcode 템플릿을 사용하시기 바랍니다 템플릿에는 시작에 필요한 기본 프레임이 포함되어 있습니다 File Provider 및 이를 구현하는 방법에 대해 자세히 알아보려면 WWDC21의 'macOS에서 File Provider를 사용하여 클라우드에 파일 동기화하기'를 참고하세요 시청해 주셔서 감사합니다 iOS 기기에서 뛰어난 성능의 안정적인 Provider를 사용해 보시기 바랍니다
-
-
6:04 - Implement a Progress Cancellation Handler
// Implementing a progress cancellation handler public func modifyItem(_ item: ..., completionHandler: (..., Error?) -> Void) -> Progress { let progress = Progress() let uploadTask = Task { do { // ... try Task.checkCancellation() // ... } catch let error { completionHandler(nil, [], false, error) } } progress.cancellationHandler = { uploadTask.cancel() } return progress }
-
6:53 - Register for Push Notifications
// Registering for push notifications import PushKit let pushRegistry = PKPushRegistry(queue: queue) pushRegistry.delegate = self pushRegistry.desiredPushTypes = Set([PKPushType.fileProvider]) ... // On the server: push // // { // "container-identifier" = "NSFileProviderWorkingSetContainerItemIdentifier" // "domain" = "<domain identifier>" // } // // with topic "<your application identifier>.pushkit.fileprovider"
-
8:53 - Drag and Drop: Implement Dragging
// Sending out drags var body: some View { Text("🥐") .onDrag { let itemProvider = NSItemProvider() itemProvider.registerFileRepresentation(for: .folder, openInPlace: true) { completionHandler in self.manager.getUserVisibleURL(for: folderItemID) { fileURL, error in guard let fileURL = fileURL else { completionHandler(nil, false, error) return } completionHandler(fileURL, true, nil) } return Progress() } return itemProvider } }
-
9:24 - Drag and Drop: Implement Dropping
// Receiving drops var body: some View { Text("🥬") .onDrop(of: [.folder], isTargeted: $dropTarget) { providers in guard let prov = providers.first(where: { provider in !provider.registeredContentTypes(conformingTo: .folder).isEmpty }) else { return false } prov.loadFileRepresentation(for: .folder, openInPlace: true) { url, inPlace, err in guard let url = url else { return } Task { url.startAccessingSecurityScopedResource() // use URL url.stopAccessingSecurityScopedResource() } } return true } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.