SwiftUI - используйте средство выбора файлов, чтобы открыть файл, принадлежащий другому IOS приложению, а затем получить прямой доступ к этому файлу - PullRequest
0 голосов
/ 06 мая 2020

Я разрабатываю приложение IOS для IPAD на максимально возможном родном языке Swift (с использованием Xcode 11.4.1, Swift 5, таргетинг на IOS 13.X). Я не знаю Cocoa или Objective- C, и у меня нет предыдущего опыта IOS. Я исхожу из C / C ++ и Android / Java фона. Тем не менее, мне очень нравится Swift и то, насколько быстро он позволяет мне заниматься разработкой. Вещи, которые я ожидал быть трудными, оказались легкими, но одна вещь, которая мне кажется, что это должно быть легко, - это стена, которую я не могу преодолеть. Короче говоря, я пытаюсь разрешить своим пользователям использовать средство выбора файлов для выбора документа со своего iPad, который мое приложение затем загрузит на сервер веб-сайта.

Мой пример использования - это частный веб-сайт. платформа для совместной работы - пользователи пишут документы на своих ноутбуках, обычно с помощью Word, а затем загружают эти документы на веб-сервер, используя типичный сценарий HTTP POST. Раньше моя платформа была только для Интернета; Теперь я пытаюсь написать приложение IOS, чтобы обеспечить более естественный опыт для пользователей IPad.

Я представляю себе эту работу так: пользователи будут писать документы, используя (например) Microsoft Word на iPad, а затем из моего приложения Swift IOS пользователь загрузит свои документы Word на сервер.

Моя проблема связана с правами доступа. Я использую следующий код для средства выбора документов:

import SwiftUI
import MobileCoreServices

struct DocPicker: UIViewControllerRepresentable {

    var callback: (URL) -> ()
    private let onDismiss: () -> Void

    init(callback: @escaping (URL) -> (), onDismiss: @escaping () -> Void) {
        self.callback = callback
        self.onDismiss = onDismiss
    }

    func makeCoordinator() -> Coordinator { Coordinator(self) }

    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocPicker>) {
    }

    func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
        let controller = UIDocumentPickerViewController(documentTypes: [kUTTypeItem as String], in: UIDocumentPickerMode.import)
        controller.allowsMultipleSelection = false
        controller.delegate = context.coordinator
        return controller
    }

    class Coordinator: NSObject, UIDocumentPickerDelegate {
        var parent: DocPicker
        init(_ pickerController: DocPicker) {
            self.parent = pickerController
        }
        func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
            parent.callback(urls[0])
            parent.onDismiss()
        }
        func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
            parent.onDismiss()
        }
    }
}

Я вызываю средство выбора документов, используя этот фрагмент кода, подвешенный к VStack:

