154 lines
4.9 KiB
Swift
154 lines
4.9 KiB
Swift
|
import UIKit
|
||
|
|
||
|
class ShareViewController: UIViewController {
|
||
|
// This allows other forks to use this extension while also changing their
|
||
|
// scheme.
|
||
|
let appScheme = Bundle.main.object(forInfoDictionaryKey: "MainAppScheme") as? String ?? "bluesky"
|
||
|
|
||
|
//
|
||
|
override func viewDidAppear(_ animated: Bool) {
|
||
|
super.viewDidAppear(animated)
|
||
|
|
||
|
guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem,
|
||
|
let attachments = extensionItem.attachments,
|
||
|
let firstAttachment = extensionItem.attachments?.first
|
||
|
else {
|
||
|
self.completeRequest()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
Task {
|
||
|
if firstAttachment.hasItemConformingToTypeIdentifier("public.text") {
|
||
|
await self.handleText(item: firstAttachment)
|
||
|
} else if firstAttachment.hasItemConformingToTypeIdentifier("public.url") {
|
||
|
await self.handleUrl(item: firstAttachment)
|
||
|
} else if firstAttachment.hasItemConformingToTypeIdentifier("public.image") {
|
||
|
await self.handleImages(items: attachments)
|
||
|
} else {
|
||
|
self.completeRequest()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private func handleText(item: NSItemProvider) async -> Void {
|
||
|
do {
|
||
|
if let data = try await item.loadItem(forTypeIdentifier: "public.text") as? String {
|
||
|
if let encoded = data.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
|
||
|
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)")
|
||
|
{
|
||
|
_ = self.openURL(url)
|
||
|
}
|
||
|
}
|
||
|
self.completeRequest()
|
||
|
} catch {
|
||
|
self.completeRequest()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private func handleUrl(item: NSItemProvider) async -> Void {
|
||
|
do {
|
||
|
if let data = try await item.loadItem(forTypeIdentifier: "public.url") as? URL {
|
||
|
if let encoded = data.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
|
||
|
let url = URL(string: "\(self.appScheme)://intent/compose?text=\(encoded)")
|
||
|
{
|
||
|
_ = self.openURL(url)
|
||
|
}
|
||
|
}
|
||
|
self.completeRequest()
|
||
|
} catch {
|
||
|
self.completeRequest()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private func handleImages(items: [NSItemProvider]) async -> Void {
|
||
|
let firstFourItems: [NSItemProvider]
|
||
|
if items.count < 4 {
|
||
|
firstFourItems = items
|
||
|
} else {
|
||
|
firstFourItems = Array(items[0...3])
|
||
|
}
|
||
|
|
||
|
var valid = true
|
||
|
var imageUris = ""
|
||
|
|
||
|
for (index, item) in firstFourItems.enumerated() {
|
||
|
var imageUriInfo: String? = nil
|
||
|
|
||
|
do {
|
||
|
if let dataUri = try await item.loadItem(forTypeIdentifier: "public.image") as? URL {
|
||
|
// We need to duplicate this image, since we don't have access to the outgoing temp directory
|
||
|
// We also will get the image dimensions here, sinze RN makes it difficult to get those dimensions for local files
|
||
|
let data = try Data(contentsOf: dataUri)
|
||
|
let image = UIImage(data: data)
|
||
|
imageUriInfo = self.saveImageWithInfo(image)
|
||
|
} else if let image = try await item.loadItem(forTypeIdentifier: "public.image") as? UIImage {
|
||
|
imageUriInfo = self.saveImageWithInfo(image)
|
||
|
}
|
||
|
} catch {
|
||
|
valid = false
|
||
|
}
|
||
|
|
||
|
if let imageUriInfo = imageUriInfo {
|
||
|
imageUris.append(imageUriInfo)
|
||
|
if index < items.count - 1 {
|
||
|
imageUris.append(",")
|
||
|
}
|
||
|
} else {
|
||
|
valid = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if valid,
|
||
|
let encoded = imageUris.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
|
||
|
let url = URL(string: "\(self.appScheme)://intent/compose?imageUris=\(encoded)")
|
||
|
{
|
||
|
_ = self.openURL(url)
|
||
|
}
|
||
|
|
||
|
self.completeRequest()
|
||
|
}
|
||
|
|
||
|
private func saveImageWithInfo(_ image: UIImage?) -> String? {
|
||
|
guard let image = image else {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
// Saving this file to the bundle group's directory lets us access it from
|
||
|
// inside of the app. Otherwise, we wouldn't have access even though the
|
||
|
// extension does.
|
||
|
if let dir = FileManager()
|
||
|
.containerURL(
|
||
|
forSecurityApplicationGroupIdentifier: "group.\(Bundle.main.bundleIdentifier?.replacingOccurrences(of: ".Share-with-Bluesky", with: "") ?? "")")
|
||
|
{
|
||
|
let filePath = "\(dir.absoluteString)\(ProcessInfo.processInfo.globallyUniqueString).jpeg"
|
||
|
|
||
|
if let newUri = URL(string: filePath),
|
||
|
let jpegData = image.jpegData(compressionQuality: 1)
|
||
|
{
|
||
|
try jpegData.write(to: newUri)
|
||
|
return "\(newUri.absoluteString)|\(image.size.width)|\(image.size.height)"
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
} catch {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private func completeRequest() -> Void {
|
||
|
self.extensionContext?.completeRequest(returningItems: nil)
|
||
|
}
|
||
|
|
||
|
@objc func openURL(_ url: URL) -> Bool {
|
||
|
var responder: UIResponder? = self
|
||
|
while responder != nil {
|
||
|
if let application = responder as? UIApplication {
|
||
|
return application.perform(#selector(openURL(_:)), with: url) != nil
|
||
|
}
|
||
|
responder = responder?.next
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
}
|