Фоновая загрузка с расширением общего доступа - PullRequest
0 голосов
/ 29 сентября 2018

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

Я все еще тестирую это, поэтому любые запросы будут отправляться на https://beeceptor.com.

Расширение общего ресурса работает нормальнои он появляется в окне предварительного просмотра, как только я его запускаю:

the share extension

Я добавляю текст и нажимаю «Опубликовать»

Creating the post

Но изображение не загружается.Это мой код, который инициирует фоновую загрузку:

let sc_uploadURL = "https://xyz.free.beeceptor.com/api/posts" // https://beeceptor.com/console/xyz

override func didSelectPost() {
    // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
    let configName = "com.shinobicontrols.ShareAlike.BackgroundSessionConfig"
    let sessionConfig = URLSessionConfiguration.background(withIdentifier: configName)
    // Extensions aren't allowed their own cache disk space. Need to share with application
    sessionConfig.sharedContainerIdentifier = "group.CreateDaily"
    let session = URLSession(configuration: sessionConfig)

    // Prepare the URL Request
    let request = urlRequestWithImage(image: attachedImage, text: contentText)

    // Create the task, and kick it off
    let task = session.dataTask(with: request! as URLRequest)
    task.resume()

    // Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
    extensionContext?.completeRequest(returningItems: [AnyObject](), completionHandler: nil)
}

private func urlRequestWithImage(image: NSImage?, text: String) -> NSURLRequest? {
    let url = URL(string: sc_uploadURL)!
    let request: NSMutableURLRequest? =  NSMutableURLRequest(url: url as URL)
    request?.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request?.addValue("application/json", forHTTPHeaderField: "Accept")
    request?.httpMethod = "POST"

    let jsonObject = NSMutableDictionary()
    jsonObject["text"] = text
    if let image = image {
        jsonObject["image_details"] = extractDetailsFromImage(image: image)
    }

    // Create the JSON payload
    let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject, options: JSONSerialization.WritingOptions.prettyPrinted)
    request?.httpBody = jsonData
    return request
}

Обратите внимание, что sharedContainerIdentifier присутствует в разрешениях приложения, а также в расширениях общего доступа.

shared-container

ShareExtensions находятся в соответствующей группе приложений и имеют разрешенные исходящие соединения.

app group and networking

Ответы [ 2 ]

0 голосов
/ 18 октября 2018

Выполнение фоновой загрузки

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

let sc_uploadURL = "http://requestb.in/oha28noh"

Это URL-адрес для службы запросов, которая дает вам временный URL-адрес, позволяющий вампроверить сетевые операции.Вышеприведенный URL-адрес (и тот, что в примере кода) не будет работать для вас, но если вы посетите requestb.in, вы можете получить свой собственный URL-адрес для тестирования.

Как упоминалось ранее, это важноэти расширения очень ограничивают системные ресурсы.Следовательно, в тот момент, когда нажата кнопка «Post», нет времени для выполнения синхронной сетевой операции переднего плана.К счастью, NSURLSession предоставляет простой API для создания фоновых сетевых операций, и это то, что вам нужно здесь.

Метод, который вызывается, когда пользователь нажимает на сообщение, - didSelectPost(), и в его простейшей формеэто должно выглядеть так:

override func didSelectPost() {
  // Perform upload
  ...

  // Inform the host that we're done, so it un-blocks its UI.
  extensionContext?.completeRequestReturningItems(nil, completionHandler: nil)
}

Настройка NSURLSession довольно стандартна:

let configName = "com.shinobicontrols.ShareAlike.BackgroundSessionConfig"
let sessionConfig = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(configName)
// Extensions aren't allowed their own cache disk space. Need to share with application
sessionConfig.sharedContainerIdentifier = "group.ShareAlike"
let session = NSURLSession(configuration: sessionConfig)

