代码之家  ›  专栏  ›  技术社区  ›  Besi

带共享扩展的后台上载

  •  7
  • Besi  · 技术社区  · 6 年前

    我创建了一个macOS共享扩展,我想用它上传图片。

    https://beeceptor.com .

    共享扩展工作正常,一旦我运行它,它就会显示在预览中:

    the share extension

    我加了一些文字然后点击“Post”

    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 回复  |  直到 6 年前
        1
  •  7
  •   Kousic    6 年前

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

    这是requestbin服务的URL,它提供了一个临时URL,允许您测试网络操作。上面的URL(以及示例代码中的URL)对您不起作用,但是如果您访问requestb.in,那么您可以获得自己的URL进行测试。

    如前所述,重要的是扩展对有限的系统资源施加的压力很小。因此,在点击Post按钮时,没有时间执行同步的前台网络操作。幸运的是, NSURLSession

    当用户点击post时调用的方法是 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可以用作缓存的容器的名称(因为扩展没有自己的可写磁盘访问)。此容器需要作为主机应用程序的一部分进行设置(即本演示中的shareslike),可以通过Xcode完成:

    1. 启用应用程序组
    2. 让Xcode完成为您创建这个组的过程。

    enter image description here

    enter image description here

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

    // 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
    }
    

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

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

    enter image description here

        2
  •  1
  •   Paul King    6 年前

    开始 与“group.”匹配,并且必须与使用它的任何地方匹配-在授权文件中、在您的代码中以及在Apple Dev门户上。

    在应用程序和共享扩展权限定义中,您有$(TeamIdentifierPrefix).group.CreateDaily。这是无效的,因为它不是以“group.”开头的。

    在代码中,只有“group.CreateDaily”。如果它与您的授权文件中的内容相匹配,这将很好,不过苹果建议使用反向域名符号来避免冲突。

    我的建议是去医院 Apple Dev portal 在证书、标识符和;配置文件/标识符/AppGroups并定义应用程序组。苹果不会让你输入不以“组”开头的内容。设置完成后,请确保您的授权文件和代码(config.sharedContainerIdentifier)中的内容匹配,然后一切正常。