Add iOS Share Extension support

- Create share extension to handle shared content from other apps
- Support sharing text, URLs, and prepare for future image support
- Use app groups for data sharing between extension and main app
- Add URL scheme handling for app communication
- Add entitlements for app groups
- Display system message when content is shared
- Update project configuration to include share extension
This commit is contained in:
jack
2025-07-08 04:18:42 +02:00
parent 2afc32afbf
commit 7f6a32e350
7 changed files with 281 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>bitchat</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>
@@ -0,0 +1,122 @@
//
// ShareViewController.swift
// bitchatShareExtension
//
// This is free and unencumbered software released into the public domain.
// For more information, see <https://unlicense.org>
//
import UIKit
import Social
import UniformTypeIdentifiers
class ShareViewController: SLComposeServiceViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set placeholder text
placeholder = "Share to bitchat..."
// Set character limit (optional)
charactersRemaining = 500
}
override func isContentValid() -> Bool {
// Validate that we have text content or attachments
if let text = contentText, !text.isEmpty {
return true
}
// Check if we have attachments
if let item = extensionContext?.inputItems.first as? NSExtensionItem,
let attachments = item.attachments,
!attachments.isEmpty {
return true
}
return false
}
override func didSelectPost() {
// Get the shared content
guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem else {
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
return
}
// Process different types of shared content
for itemProvider in extensionItem.attachments ?? [] {
if itemProvider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
itemProvider.loadItem(forTypeIdentifier: UTType.plainText.identifier, options: nil) { [weak self] (item, error) in
if let text = item as? String {
self?.handleSharedText(text)
}
}
} else if itemProvider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
itemProvider.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) { [weak self] (item, error) in
if let url = item as? URL {
self?.handleSharedURL(url)
}
}
} else if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
itemProvider.loadItem(forTypeIdentifier: UTType.image.identifier, options: nil) { [weak self] (item, error) in
if let image = item as? UIImage {
self?.handleSharedImage(image)
} else if let data = item as? Data {
if let image = UIImage(data: data) {
self?.handleSharedImage(image)
}
}
}
}
}
// If we have content text, share it
if let text = contentText, !text.isEmpty {
handleSharedText(text)
}
// Complete the share action
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
}
override func configurationItems() -> [Any]! {
// No configuration items needed
return []
}
// MARK: - Helper Methods
private func handleSharedText(_ text: String) {
// Save to shared user defaults to pass to main app
saveToSharedDefaults(content: text, type: "text")
openMainApp()
}
private func handleSharedURL(_ url: URL) {
// Convert URL to text and share
let text = url.absoluteString
saveToSharedDefaults(content: text, type: "url")
openMainApp()
}
private func handleSharedImage(_ image: UIImage) {
// For now, we'll just notify that image sharing isn't supported
// In the future, we could implement image sharing via the mesh
saveToSharedDefaults(content: "Image sharing coming soon!", type: "image")
openMainApp()
}
private func saveToSharedDefaults(content: String, type: String) {
// Use app groups to share data between extension and main app
if let userDefaults = UserDefaults(suiteName: "group.chat.bitchat") {
userDefaults.set(content, forKey: "sharedContent")
userDefaults.set(type, forKey: "sharedContentType")
userDefaults.set(Date(), forKey: "sharedContentDate")
userDefaults.synchronize()
}
}
private func openMainApp() {
// Note: Share extensions cannot directly open the containing app
// The user will need to tap on the notification or manually open the app
// to see the shared content
}
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.chat.bitchat</string>
</array>
</dict>
</plist>