iosflutterdartfile-sharing

Flutter iOS File Sharing - Cannot Open Shared File


I am building a flutter app for iOS. I have created a file type that my app and another app can share back and forth. I am using the flutter receive_sharing_intent library to achieve this. To test it, I made all the necessary changes to my info.plist file to handle my custom file type, I placed an example file of that file type in the downloads folder of my device (testing on ipad), and I click on it from there to open it up in my app. The OS knows that my app can handle it. So, my app opens, it is receives the path of the shared file, but my app can't open the file. Here is the code in my main.dart that is handling reception of the file:

StreamSubscription _intentDataStreamSubscription =
    ReceiveSharingIntent.getTextStream().listen((String value) {
      setState(() {
        try{

        //This was added to test and make sure the directory actually exists. 
        //The value for the directory path was added after I ran it once to capture where the shared file was coming from.
        Directory dir = Directory("/private/var/mobile/Containers/Shared/AppGroup/7DD4B316-3D73-4339-9B11-7516DE52F6FC/File Provider Storage/Downloads/");
        bool dirExists = dir.existsSync();
        
        var files = dir.listSync();
        File drawingFile = File.fromUri(Uri.parse(value));
        bool fileExists = drawingFile.existsSync();

        var contents = drawingFile.readAsStringSync();
        DrawingCanvas canvas = DrawingCanvas.fromFileContents(contents);
        DrawingCanvasBloc canvasBloc = DrawingCanvasBloc(canvas);

        Navigator.of(context).push(MaterialPageRoute(builder: (context) => CanvasScreen(canvasBloc)));
        }
        catch(e){
          log(e.toString());
        }
      });
    }, onError: (err) {
      print("getLinkStream error: $err");
    });

Scenario: I run the application. I go into my downloads folder in files on the ipad. I select my example.fbg file located there (my custom file type). My app opens up.

In the above code it blows up when I try to list the contents of the directory. I put this in here (after previously catching the directory path and hard coding it) to test to make sure I was even getting the right location. dirExists is true but I can't list the files in it. The error I get is:

FileSystemException: Directory listing failed, path = '/private/var/mobile/Containers/Shared/AppGroup/7DD4B316-3D73-4339-9B11-7516DE52F6FC/File Provider Storage/Downloads/' (OS Error: Operation not permitted, errno = 1)

If I take that line out and continue down to the opening of the file (readAsStringSync) I get:

FileSystemException: Cannot open file, path = '/private/var/mobile/Containers/Shared/AppGroup/7DD4B316-3D73-4339-9B11-7516DE52F6FC/File Provider Storage/Downloads/example.fbg' (OS Error: Operation not permitted, errno = 1)

I'm not sure why it won't let me access this file. Is there a permissions thing I'm missing? Let me know if I need to include more information in the question and I will update.


Solution

  • Finally found a work around and got it working using this answer. What I ended up doing was opening the file in the app delegate, saving the file to the apps documents directory, then passing that url to the Flutter application. I opened the iOS module in xcode and changed the AppDelegate.swift to the following:

    import UIKit
    import Flutter
    
    @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 {
        var drawingFilename = ""
        do {
            let isAcccessing = url.startAccessingSecurityScopedResource()
            var error: Error? = nil
            let path = url.path
            
            let string = try String(contentsOf: url)
            drawingFilename = (url.path as NSString).lastPathComponent
            print(drawingFilename)
           
            let filename = getDocumentsDirectory().appendingPathComponent(drawingFilename)
    
            do {
                try string.write(to: filename, atomically: true, encoding: String.Encoding.utf8)
            } catch {
                // failed to write file – bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
            }
            if isAcccessing {
                url.stopAccessingSecurityScopedResource()
            }
          if #available(iOS 9.0, *) {
              return super.application(app, open: filename, options: options)
          } else {
             return false
          }
        } catch {
            print("Unable to load data: \(error)")
            return false
        }
    
        
          
    }
    
    func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }
    }
    

    Hope this helps someone in the future.