mirror of
https://github.com/Livinglist/Hacki.git
synced 2025-08-06 18:24:42 +08:00
192 lines
6.7 KiB
Swift
192 lines
6.7 KiB
Swift
import UIKit
|
|
import MobileCoreServices
|
|
import UniformTypeIdentifiers
|
|
|
|
public class SharedMediaFile: Codable {
|
|
var path: String
|
|
var mimeType: String?
|
|
var thumbnail: String? // video thumbnail
|
|
var duration: Double? // video duration in milliseconds
|
|
var message: String? // post message
|
|
var type: SharedMediaType
|
|
|
|
public init(
|
|
path: String,
|
|
mimeType: String? = nil,
|
|
thumbnail: String? = nil,
|
|
duration: Double? = nil,
|
|
message: String?=nil,
|
|
type: SharedMediaType) {
|
|
self.path = path
|
|
self.mimeType = mimeType
|
|
self.thumbnail = thumbnail
|
|
self.duration = duration
|
|
self.message = message
|
|
self.type = type
|
|
}
|
|
}
|
|
|
|
public enum SharedMediaType: String, Codable, CaseIterable {
|
|
case image
|
|
case video
|
|
case text
|
|
case file
|
|
case url
|
|
|
|
public var toUTTypeIdentifier: String {
|
|
if #available(iOS 14.0, *) {
|
|
switch self {
|
|
case .image:
|
|
return UTType.image.identifier
|
|
case .video:
|
|
return UTType.movie.identifier
|
|
case .text:
|
|
return UTType.text.identifier
|
|
case .file:
|
|
return UTType.fileURL.identifier
|
|
case .url:
|
|
return UTType.url.identifier
|
|
}
|
|
}
|
|
switch self {
|
|
case .image:
|
|
return "public.image"
|
|
case .video:
|
|
return "public.movie"
|
|
case .text:
|
|
return "public.text"
|
|
case .file:
|
|
return "public.file-url"
|
|
case .url:
|
|
return "public.url"
|
|
}
|
|
}
|
|
}
|
|
|
|
let kSchemePrefix = "ShareMedia"
|
|
let kUserDefaultsKey = "ShareKey"
|
|
let kUserDefaultsMessageKey = "ShareMessageKey"
|
|
let kAppGroupIdKey = "AppGroupId"
|
|
|
|
class ActionViewController: UIViewController {
|
|
var hostAppBundleIdentifier = "com.jiaqi.hacki"
|
|
var appGroupId = "group.com.jiaqi.hacki"
|
|
var sharedText: [String] = []
|
|
var sharedMedia: [SharedMediaFile] = []
|
|
let urlContentType = UTType.url
|
|
@IBOutlet weak var imageView: UIImageView!
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
|
|
if let contents = content.attachments {
|
|
for (index, attachment) in (contents).enumerated() {
|
|
handleUrl(content: content, attachment: attachment, index: index)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func handleUrl (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
|
attachment.loadItem(forTypeIdentifier: urlContentType.identifier, options: nil) { [weak self] data, error in
|
|
|
|
if error == nil, let item = data as? URL, let this = self {
|
|
this.sharedText.append(item.absoluteString)
|
|
|
|
// If this is the last item, save imagesData in userDefaults and redirect to host app
|
|
if index == (content.attachments?.count)! - 1 {
|
|
this.sharedMedia.removeAll()
|
|
this.sharedMedia.append(.init(path: item.absoluteString, type: .url))
|
|
print(this.sharedText)
|
|
this.saveAndRedirect()
|
|
}
|
|
} else {
|
|
self?.dismissWithError()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func dismissWithError() {
|
|
print("[ERROR] Error loading data!")
|
|
let alert = UIAlertController(title: "Error", message: "Error loading data", preferredStyle: .alert)
|
|
|
|
let action = UIAlertAction(title: "Error", style: .cancel) { _ in
|
|
self.dismiss(animated: true, completion: nil)
|
|
}
|
|
|
|
alert.addAction(action)
|
|
present(alert, animated: true, completion: nil)
|
|
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
|
}
|
|
|
|
// Save shared media and redirect to host app
|
|
private func saveAndRedirect(message: String? = nil) {
|
|
let userDefaults = UserDefaults(suiteName: appGroupId)
|
|
userDefaults?.set(toData(data: sharedMedia), forKey: kUserDefaultsKey)
|
|
userDefaults?.set(message, forKey: kUserDefaultsMessageKey)
|
|
userDefaults?.synchronize()
|
|
redirectToHostApp()
|
|
}
|
|
|
|
private func toData(data: [SharedMediaFile]) -> Data {
|
|
let encodedData = try? JSONEncoder().encode(data)
|
|
return encodedData!
|
|
}
|
|
|
|
private func redirectToHostApp() {
|
|
// ids may not loaded yet so we need loadIds here too
|
|
loadIds()
|
|
let url = URL(string: "\(kSchemePrefix)-\(hostAppBundleIdentifier):share")
|
|
var responder = self as UIResponder?
|
|
|
|
if #available(iOS 18.0, *) {
|
|
while responder != nil {
|
|
if let application = responder as? UIApplication {
|
|
application.open(url!, options: [:], completionHandler: nil)
|
|
}
|
|
responder = responder?.next
|
|
}
|
|
} else {
|
|
let selectorOpenURL = sel_registerName("openURL:")
|
|
|
|
while (responder != nil) {
|
|
if (responder?.responds(to: selectorOpenURL))! {
|
|
_ = responder?.perform(selectorOpenURL, with: url)
|
|
}
|
|
responder = responder!.next
|
|
}
|
|
}
|
|
|
|
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
|
}
|
|
|
|
private func loadIds() {
|
|
// loading Share extension App Id
|
|
let shareExtensionAppBundleIdentifier = Bundle.main.bundleIdentifier!
|
|
|
|
|
|
// extract host app bundle id from ShareExtension id
|
|
// by default it's <hostAppBundleIdentifier>.<ShareExtension>
|
|
// for example: "com.kasem.sharing.Share-Extension" -> com.kasem.sharing
|
|
let lastIndexOfPoint = shareExtensionAppBundleIdentifier.lastIndex(of: ".")
|
|
hostAppBundleIdentifier = String(shareExtensionAppBundleIdentifier[..<lastIndexOfPoint!])
|
|
let defaultAppGroupId = "group.\(hostAppBundleIdentifier)"
|
|
|
|
|
|
// loading custom AppGroupId from Build Settings or use group.<hostAppBundleIdentifier>
|
|
let customAppGroupId = Bundle.main.object(forInfoDictionaryKey: kAppGroupIdKey) as? String
|
|
|
|
appGroupId = customAppGroupId ?? defaultAppGroupId
|
|
}
|
|
|
|
@IBAction func done() {
|
|
// Return any edited content to the host app.
|
|
// This template doesn't do anything, so we just echo the passed in items.
|
|
self.extensionContext!.completeRequest(returningItems: self.extensionContext!.inputItems, completionHandler: nil)
|
|
}
|
|
}
|