swift - 带共享扩展的后台上传

标签 swift macos nsurlsession share-extension macos-mojave

我创建了一个 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

最佳答案

执行后台上传

一旦用户完成输入并单击“发布”按钮,扩展程序就会将内容上传到某处的某个 Web 服务。出于本示例的目的,端点的 URL 包含在 View Controller 的属性中:

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

这是 Request Bin 服务的 URL,它为您提供一个临时 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)

上面代码段中需要注意的重要部分是在 session 配置上设置 sharedContainerIdentifier 的行。这指定了 NSURLSession 可以用作缓存的容器的名称(因为扩展没有自己的可写磁盘访问权限)。此容器需要设置为主机应用程序的一部分(即本演示中的 ShareAlike),并且可以通过 Xcode 完成:

  1. 转到应用目标的功能选项卡
  2. 启用应用组
  3. 创建一个新的应用程序组,命名为适当的名称。它必须 从组开始.. 在演示中,该组称为 group.ShareAlike
  4. 让 Xcode 完成为您创建该组的过程。

enter image description here

然后你需要转到扩展的目标,并遵循相同的过程。请注意,您不需要创建新的应用程序组,而是选择您为主机应用程序创建的应用程序组。

enter image description here

这些应用组是根据您的开发者 ID 注册的,签名过程确保只有您的应用才能访问这些共享容器。

Xcode 将为您的每个项目创建一个权利文件,其中将包含它有权访问的共享容器的名称。

现在您已经正确设置了 session ,您需要创建一个 URL 请求来执行:

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

这会调用一个构造 URL 请求的方法,该 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
}

最后,您可以要求 session 创建一个与您构建的请求关联的任务,然后对其调用 resume() 以在后台启动它:

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

如果您现在运行此过程,并使用您自己的 requestb.in URL,那么您可以期望看到如下结果:

enter image description here

关于swift - 带共享扩展的后台上传,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52567979/

相关文章:

ios - 无法从 Firebase 数据库检索数据

ios - 选择时如何更改位于 tableVIewCell 上的 UIButton 的图像。在 swift 3

swift - alamofire和URLSession中上传数据key,如何添加?

ios - 通过网络代理工具下载或查看作为多部分请求(PNG、PDF)发送的文件?

macos - 如何在 mac os x 上设置 postgres 管理员用户?

swift - 使用 NSURLSession 复制 AFNetworking POST 请求

swift - Void 函数中的意外非 Void 返回值(Swift 2.0)

ios - 在 swift 或 obj-c 的 uitextview 末尾添加一个 View

cocoa - 以编程方式设置 NSTableView(捕获 tableView :objectValueForTableColumn:row:)

ios - ConnectionWithRequest 在 iOS 9.0 中被弃用