Skip to content

Commit 1dc1cce

Browse files
authored
fix: iOS share extension. (#522)
1 parent 1c87d74 commit 1dc1cce

File tree

13 files changed

+471
-256
lines changed

13 files changed

+471
-256
lines changed

ios/Action Extension/ActionViewController.swift

Lines changed: 123 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,78 @@
1-
//
2-
// ActionViewController.swift
3-
// Action Extension
4-
//
5-
// Created by Jiaqi Feng on 5/22/22.
6-
//
7-
81
import UIKit
92
import MobileCoreServices
103
import UniformTypeIdentifiers
114

5+
public class SharedMediaFile: Codable {
6+
var path: String
7+
var mimeType: String?
8+
var thumbnail: String? // video thumbnail
9+
var duration: Double? // video duration in milliseconds
10+
var message: String? // post message
11+
var type: SharedMediaType
12+
13+
public init(
14+
path: String,
15+
mimeType: String? = nil,
16+
thumbnail: String? = nil,
17+
duration: Double? = nil,
18+
message: String?=nil,
19+
type: SharedMediaType) {
20+
self.path = path
21+
self.mimeType = mimeType
22+
self.thumbnail = thumbnail
23+
self.duration = duration
24+
self.message = message
25+
self.type = type
26+
}
27+
}
28+
29+
public enum SharedMediaType: String, Codable, CaseIterable {
30+
case image
31+
case video
32+
case text
33+
case file
34+
case url
35+
36+
public var toUTTypeIdentifier: String {
37+
if #available(iOS 14.0, *) {
38+
switch self {
39+
case .image:
40+
return UTType.image.identifier
41+
case .video:
42+
return UTType.movie.identifier
43+
case .text:
44+
return UTType.text.identifier
45+
case .file:
46+
return UTType.fileURL.identifier
47+
case .url:
48+
return UTType.url.identifier
49+
}
50+
}
51+
switch self {
52+
case .image:
53+
return "public.image"
54+
case .video:
55+
return "public.movie"
56+
case .text:
57+
return "public.text"
58+
case .file:
59+
return "public.file-url"
60+
case .url:
61+
return "public.url"
62+
}
63+
}
64+
}
65+
66+
let kSchemePrefix = "ShareMedia"
67+
let kUserDefaultsKey = "ShareKey"
68+
let kUserDefaultsMessageKey = "ShareMessageKey"
69+
let kAppGroupIdKey = "AppGroupId"
70+
1271
class ActionViewController: UIViewController {
13-
let hostAppBundleIdentifier = "com.jiaqi.hacki"
14-
let sharedKey = "ShareKey"
72+
var hostAppBundleIdentifier = "com.jiaqi.hacki"
73+
var appGroupId = "group.com.jiaqi.hacki"
1574
var sharedText: [String] = []
75+
var sharedMedia: [SharedMediaFile] = []
1676
let urlContentType = UTType.url
1777
@IBOutlet weak var imageView: UIImageView!
1878

@@ -39,13 +99,11 @@ class ActionViewController: UIViewController {
3999

40100
// If this is the last item, save imagesData in userDefaults and redirect to host app
41101
if index == (content.attachments?.count)! - 1 {
42-
let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
43-
userDefaults?.set(this.sharedText, forKey: this.sharedKey)
44-
userDefaults?.synchronize()
102+
this.sharedMedia.removeAll()
103+
this.sharedMedia.append(.init(path: item.absoluteString, type: .url))
45104
print(this.sharedText)
46-
this.redirectToHostApp()
105+
this.saveAndRedirect()
47106
}
48-
49107
} else {
50108
self?.dismissWithError()
51109
}
@@ -65,25 +123,69 @@ class ActionViewController: UIViewController {
65123
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
66124
}
67125

126+
// Save shared media and redirect to host app
127+
private func saveAndRedirect(message: String? = nil) {
128+
let userDefaults = UserDefaults(suiteName: appGroupId)
129+
userDefaults?.set(toData(data: sharedMedia), forKey: kUserDefaultsKey)
130+
userDefaults?.set(message, forKey: kUserDefaultsMessageKey)
131+
userDefaults?.synchronize()
132+
redirectToHostApp()
133+
}
134+
135+
private func toData(data: [SharedMediaFile]) -> Data {
136+
let encodedData = try? JSONEncoder().encode(data)
137+
return encodedData!
138+
}
139+
68140
private func redirectToHostApp() {
69-
let url = URL(string: "ShareMedia-\(hostAppBundleIdentifier)://dataUrl=\(sharedKey)#text")
141+
// ids may not loaded yet so we need loadIds here too
142+
loadIds()
143+
let url = URL(string: "\(kSchemePrefix)-\(hostAppBundleIdentifier):share")
70144
var responder = self as UIResponder?
71-
let selectorOpenURL = sel_registerName("openURL:")
72145

73-
while (responder != nil) {
74-
if let application = responder as? UIApplication {
75-
application.performSelector(inBackground: selectorOpenURL, with: url)
146+
if #available(iOS 18.0, *) {
147+
while responder != nil {
148+
if let application = responder as? UIApplication {
149+
application.open(url!, options: [:], completionHandler: nil)
150+
}
151+
responder = responder?.next
76152
}
153+
} else {
154+
let selectorOpenURL = sel_registerName("openURL:")
77155

78-
responder = responder!.next
156+
while (responder != nil) {
157+
if (responder?.responds(to: selectorOpenURL))! {
158+
_ = responder?.perform(selectorOpenURL, with: url)
159+
}
160+
responder = responder!.next
161+
}
79162
}
163+
80164
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
81165
}
82166

167+
private func loadIds() {
168+
// loading Share extension App Id
169+
let shareExtensionAppBundleIdentifier = Bundle.main.bundleIdentifier!
170+
171+
172+
// extract host app bundle id from ShareExtension id
173+
// by default it's <hostAppBundleIdentifier>.<ShareExtension>
174+
// for example: "com.kasem.sharing.Share-Extension" -> com.kasem.sharing
175+
let lastIndexOfPoint = shareExtensionAppBundleIdentifier.lastIndex(of: ".")
176+
hostAppBundleIdentifier = String(shareExtensionAppBundleIdentifier[..<lastIndexOfPoint!])
177+
let defaultAppGroupId = "group.\(hostAppBundleIdentifier)"
178+
179+
180+
// loading custom AppGroupId from Build Settings or use group.<hostAppBundleIdentifier>
181+
let customAppGroupId = Bundle.main.object(forInfoDictionaryKey: kAppGroupIdKey) as? String
182+
183+
appGroupId = customAppGroupId ?? defaultAppGroupId
184+
}
185+
83186
@IBAction func done() {
84187
// Return any edited content to the host app.
85188
// This template doesn't do anything, so we just echo the passed in items.
86189
self.extensionContext!.completeRequest(returningItems: self.extensionContext!.inputItems, completionHandler: nil)
87190
}
88-
89191
}

ios/Share Extension/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
<dict>
99
<key>NSExtensionActivationRule</key>
1010
<dict>
11+
<key>NSExtensionActivationSupportsText</key>
12+
<true/>
1113
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
1214
<integer>1</integer>
1315
</dict>

0 commit comments

Comments
 (0)