Важная часть, на которую следует обратить внимание в приведенном выше фрагменте кода, - это строка, которая устанавливаетsharedContainerIdentifier на конфигурации сеанса.Здесь указывается имя контейнера, который NSURLSession может использовать в качестве кэша (поскольку расширения не имеют собственного доступа к диску для записи).Этот контейнер должен быть настроен как часть хост-приложения (т.е. ShareAlike в этой демонстрации), и его можно выполнить с помощью Xcode:

  1. Перейти на вкладку возможностей целевого приложения
  2. Включить группы приложений
  3. Создать новую группу приложений с названием что-то подходящее.Она должна начинаться с группы. В демоверсии группа называется group.ShareAlike
  4. Пусть XCode проходит процесс создания этой группы для вас.

enter image description here

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

enter image description here

Эти приложениягруппы регистрируются под вашим идентификатором разработчика, и процесс подписания гарантирует, что только ваши приложения могут получить доступ к этим общим контейнерам.

Xcode создаст файл разрешений для каждого из ваших проектов, и он будет содержать имяобщего контейнера, к которому он имеет доступ.

Теперь, когда вы правильно настроили сеанс, вам нужно создать URL-запрос для выполнения:

// Prepare the URL Request
let request = urlRequestWithImage(attachedImage, text: contentText)

Это вызывает методкоторый создает запрос URL, который использует HTTP POST для отправки некоторого JSON, который включает содержимое строки и некоторые свойства метаданных об изображении:

func urlRequestWithImage(image: UIImage?, text: String) -> NSURLRequest? {
  let url = NSURL.URLWithString(sc_uploadURL)
  let request = NSMutableURLRequest(URL: url)
  request.addValue("application/json", forHTTPHeaderField: "Content-Type")
  request.addValue("application/json", forHTTPHeaderField: "Accept")
  request.HTTPMethod = "POST"

  var jsonObject = NSMutableDictionary()
  jsonObject["text"] = text
  if let image = image {
    jsonObject["image_details"] = extractDetailsFromImage(image)
  }

  // Create the JSON payload
  var jsonError: NSError?
  let jsonData = NSJSONSerialization.dataWithJSONObject(jsonObject, options: nil, error: &jsonError)
  if jsonData {
    request.HTTPBody = jsonData
  } else {
    if let error = jsonError {
      println("JSON Error: \(error.localizedDescription)")
    }
  }

  return request
}

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

func extractDetailsFromImage(image: UIImage) -> NSDictionary {
  var resultDict = [String : AnyObject]()
  resultDict["height"] = image.size.height
  resultDict["width"] = image.size.width
  resultDict["orientation"] = image.imageOrientation.toRaw()
  resultDict["scale"] = image.scale
  resultDict["description"] = image.description
  return resultDict
}

Наконец, вы можете попросить сеанс создать задачу, связанную с созданным вами запросом, а затем вызвать resume () длячтобы запустить его в фоновом режиме:

// Create the task, and kick it off
let task = session.dataTaskWithRequest(request!)
task.resume()

Если вы выполните этот процесс сейчас, с вашим собственным URL-адресом requestb.in, вы можете ожидать увидеть результаты, подобные этому:

enter image description here

0 голосов
/ 17 октября 2018

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

В вашем приложении и определениях прав доступа к расширению у вас есть $ (TeamIdentifierPrefix) .group.CreateDaily.Это недопустимо, так как оно не начинается с «group.».

В вашем коде у вас просто есть «group.CreateDaily».Это было бы хорошо, если бы оно соответствовало тому, что было в ваших файлах разрешений, хотя Apple рекомендует использовать обратную нотацию доменного имени, чтобы избежать конфликтов.

Я бы порекомендовал перейти на портал Apple Dev подСертификаты, идентификаторы и профили / идентификаторы / группы приложений и определить группы приложений.Apple не позволит вам ввести то, что не начинается с «группы».После того, как это будет установлено, убедитесь, что то, что у вас есть в ваших файлах и коде прав (config.sharedContainerIdentifier), совпадает, и тогда все должно работать.

...