Skip to content

Commit

Permalink
refactor(ios): new share intent implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
andelf committed May 23, 2023
1 parent 8ec5ea7 commit 016fd2b
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 121 deletions.
12 changes: 8 additions & 4 deletions ios/App/App.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
D3D62A0C275C928F0003FBDC /* FileContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D62A0B275C928F0003FBDC /* FileContainer.m */; };
FE647FF427BDFEDE00F3206B /* FsWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE647FF327BDFEDE00F3206B /* FsWatcher.swift */; };
FE647FF627BDFEF500F3206B /* FsWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = FE647FF527BDFEF500F3206B /* FsWatcher.m */; };
FE96D6102A1B811A001ECE32 /* SharedData.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE96D60F2A1B811A001ECE32 /* SharedData.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -83,6 +84,7 @@
DE5650F4AD4E2242AB9C012D /* Pods-Logseq.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Logseq.debug.xcconfig"; path = "Target Support Files/Pods-Logseq/Pods-Logseq.debug.xcconfig"; sourceTree = "<group>"; };
FE647FF327BDFEDE00F3206B /* FsWatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FsWatcher.swift; sourceTree = "<group>"; };
FE647FF527BDFEF500F3206B /* FsWatcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FsWatcher.m; sourceTree = "<group>"; };
FE96D60F2A1B811A001ECE32 /* SharedData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedData.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -156,6 +158,7 @@
children = (
5FFF7D7927E4E70700B00DA8 /* ShareViewController.entitlements */,
5FFF7D6C27E343FA00B00DA8 /* ShareViewController.swift */,
FE96D60F2A1B811A001ECE32 /* SharedData.swift */,
5FFF7D6E27E343FA00B00DA8 /* MainInterface.storyboard */,
5FFF7D7127E343FA00B00DA8 /* Info.plist */,
);
Expand Down Expand Up @@ -345,6 +348,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FE96D6102A1B811A001ECE32 /* SharedData.swift in Sources */,
5FFF7D6D27E343FA00B00DA8 /* ShareViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -438,7 +442,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -492,7 +496,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
Expand Down Expand Up @@ -565,7 +569,7 @@
INFOPLIST_FILE = ShareViewController/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareViewController;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 0.9.6;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
Expand All @@ -592,7 +596,7 @@
INFOPLIST_FILE = ShareViewController/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareViewController;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 0.9.6;
MTL_FAST_MATH = YES;
Expand Down
6 changes: 4 additions & 2 deletions ios/App/ShareViewController/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationDictionaryVersion</key>
<integer>2</integer>
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
<integer>5</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
Expand All @@ -17,9 +19,9 @@
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
<integer>1</integer>
<integer>3</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<integer>3</integer>
<key>NSExtensionActivationUsesStrictMatching</key>
<false/>
</dict>
Expand Down
228 changes: 117 additions & 111 deletions ios/App/ShareViewController/ShareViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,182 +9,180 @@
import MobileCoreServices
import Social
import UIKit

class ShareItem {
public var title: String?
public var type: String?
public var url: String?
}
import UniformTypeIdentifiers

class ShareViewController: UIViewController {
private var shareItems: [ShareItem] = []

private var sharedData: SharedData = SharedData.init(resources: [])

var groupContainerUrl: URL? {
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.logseq.logseq")
}

override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
}

private func sendData() {
let queryItems = shareItems.map {
let encoder: JSONEncoder = JSONEncoder()
let data = try? encoder.encode(self.sharedData)
let queryPayload = String(decoding: data!, as: UTF8.self)

let queryItems =
[
URLQueryItem(
name: "title",
value: $0.title?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""),
URLQueryItem(name: "description", value: ""),
URLQueryItem(
name: "type",
value: $0.type?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""),
URLQueryItem(
name: "url",
value: $0.url?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""),
name: "payload",
value: queryPayload.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""),
]
}.flatMap({ $0 })
var urlComps = URLComponents(string: "logseq://shared?")!
urlComps.queryItems = queryItems
openURL(urlComps.url!)
}

fileprivate func createSharedFileUrl(_ url: URL?) -> String {

let copyFileUrl = groupContainerUrl!.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! + "/" + url!

fileprivate func createSharedFileUrl(_ url: URL?) -> URL? {
let tempFilename = url!
.lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
try? Data(contentsOf: url!).write(to: URL(string: copyFileUrl)!)

let copyFileUrl = groupContainerUrl!.appendingPathComponent(tempFilename)
try? Data(contentsOf: url!).write(to: copyFileUrl)
return copyFileUrl
}
func saveScreenshot(_ image: UIImage) -> String {

// Screenshots, shared images from some system App are passed as UIImage
func saveUIImage(_ image: UIImage) -> URL? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd-HH-mm-ss"

let copyFileUrl = groupContainerUrl!.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
+ dateFormatter.string(from: Date()) + ".png"
let filename = dateFormatter.string(from: Date()) + ".png"

let copyFileUrl = groupContainerUrl!.appendingPathComponent(filename)

do {
try image.pngData()?.write(to: URL(string: copyFileUrl)!)
try image.pngData()?.write(to: copyFileUrl)
return copyFileUrl
} catch {
print(error.localizedDescription)
return ""
return nil
}
}


// Can be a path or a web URL
fileprivate func handleTypeUrl(_ attachment: NSItemProvider)
async throws -> ShareItem
async throws -> SharedResource
{
let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil)
let url = results as! URL?
let shareItem: ShareItem = ShareItem()


var res = SharedResource()

if url!.isFileURL {
shareItem.title = url!.lastPathComponent
shareItem.type = "application/" + url!.pathExtension.lowercased()
shareItem.url = createSharedFileUrl(url)
res.name = url!.lastPathComponent
res.ext = url!.pathExtension
res.type = url!.pathExtensionAsMimeType()
res.url = createSharedFileUrl(url)
} else {
shareItem.title = url!.absoluteString
shareItem.url = url!.absoluteString
shareItem.type = "text/plain"
res.name = url!.absoluteString
res.type = "text/plain"
}
return shareItem

return res
}

fileprivate func handleTypeText(_ attachment: NSItemProvider)
async throws -> ShareItem
async throws -> SharedResource?
{
let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil)
let shareItem: ShareItem = ShareItem()
let text = results as! String
shareItem.title = text
shareItem.type = "text/plain"

return shareItem
let item = try await attachment.loadItem(forTypeIdentifier: kUTTypeText as String, options: nil)
self.sharedData.text = item as? String
return nil
}

fileprivate func handleTypeMovie(_ attachment: NSItemProvider)
async throws -> ShareItem
async throws -> SharedResource
{
let results = try await attachment.loadItem(forTypeIdentifier: kUTTypeMovie as String, options: nil)
let shareItem: ShareItem = ShareItem()


let url = results as! URL?
shareItem.title = url!.lastPathComponent
shareItem.type = "video/" + url!.pathExtension.lowercased()
shareItem.url = createSharedFileUrl(url)

return shareItem

let name = url!.lastPathComponent
let ext = url!.pathExtension.lowercased()
let type = url!.pathExtensionAsMimeType()
let sharedUrl = createSharedFileUrl(url)

let res = SharedResource(name: name, ext: ext, type: type, url: sharedUrl)

return res
}

fileprivate func handleTypeImage(_ attachment: NSItemProvider)
async throws -> ShareItem
async throws -> SharedResource
{
let data = try await attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil)

let shareItem: ShareItem = ShareItem()

var res = SharedResource()

switch data {
case let image as UIImage:
shareItem.title = "screenshot"
shareItem.type = "image/png"
shareItem.url = self.saveScreenshot(image)
res.url = self.saveUIImage(image)
res.ext = "png"
res.name = res.url?.lastPathComponent
res.type = res.url?.pathExtensionAsMimeType()
case let url as URL:
shareItem.title = url.lastPathComponent
shareItem.type = "image/" + url.pathExtension.lowercased()
shareItem.url = self.createSharedFileUrl(url)
res.name = url.lastPathComponent
res.ext = url.pathExtension.lowercased()
res.type = url.pathExtensionAsMimeType()
res.url = self.createSharedFileUrl(url)
default:
print("Unexpected image data:", type(of: data))
}
return shareItem

return res
}


override public func viewDidLoad() {
super.viewDidLoad()

shareItems.removeAll()

let extensionItem = extensionContext?.inputItems.first as! NSExtensionItem

sharedData.empty()
let inputItems = extensionContext?.inputItems as! [NSExtensionItem]
Task {
try await withThrowingTaskGroup(
of: ShareItem.self,
of: SharedResource?.self,
body: { taskGroup in

for attachment in extensionItem.attachments! {
if attachment.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
taskGroup.addTask {
return try await self.handleTypeUrl(attachment)
}
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
taskGroup.addTask {
return try await self.handleTypeText(attachment)
}
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeMovie as String) {
taskGroup.addTask {
return try await self.handleTypeMovie(attachment)
}
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
taskGroup.addTask {
return try await self.handleTypeImage(attachment)
for extensionItem in inputItems {
for attachment in extensionItem.attachments! {
if attachment.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
taskGroup.addTask {
return try await self.handleTypeUrl(attachment)
}
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
taskGroup.addTask {
return try await self.handleTypeText(attachment)
}
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeMovie as String) {
taskGroup.addTask {
return try await self.handleTypeMovie(attachment)
}
} else if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
taskGroup.addTask {
return try await self.handleTypeImage(attachment)
}
}
}
}

for try await item in taskGroup {
self.shareItems.append(item)
if let item = item {
self.sharedData.resources.append(item)
}
}
})

self.sendData()

}
}

@discardableResult
@objc func openURL(_ url: URL) -> Bool {
var responder: UIResponder? = self
Expand All @@ -196,6 +194,14 @@ class ShareViewController: UIViewController {
}
return false
}



}

extension URL {
func pathExtensionAsMimeType() -> String? {
let type = UTType(filenameExtension: self.pathExtension)
return type?.preferredMIMEType
}
}

Loading

0 comments on commit 016fd2b

Please sign in to comment.