Closed akilanfidelis closed 1 year ago
Same here
same
I tried with real device, it works there, did you check there ?
Both the app and the extension had to have the same ios Deployment target in build settings. Remember to uninstall the old version after the modification.
I've the same problem. I've the same deployment target in build settings 14.0, but myApp don't open or not resume if it's in background. I try in simulator and real device, but the behavior don't change. My suspicion is that I have not properly understood how to modify AppDelegate. This is how I did it. Is this correct?
import UIKit
import Flutter
import flutter_sharing_intent
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let sharingIntent = SwiftFlutterSharingIntentPlugin.instance
/// if the url is made from SwiftFlutterSharingIntentPlugin then handle it with plugin [SwiftFlutterSharingIntentPlugin]
if sharingIntent.hasSameSchemePrefix(url: url) {
return sharingIntent.application(app, open: url, options: options)
}
// Proceed url handling for other Flutter libraries like uni_links
return super.application(app, open: url, options:options)
}
}
This is the video showing my problem. The app does not open
I tried downloading the example app you made to see if I had done something wrong in configuring mine, but the result is the same. So it's not my problem.
I set the minimum Deployed to 14.0 because it is required by
let imageContentType = UTType.image.identifier;
and others
I also noticed that the AppDelegate gives this error
I have the same issue, Deployed target minimum also set to 14 to both share extension and Runner .. and I have the exact same behavior than @csacchetti video.
I found the fix ! Have a look at https://github.com/bhagat-techind/flutter_sharing_intent/issues/9 this issue. I changed the "sharing file" to the same name in both places (like they said in the issue), and also add my groupid explicitely. The app open now.
Now the app open, but it looks like it does not goes through the callback : FlutterSharingIntent.instance.getMediaStream().listen ...
@GaelleJoubert I have tried to change these two things in ShareViewController
let url = URL(string: "SharingMedia-\(hostAppBundleIdentifier)://dataUrl=\(sharedKey)#\(type)")
and in info.plist/Runner I put the same name SharingMedia and add AppGroupID with the explicit name of my group
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>SharingMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
</dict>
</array>
<key>AppGroupId</key>
<string>group.com.xxxxxxxx.xxxxx</string>
but the app does not open
Same here
in SharingViewController.swift change
line "SharingMedia-\(hostAppBundleIdentifier)://dataUrl=\(sharedKey)#\(type)"
to
"ShareMedia-\(hostAppBundleIdentifier)://dataUrl=\(sharedKey)#\(type)"
this worked, opened the app for me.
@didiabel Hi, could you send your whole code of how you implemented this package? I'm trying to do everything you mentioned here guys but still my app does not open :(
Now the app open, but it looks like it does not goes through the callback : FlutterSharingIntent.instance.getMediaStream().listen ...
Facing the same issue on iOS, works on Android perfectly for some reason
@aviataur @Dominik-domin
hey guys, check your xcode, make sure that you have added & selected the same group it should be something like: group.${bundleID}
,
and that minimum deployment for the Share Extension and Runner are the same, in my case 11.0
.
@aviataur @Dominik-domin hey guys, check your xcode, make sure that you have added & selected the same group it should be something like:
group.${bundleID}
, and that minimum deployment for the Share Extension and Runner are the same, in my case11.0
.
I did, both group and min deployment are same for Share extension as well as Runner.
try changing your ShareViewController.swift
(dont forget to change bundle id)
to the following:
import UIKit
import Social
import MobileCoreServices
import Photos
class ShareViewController: SLComposeServiceViewController {
// TODO: IMPORTANT: This should be your host app bundle identifier
let hostAppBundleIdentifier = "com.your.app"
let sharedKey = "ShareKey"
var sharedMedia: [SharedMediaFile] = []
var sharedText: [String] = []
let imageContentType = kUTTypeImage as String
let videoContentType = kUTTypeMovie as String
let textContentType = kUTTypeText as String
let urlContentType = kUTTypeURL as String
let fileURLType = kUTTypeFileURL as String;
override func isContentValid() -> Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad();
}
override func didSelectPost() {
if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
if let contents = content.attachments {
for (index, attachment) in (contents).enumerated() {
if attachment.hasItemConformingToTypeIdentifier(imageContentType) {
handleImages(content: content, attachment: attachment, index: index)
} else if attachment.hasItemConformingToTypeIdentifier(textContentType) {
handleText(content: content, attachment: attachment, index: index)
} else if attachment.hasItemConformingToTypeIdentifier(fileURLType) {
handleFiles(content: content, attachment: attachment, index: index)
} else if attachment.hasItemConformingToTypeIdentifier(urlContentType) {
handleUrl(content: content, attachment: attachment, index: index)
} else if attachment.hasItemConformingToTypeIdentifier(videoContentType) {
handleVideos(content: content, attachment: attachment, index: index)
}
}
}
}
}
override func configurationItems() -> [Any]! {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
return []
}
private func handleText (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
attachment.loadItem(forTypeIdentifier: textContentType, options: nil) { [weak self] data, error in
if error == nil, let item = data as? String, let this = self {
this.sharedText.append(item)
// If this is the last item, save imagesData in userDefaults and redirect to host app
if index == (content.attachments?.count)! - 1 {
let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
userDefaults?.set(this.sharedText, forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .text)
}
} else {
self?.dismissWithError()
}
}
}
private func handleUrl (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
attachment.loadItem(forTypeIdentifier: urlContentType, 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 {
let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
userDefaults?.set(this.sharedText, forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .text)
}
} else {
self?.dismissWithError()
}
}
}
private func handleImages (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
attachment.loadItem(forTypeIdentifier: imageContentType, options: nil) { [weak self] data, error in
if error == nil, let url = data as? URL, let this = self {
// Always copy
let fileName = this.getFileName(from: url, type: .image)
let newPath = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(this.hostAppBundleIdentifier)")!
.appendingPathComponent(fileName)
let copied = this.copyFile(at: url, to: newPath)
if(copied) {
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .image))
}
// If this is the last item, save imagesData in userDefaults and redirect to host app
if index == (content.attachments?.count)! - 1 {
let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .media)
}
} else {
self?.dismissWithError()
}
}
}
private func handleVideos (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
attachment.loadItem(forTypeIdentifier: videoContentType, options: nil) { [weak self] data, error in
if error == nil, let url = data as? URL, let this = self {
// Always copy
let fileName = this.getFileName(from: url, type: .video)
let newPath = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(this.hostAppBundleIdentifier)")!
.appendingPathComponent(fileName)
let copied = this.copyFile(at: url, to: newPath)
if(copied) {
guard let sharedFile = this.getSharedMediaFile(forVideo: newPath) else {
return
}
this.sharedMedia.append(sharedFile)
}
// If this is the last item, save imagesData in userDefaults and redirect to host app
if index == (content.attachments?.count)! - 1 {
let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .media)
}
} else {
self?.dismissWithError()
}
}
}
private func handleFiles (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
attachment.loadItem(forTypeIdentifier: fileURLType, options: nil) { [weak self] data, error in
if error == nil, let url = data as? URL, let this = self {
// Always copy
let fileName = this.getFileName(from :url, type: .file)
let newPath = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(this.hostAppBundleIdentifier)")!
.appendingPathComponent(fileName)
let copied = this.copyFile(at: url, to: newPath)
if (copied) {
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .file))
}
if index == (content.attachments?.count)! - 1 {
let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .file)
}
} 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)
}
private func redirectToHostApp(type: RedirectType) {
let url = URL(string: "ShareMedia://dataUrl=\(sharedKey)#\(type)")
var responder = self as UIResponder?
let selectorOpenURL = sel_registerName("openURL:")
while (responder != nil) {
if (responder?.responds(to: selectorOpenURL))! {
let _ = responder?.perform(selectorOpenURL, with: url)
}
responder = responder!.next
}
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
enum RedirectType {
case media
case text
case file
}
func getExtension(from url: URL, type: SharedMediaType) -> String {
let parts = url.lastPathComponent.components(separatedBy: ".")
var ex: String? = nil
if (parts.count > 1) {
ex = parts.last
}
if (ex == nil) {
switch type {
case .image:
ex = "PNG"
case .video:
ex = "MP4"
case .file:
ex = "TXT"
}
}
return ex ?? "Unknown"
}
func getFileName(from url: URL, type: SharedMediaType) -> String {
var name = url.lastPathComponent
if (name.isEmpty) {
name = UUID().uuidString + "." + getExtension(from: url, type: type)
}
return name
}
func copyFile(at srcURL: URL, to dstURL: URL) -> Bool {
do {
if FileManager.default.fileExists(atPath: dstURL.path) {
try FileManager.default.removeItem(at: dstURL)
}
try FileManager.default.copyItem(at: srcURL, to: dstURL)
} catch (let error) {
print("Cannot copy item at \(srcURL) to \(dstURL): \(error)")
return false
}
return true
}
private func getSharedMediaFile(forVideo: URL) -> SharedMediaFile? {
let asset = AVAsset(url: forVideo)
let duration = (CMTimeGetSeconds(asset.duration) * 1000).rounded()
let thumbnailPath = getThumbnailPath(for: forVideo)
if FileManager.default.fileExists(atPath: thumbnailPath.path) {
return SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video)
}
var saved = false
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
// let scale = UIScreen.main.scale
assetImgGenerate.maximumSize = CGSize(width: 360, height: 360)
do {
let img = try assetImgGenerate.copyCGImage(at: CMTimeMakeWithSeconds(600, preferredTimescale: Int32(1.0)), actualTime: nil)
try UIImage.pngData(UIImage(cgImage: img))()?.write(to: thumbnailPath)
saved = true
} catch {
saved = false
}
return saved ? SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video) : nil
}
private func getThumbnailPath(for url: URL) -> URL {
let fileName = Data(url.lastPathComponent.utf8).base64EncodedString().replacingOccurrences(of: "==", with: "")
let path = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
.appendingPathComponent("\(fileName).jpg")
return path
}
class SharedMediaFile: Codable {
var path: String; // can be image, video or url path. It can also be text content
var thumbnail: String?; // video thumbnail
var duration: Double?; // video duration in milliseconds
var type: SharedMediaType;
init(path: String, thumbnail: String?, duration: Double?, type: SharedMediaType) {
self.path = path
self.thumbnail = thumbnail
self.duration = duration
self.type = type
}
// Debug method to print out SharedMediaFile details in the console
func toString() {
print("[SharedMediaFile] \n\tpath: \(self.path)\n\tthumbnail: \(self.thumbnail)\n\tduration: \(self.duration)\n\ttype: \(self.type)")
}
}
enum SharedMediaType: Int, Codable {
case image
case video
case file
}
func toData(data: [SharedMediaFile]) -> Data {
let encodedData = try? JSONEncoder().encode(data)
return encodedData!
}
}
extension Array {
subscript (safe index: UInt) -> Element? {
return Int(index) < count ? self[Int(index)] : nil
}
}
@didiabel thanks for the code, unfortunately it's not working for me :C I'm trying to debug this code and I've put a few prints, but when I open for example Youtube and try to share something, I got this error in Xcode: Share[4937:807452] [SRHDataCollectionController] Failed to find application record for ${BundleID}.ShareExtension becase (null)
I've checked every setting in XCode and I have the same like you guys, I don't know what's happening here :(
@Dominik-domin if you want i can help you out someday when im free dm me
I too like @Dominik-domin have tried all the suggestions you gave but the app still does not open just like the viedeo I posted above. @didiabel let me understand something. How do you have the target at 11.0 when xCode is giving you an error if you have a target less than 14.0. I use the latest versions of xCode and Flutter. Any person who can help me would be grateful. This problem has been stopping me for months. I have already lost a lot of time. Thanks
my doctor -v [✓] Flutter (Channel stable, 3.13.1, on macOS 13.5 22G74 darwin-arm64, locale it-IT) • Flutter version 3.13.1 on channel stable at /Users/carlosacchetti/development/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision e1e47221e8 (10 days ago), 2023-08-22 21:43:18 -0700 • Engine revision b20183e040 • Dart version 3.1.0 • DevTools version 2.25.0
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.1) • Android SDK at /Users/carlosacchetti/Library/Android/sdk • Platform android-33, build-tools 33.0.1 • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301) • All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 14.3.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 14E300c • CocoaPods version 1.12.1
[✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2022.1) • Android Studio at /Applications/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301)
[✓] VS Code (version 1.81.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.70.0
[✓] Connected device (3 available) • iPhone di CS (mobile) • 00008101-0015458A3488001E • ios • iOS 14.7.1 18G82 • macOS (desktop) • macos • darwin-arm64 • macOS 13.5 22G74 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 116.0.5845.140
[✓] Network resources • All expected network resources are available.
• No issues found!
I have fixed this issue in version 1.1.0
Thank you for the update. Now the app opens however the moment you try to share a file it creates this error and the app is killed. I don't know whether I should continue here or open a new issue.
This is the error in SwiftFlutterSharingIntentPlugin.swift at line 253:
[User Defaults] Couldn't read values in CFPrefsPlistSource<0x281d0f100> (Domain: group.com.myid.sharingIntentOk, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
2
flutter_sharing_intent/SwiftFlutterSharingIntentPlugin.swift:253: Fatal error: The data couldn’t be read because it is missing.
* thread #1, queue = 'com.apple.main-thread', stop reason = Fatal error: The data couldn’t be read because it is missing.
frame #0: 0x0000000194c16eac libswiftCore.dylib`_swift_runtime_on_report
libswiftCore.dylib`:
-> 0x194c16eac <+0>: ret
libswiftCore.dylib`:
0x194c16eb0 <+0>: b 0x194c16eac ; _swift_runtime_on_report
libswiftCore.dylib`:
0x194c16eb4 <+0>: adrp x8, 337372
0x194c16eb8 <+4>: ldrb w0, [x8, #0x299]
Target 0: (Runner) stopped.
Lost connection to device.
Exited
Stuck on the same error, the app only opens if I share a text, but it gives me the error above: [User Defaults] Couldn't read values in CFPrefsPlistSource<0x281d0f100> [...] I cannot understand this and also why it only opens with a text and not with photo, pdf etc.
Thank you for the update. Now the app opens however the moment you try to share a file it creates this error and the app is killed. I don't know whether I should continue here or open a new issue.
This is the error in SwiftFlutterSharingIntentPlugin.swift at line 253:
[User Defaults] Couldn't read values in CFPrefsPlistSource<0x281d0f100> (Domain: group.com.myid.sharingIntentOk, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd 2 flutter_sharing_intent/SwiftFlutterSharingIntentPlugin.swift:253: Fatal error: The data couldn’t be read because it is missing. * thread #1, queue = 'com.apple.main-thread', stop reason = Fatal error: The data couldn’t be read because it is missing. frame #0: 0x0000000194c16eac libswiftCore.dylib`_swift_runtime_on_report libswiftCore.dylib`: -> 0x194c16eac <+0>: ret libswiftCore.dylib`: 0x194c16eb0 <+0>: b 0x194c16eac ; _swift_runtime_on_report libswiftCore.dylib`: 0x194c16eb4 <+0>: adrp x8, 337372 0x194c16eb8 <+4>: ldrb w0, [x8, #0x299] Target 0: (Runner) stopped. Lost connection to device. Exited
any update?
Now the app open, but it looks like it does not goes through the callback : FlutterSharingIntent.instance.getMediaStream().listen ...
I am having the same issue. Where you able to resolve this? Any tips or suggestion would be great! Thank you.
Now the app open, but it looks like it does not goes through the callback : FlutterSharingIntent.instance.getMediaStream().listen ...
I am having the same issue. Where you able to resolve this? Any tips or suggestion would be great! Thank you.
Unfortunately I couldn't fix it in any way so I had to find another package :/ I've used https://pub.dev/packages/share_handler/example and it worked for me
I followed through steps in the readme.
Tested on
Version: flutter_sharing_intent: ^1.0.6
https://user-images.githubusercontent.com/83576211/223353147-ae59b543-31bc-4c45-9499-ab7eb4c3529d.mp4