.sheet(isPresented: $showFilePicker, onDismiss: {self.showFilePicker = false}) {
            DocPicker(callback: self.readyFile(_:), onDismiss: { self.showFilePicker = false })

И, когда выполняется обратный вызов, Я действительно получаю обратно URL-адрес, который, кажется, после длинного компонента пути, подобного UUID, указывает на выбранный файл ... или, по крайней мере, последний компонент URL-адреса совпадает с именем выбранного мной файла.

файл: ///private/var/mobile/Containers/Data/Application/4B63589A-7037-4105-99F1-427444B27F40/tmp/org.me.myapp-Inbox/Document.docx

Итак - мое приложение запускается, я выбираю функцию загрузки, открывается сборщик. Я перехожу в каталог документов Microsoft Word, нахожу сохраненный файл и выбираю его. Все хорошо.

Однако, когда я перехожу к функции загрузки, я обнаруживаю ошибку. Я создаю сообщение multipart / form-data, используя методы, которые, кажется, используют все, но когда я пытаюсь ввести фактические данные для файла сюда:

do {
            data1 = try NSData.init(contentsOf: URL.init(fileURLWithPath: fileURL, isDirectory: true)) as Data
        }
        catch { print("Error \(error)") }

, я получаю сообщение об ошибке «Файл не найден» в окне отладки Xcode:

file: ///private/var/mobile/Containers/Data/Application/4B63589A-7037-4105-99F1-427444B27F40/tmp/org.me.myapp-Inbox /Document.docx Error Error Domain = NSCocoaErrorDomain Code = 260 «Невозможно открыть файл« Document.docx », потому что такого файла нет». UserInfo = {NSFilePath = / file: /private/var/mobile/Containers/Data/Application/4B63589A-7037-4105-99F1-427444B27F40/tmp/org.me.myapp-Inbox/Document.docx, NSUnderlyingError = 0x283039bf0 {Error} Domain = NSPOSIXErrorDomain Code = 2 «Нет такого файла или каталога»}}

Я читал комментарии к нескольким другим вопросам - которые были неуместны, но все равно привлекли мое внимание - к которым можно получить доступ файл в этом случае необходимо «скопировать в песочницу приложения». Когда средство выбора запускается и пользователь выбирает файл, я отмечаю это в отладочных данных:

Не удалось связать эскизы для выбранного файла URL: /// private / var / mobile / Containers / Data / Application / 83576950-92C8-442 C -83B5-8320C037112F / Documents / Document.docx с файлом копии папки «Входящие»: /// private / var / mobile / Containers / Data / Application / 4B63589A-7037-4105-99F1-427444B27F40 /tmp/org.me.myapp-Inbox/Document.docx: Error Domain = QLThumbnailErrorDomain Code = 102 "(null)" UserInfo = {NSUnderlyingError = 0x2830224c0 {Error Domain = GSLibraryErrorDomain Code = 3 "Generation not found" UserInfo = {NSDescription = Генерация не найдена}}}

В нем говорится о "файле копии папки" Входящие ", и это тот URL-адрес" копии ", который средство выбора отправляет мне обратно, но сам файл, похоже, не существует .

Теперь я должен отметить, что я уже реализовал функцию «Загрузить изображение», аналогичную моей функции «Загрузить файл», за исключением того, что функция «Загрузить изображение» использует ImagePicker. В этом случае я получаю изображение обратно из средства выбора, я могу получить доступ к изображению, и оно отлично загружается, без проблем. Проблема находится где-то в коде, который я опубликовал выше ... или в моем понимании, который вполне может быть ограничен.

На этой странице на веб-сайте Apple говорится о том, чтобы попросить пользователя выберите каталог, но их фрагмент кода, похоже, не работает, когда я пытаюсь вставить его (похоже, повторяющаяся тема для меня), поэтому я не могу продолжить с ним.

Вопрос о переполнении стека 47902995 использует другой подход, говорит о ключах безопасности, но, похоже, полагается на Какао, а этот вопрос имеет фрагмент кода, но я не вижу, как его ie в мой существующий сборщик.

Сводка:

В Android (извините), я могу дать своему приложению «разрешение», которое позволяет ему просматривать все ( open) файлы на внутренней памяти устройства. Я понимаю, что IOS работает по-другому, но я не понимаю как это другое. Я недостаточно (пока) знаю о Xcode / IOS / Swift / SwiftUI, чтобы понимать все нюансы, и до сих пор работаю довольно слепо.

Итак, мой вопрос вкратце: может кто-нибудь, пожалуйста, скажите мне, как изменить мой код, чтобы пользователь мог открыть документ, созданный Microsoft-Office в локальном хранилище устройства, и прочитать его, чтобы затем он мог быть загружен моим приложением без нарушения каких-либо правил и ошибок?

TIA

Ответы [ 2 ]

0 голосов
/ 12 мая 2020

Итак, что касается архива, оказалось, что мой исходный код средства выбора документов работал - и работал правильно - так же, как я его предоставил.

Проблема оказалась в том, как я обрабатывал ответ.

Мой исходный неправильный код был:

do {
            data1 = try NSData.init(contentsOf: URL.init(fileURLWithPath: fileURL, isDirectory: true)) as Data
        }
        catch { print("Error \(error)") }

, и это было проблемой. Оказывается, чтобы получить поток данных из файла, все, что мне нужно было сделать, это:

do {
            data1 = try Data(contentsOf: fileURL!)
        }
        catch { print("Error \(error)") }

Вот и все! Когда я это сделал, я смог прочитать содержимое файла и правильно загрузить его через POST на свой сервер.

Спасибо всем, кто прочитал мой вопрос, и Prafulla за их ответ!

0 голосов
/ 06 мая 2020

В iOS, вы не можете получить доступ к чему-либо за пределами песочницы приложения без указания c разрешения приложения.

Если вы хотите выбрать документ из локального файла пользователя, iCloud или Google диска (если пользователь он установлен) или любое другое облачное хранилище, необходимое для использования MobileCoreServices.

Ниже приведен ответ, в котором приведены шаги Как это сделать:

{ ссылка }

Вышеупомянутый ответ - это WRT UIKit, а не SwiftUI. Что предоставит вам UIDocumentMenuViewController. Теперь вам нужно обернуть этот контроллер, используя следующее, чтобы он работал со SwiftUI.

https://www.hackingwithswift.com/books/ios-swiftui/wrapping-a-uiviewcontroller-in-a-swiftui-view

...