最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ios - swiftui fileimporter inside UIHostingController - Stack Overflow

programmeradmin4浏览0评论

I'm working on an old iOS app that started with objective-C + UIKit and has being migrated to Swift + SwiftUI. Currently its code is mostly Swift + SwiftUI but it has still some objective-C and some UIKit ViewControllers.

One of the SwiftUI views uses fileImporter to open Files App and select a file from the device. This has been working well until iOS 18 is launched. With iOS 18 the file picker is not launching correctly and is frozen in every simulator (the unique real device I've could test with iOS 18 seemed to work correctly).

I managed to clone my project and leave it with the minimal amount of files to reproduce this error. This is the code:

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate> {}

@property (strong, nonatomic) UIWindow *window;

@end

AppDelegate.m

#import "AppDelegate.h"
#import "MyApp-Swift.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    
    FirstViewBuilder *viewBuilder = [[FirstViewBuilder alloc] init];
    [viewBuilder show];
    
    return YES;
}

@end

FirstViewBuilder.swift

import SwiftUI

@objc class FirstViewBuilder: NSObject {
    
    private var view: UIHostingController<FirstView>

    @objc override init() {
        self.view = MyHostingController(rootView: FirstView())
    }

    @objc func show() {
        
        let app = UIApplication.shared.delegate as? AppDelegate
        let window = app?.window
        window?.backgroundColor = .white
        // Use navigationController or view directly depending on use
        window?.rootViewController = view
    }
}

FirstView.swift

import SwiftUI

struct FirstView: View {
    
    @State var hasToOpenFilesApp = false
    
    var body: some View {

        VStack(alignment: .leading, spacing: 0) {
            
            Button("Open Files app") {
                hasToOpenFilesApp = true
            }.fileImporter(isPresented: $hasToOpenFilesApp, allowedContentTypes: [.text]) { result in
                switch result {
                case .success(let url):
                    print(url.debugDescription)
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }
        }
    }
}

And finally, MyHostingController

import SwiftUI

class MyHostingController<Content>: UIHostingController<Content> where Content: View {
    
    override init(rootView: Content) {
        super.init(rootView: rootView)
    }
    
    @objc required dynamic init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.hidesBackButton = true
    }
}

Launching this and opening the file picker (it takes a couple of seconds) in an iPhone 13 Pro (18.2) simulator gives this:

And it doesn't respond to any touch, the app is frozen and I have to kill it.

I created a fresh SwiftUI project just with this unique view and the fileimport worked as expected so I thought the problem was due to embed the SwiftUI view inside the UIHostingController. So I made these modifications to the minimal project:

  • Remove the files AppDelegate, FirstViewBuilder and MyHostingController.
  • Create this SwiftUI App file
import SwiftUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            FirstView()
        }
    }
}

And again the same problem with iOS 18.

But if I launch this exact project in an iPhone 13 Pro (17.4) simulator and open the files apps (now it opens almost instantly) it works OK and shows this, as expected, and I can interact with it and select files:

Last thing I've tried is removing LaunchScreen.xib from my project and Launch screen interface file base name key from my info.plist but the problem keeps happening.

I guess it must be due to my project configuration (too old) but I have no more ideas of where to look at.

The possibility of having a fresh SwiftUI project and "move" the old project to the new one could take me several weeks and I discard it by the moment.

Could I use another method to select files from SwiftUI views with iOS 18?

I'm working on an old iOS app that started with objective-C + UIKit and has being migrated to Swift + SwiftUI. Currently its code is mostly Swift + SwiftUI but it has still some objective-C and some UIKit ViewControllers.

One of the SwiftUI views uses fileImporter to open Files App and select a file from the device. This has been working well until iOS 18 is launched. With iOS 18 the file picker is not launching correctly and is frozen in every simulator (the unique real device I've could test with iOS 18 seemed to work correctly).

I managed to clone my project and leave it with the minimal amount of files to reproduce this error. This is the code:

AppDelegate.h

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate> {}

@property (strong, nonatomic) UIWindow *window;

@end

AppDelegate.m

#import "AppDelegate.h"
#import "MyApp-Swift.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    
    FirstViewBuilder *viewBuilder = [[FirstViewBuilder alloc] init];
    [viewBuilder show];
    
    return YES;
}

@end

FirstViewBuilder.swift

import SwiftUI

@objc class FirstViewBuilder: NSObject {
    
    private var view: UIHostingController<FirstView>

    @objc override init() {
        self.view = MyHostingController(rootView: FirstView())
    }

    @objc func show() {
        
        let app = UIApplication.shared.delegate as? AppDelegate
        let window = app?.window
        window?.backgroundColor = .white
        // Use navigationController or view directly depending on use
        window?.rootViewController = view
    }
}

FirstView.swift

import SwiftUI

struct FirstView: View {
    
    @State var hasToOpenFilesApp = false
    
    var body: some View {

        VStack(alignment: .leading, spacing: 0) {
            
            Button("Open Files app") {
                hasToOpenFilesApp = true
            }.fileImporter(isPresented: $hasToOpenFilesApp, allowedContentTypes: [.text]) { result in
                switch result {
                case .success(let url):
                    print(url.debugDescription)
                case .failure(let error):
                    print(error.localizedDescription)
                }
            }
        }
    }
}

And finally, MyHostingController

import SwiftUI

class MyHostingController<Content>: UIHostingController<Content> where Content: View {
    
    override init(rootView: Content) {
        super.init(rootView: rootView)
    }
    
    @objc required dynamic init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.hidesBackButton = true
    }
}

Launching this and opening the file picker (it takes a couple of seconds) in an iPhone 13 Pro (18.2) simulator gives this:

And it doesn't respond to any touch, the app is frozen and I have to kill it.

I created a fresh SwiftUI project just with this unique view and the fileimport worked as expected so I thought the problem was due to embed the SwiftUI view inside the UIHostingController. So I made these modifications to the minimal project:

  • Remove the files AppDelegate, FirstViewBuilder and MyHostingController.
  • Create this SwiftUI App file
import SwiftUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            FirstView()
        }
    }
}

And again the same problem with iOS 18.

But if I launch this exact project in an iPhone 13 Pro (17.4) simulator and open the files apps (now it opens almost instantly) it works OK and shows this, as expected, and I can interact with it and select files:

Last thing I've tried is removing LaunchScreen.xib from my project and Launch screen interface file base name key from my info.plist but the problem keeps happening.

I guess it must be due to my project configuration (too old) but I have no more ideas of where to look at.

The possibility of having a fresh SwiftUI project and "move" the old project to the new one could take me several weeks and I discard it by the moment.

Could I use another method to select files from SwiftUI views with iOS 18?

Share Improve this question edited Feb 15 at 21:11 Wonton asked Feb 15 at 16:39 WontonWonton 1,1431 gold badge19 silver badges38 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

For iOS 18 you can use DocumentGroup and DocumentGroupLaunchScene to access files. Consider checking it out.

source: https://developer.apple/documentation/swiftui/building-a-document-based-app-with-swiftui

发布评论

评论列表(0)

  1. 暂无评论