我创建了要用于上传图片的macOS ShareExtension。
我仍在测试,因此所有请求都将发送到https://beeceptor.com。
共享扩展名运行良好,并在运行后显示在预览中:
我添加一些文字并点击“发布”
但是图像然后没有上传。 这是我启动后台上传的代码:
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
出现在应用程序的权限以及共享扩展程序的权限中。
ShareExtensions位于相应的 App组中,并且启用了传出连接。
答案 0 :(得分:3)
执行后台上传
用户完成输入后,单击“发布”按钮,则扩展程序应将内容上传到某处的某些Web服务。出于本示例的目的,端点的URL包含在视图控制器上的属性内:
let sc_uploadURL = "http://requestb.in/oha28noh"
这是Request Bin服务的URL,它为您提供一个临时URL,以便您测试网络操作。上面的URL(以及示例代码中的一个)对您不起作用,但是如果您访问requestb.in,则可以保留自己的URL进行测试。
如前所述,扩展对有限的系统资源的压力应很小,这一点很重要。因此,在点按“发布”按钮时,没有时间执行同步的前台网络操作。幸运的是,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完成:
然后,您需要转到扩展程序的目标并遵循相同的过程。请注意,您不需要创建新的应用程序组,而是选择为主机应用程序创建的应用程序组。
这些应用组是根据您的开发人员ID注册的,并且签名过程可确保只有您的应用才能访问这些共享容器。
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()
如果您现在使用自己的requestb.in URL运行此过程,那么您可以期望看到如下结果:
答案 1 :(得分:1)
应用组标识符必须以“组” 开始 。并且必须与使用的所有位置匹配-在权利文件,代码中以及Apple Dev门户中。
在您的应用程序和共享扩展名权利定义中,您具有$(TeamIdentifierPrefix).group.CreateDaily。这是无效的,因为它不是以“ group。”开头。
在您的代码中,您只有“ group.CreateDaily”。如果它与您的权利文件中的内容相匹配,那会很好,尽管Apple建议使用反向域名表示法以避免冲突。
我的建议是转到“证书,标识符和配置文件/标识符/应用组”下的Apple Dev portal并定义您的应用组。 Apple不允许您输入不以“ group”开头的内容。设置完成后,请确保授权文件和代码(config.sharedContainerIdentifier)中的内容匹配,然后一切正常。