Improve handling of unexpected types.

pull/1/head
Matthew Chen 7 years ago
parent d1c17167c6
commit 9b5327cc8b

@ -482,14 +482,9 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE
return kUTTypeContact as String
}
// Order matters if we want to take advantage of share conversion in loadItem,
// Though currently we just use "data" for most things and rely on our SignalAttachment
// class to convert types for us.
let utiTypes: [String] = [kUTTypeImage as String,
kUTTypeData as String]
let matchingUtiType = utiTypes.first { (utiType: String) -> Bool in
itemProvider.hasItemConformingToTypeIdentifier(utiType)
// Use the first UTI that conforms to "data".
let matchingUtiType = itemProvider.registeredTypeIdentifiers.first { (utiType: String) -> Bool in
UTTypeConformsTo(utiType as CFString, kUTTypeData)
}
return matchingUtiType
}
@ -548,25 +543,27 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE
var customFileName: String?
var isConvertibleToTextMessage = false
itemProvider.loadItem(forTypeIdentifier: srcUtiType, options: nil, completionHandler: {
(provider, error) in
let loadCompletion: NSItemProvider.CompletionHandler = {
(value, error) in
guard error == nil else {
reject(error!)
return
}
guard let provider = provider else {
guard let value = value else {
let missingProviderError = ShareViewControllerError.assertionError(description: "missing item provider")
reject(missingProviderError)
return
}
Logger.info("\(self.logTag) provider type: \(type(of:provider))")
Logger.info("\(self.logTag) value type: \(type(of:value))")
if let data = provider as? Data {
if let data = value as? Data {
// Although we don't support contacts _yet_, when we do we'll want to make
// sure they are shared with a reasonable filename.
if ShareViewController.itemMatchesSpecificUtiType(itemProvider:itemProvider,
utiType:kUTTypeVCard as String) {
utiType:kUTTypeVCard as String) {
customFileName = "Contact.vcf"
}
@ -578,7 +575,7 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE
}
let fileUrl = URL(fileURLWithPath:tempFilePath)
fulfill((fileUrl, srcUtiType))
} else if let string = provider as? String {
} else if let string = value as? String {
Logger.debug("\(self.logTag) string provider: \(string)")
guard let data = string.data(using: String.Encoding.utf8) else {
let writeError = ShareViewControllerError.assertionError(description: "Error writing item data: \(String(describing: error))")
@ -600,18 +597,32 @@ public class ShareViewController: UINavigationController, ShareViewDelegate, SAE
} else {
fulfill((fileUrl, kUTTypeText as String))
}
} else if let url = provider as? URL {
isConvertibleToTextMessage = !itemProvider.registeredTypeIdentifiers.contains(kUTTypeFileURL as String)
fulfill((url, srcUtiType))
} else if let url = value as? URL {
// If the share itself is a URL (e.g. a link from Safari), try to send this as a text message.
isConvertibleToTextMessage = (itemProvider.registeredTypeIdentifiers.contains(kUTTypeURL as String) &&
!itemProvider.registeredTypeIdentifiers.contains(kUTTypeFileURL as String))
if isConvertibleToTextMessage {
fulfill((url, kUTTypeURL as String))
} else {
fulfill((url, srcUtiType))
}
} else {
let unexpectedTypeError = ShareViewControllerError.assertionError(description: "unexpected item type: \(String(describing: provider))")
let unexpectedTypeError = ShareViewControllerError.assertionError(description: "unexpected value: \(String(describing: value))")
reject(unexpectedTypeError)
}
})
}
// NSItemProvider.loadItem(forTypeIdentifier:...) is unsafe to call from Swift,
// since it can yield values of arbitrary type. It has a highly unusual design
// in which its behavior depends on the _type_ of the completion handler.
// loadItem(forTypeIdentifier:...) tries to satisfy the expected type of the
// completion handler. This "hinting" only works in Objective-C. In Swift,
// The type of the completion handler must agree with the param type.
//
// Unfortunately, we have no alternative. Therefore, we face the real possibility
// of receiving an "unexpected type" that we don't know how to handle.
itemProvider.loadItem(forTypeIdentifier: srcUtiType, options: nil, completionHandler: loadCompletion)
// TODO accept other data types
// TODO whitelist attachment types
// TODO coerce when necessary and possible
return promise.then { (itemUrl: URL, utiType: String) -> Promise<SignalAttachment> in
let url: URL = try {

Loading…
Cancel
Save