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 in an iPhone 13 Pro (18.2) simulator I click on Open Files App, it takes 2 seconds to open it, and it opens full screen (not like a modal). Buttons on the top are behind the status bar and buttons at the bottom are behind the Home indicator. But it's worse because the user can't interact with this view, it's frozen.
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 the file picker as a modal, 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?
Post
Replies
Boosts
Views
Activity
I'm making some maintenance on my iOS app with SwiftUI. One feature of this app is open the Files app (using fileimporter) to select a file, import it and make some analysis to it. This has been working ok so far.
But today I've suddenly found out that fileimporter it's not working properly and I'm pretty sure it was working ok some weeks ago and I haven't made any change in the app since then.
I'm thinking if it's a problem of any ios update.
My code to open fileimporter is this:
import SwiftUI
struct AnalyseFileView: View {
@ObservedObject var observedModel: AnalyseFileViewAdapter
@State var hasToOpenFilesApp = false
@State var isViewVisible: Bool = false
init(observedModel: AnalyseFileViewAdapter) {
self.observedModel = observedModel
}
var body: some View {
NavigationView {
ZStack {
VStack(alignment: .leading, spacing: 0) {
ScrollView {
VStack(alignment: .center, spacing: 0) {
...
CustomButton(title: "Open files app") {
hasToOpenFilesApp = true
}.fileImporter(isPresented: $hasToOpenFilesApp, allowedContentTypes: [.text, .zip, .gzip, .vCard]) { result in
switch result {
case .success(let url):
print(url)
manageSelectedFile(url: url)
case .failure(let error):
print(error)
}
}
.buttonStyle(CustomButtonStyle(style: .terciary, size: .large, isFullWidth: true))
.padding([.leading, .trailing], 25)
.padding(.bottom, 24)
...
}
}
}
}
.background(Color("primary4_primary3"))
.navigationBarHidden(true)
.onAppear {
isViewVisible = true
startView()
}
.onAppCameToForeground {
if isViewVisible {
startView()
}
}
.onDisappear {
isViewVisible = false
}
}
}
private func startView() {
observedModel.presenter?.viewWillAppear()
}
private func manageSelectedFile(url: URL) {
observedModel.selectedFile = url
guard url.startAccessingSecurityScopedResource() else { return }
observedModel.presenter?.manageFilePicked(url: url)
url.stopAccessingSecurityScopedResource()
}
}
With my iPhones 16.5 and 16.2 Files app is open this way:
As you can see there is only the Recents option and there is no way to get the Browse button to go to iCloud, Dropbox, My Device, etc... Search bar is working, it finds my files in those locations but it's more friendly to have the browse button.
What could have happened? I'm pretty sure this browse button was being shown a couple of weeks ago.
Just in case, I've just tested in an iPhone 14 and iPhone 15 and Files app shows the correct button:
I have a pretty simple swift code to open Files app and let the user browse to any folder and pick a file.
func goToFilesApp() {
var documentPicker: UIDocumentPickerViewController
if #available(iOS 14, *) {
let types: [UTType] = [UTType.text,
UTType.vCard,
UTType.zip,
UTType.gzip]
documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: types)
} else {
let types = [String(kUTTypeText),
String(kUTTypeVCard),
"com.pkware.zip-archive",
"org.gnu.gnu-zip-archive",
"org.gnu.gnu-zip-tar-archive"]
documentPicker = UIDocumentPickerViewController(documentTypes: types, in: .import)
}
documentPicker.delegate = self
documentPicker.modalPresentationStyle = .formSheet
if let view = self.view as? UIViewController {
view.present(documentPicker, animated: true, completion: nil)
}
}
and
extension AnalyzeFilePresenter : UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
if let firstUrl = urls.first {
manageFilePicked(url: firstUrl)
}
}
}
This is working well, but users use this feature very often and under some circumstances the Files app is not remembering the last folder and the user has to browse to their last location (which is a bit annoying).
With my iPhone X, ios 15.3.1 and iCloud unactive, this code remembers the last folder accessed.
With my iPhone 13 Pro, iOS 16.2 and iCloud active, this code doesn't remember the last folder accessed and instead show a default folder. With this iPhone 13, if the user uses Files app (outside of my app) Files app indeed remembers the last folder, so I guess it's a problem of my app.
What could I try? Because every SO answer I've read show this little piece of code I'm using.
Hi,I've just developed an app with auto-renewable susbscriptions. I've tested them deeply with sandbox test users without problems.Now the app has been aproved by Apple and I've tested with a friend promocodes for this auto-renewable subscription.My friend has just redeemed the code without problems but when he tries to access the content he recives an internal app error . This error is generated when the app refresh the receipt and tries to parse it's content, to be exact the error is generated here:guard let receipts_array = json["latest_receipt_info"] as? [Dictionary<String, Any>] else { // throw error return }I assumed that auto-renewal promocodes worked the same way than a purchase, so the flow and the receipt should be equal with a promocode and with a purchase. Am I wrong? Soes this error mean that the receipt has not the latest_receipt_info field? Should I search for the promocode info in any other field of the receipt?Thank you very much!