I am working on showing the React-native-View in the swift project. I download the bunlde file through asynchronous operation, save the bundle file in the app directory, and use it to create the RCTROOTVIEW. My current problem is that when I try to show the view, I see a blank screen because the process of downloading and saving the file is asynchronous. How can I solve this problem?
my source
import UIKit
import React
import SwiftUI
import Foundation
struct ReactNativeView: UIViewRepresentable {
let mockData: NSDictionary = ["scores": [["name": "Alex", "value": "42"], ["name": "Joel", "value": "10"]]]
let jsCodeLocation = URL(string: "https://CDN.com/main.jsbundle")!
func getDocumentsDirectory() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
func downloadBundle(url: URL, completion: @escaping (URL?, Error?) -> Void) {
let documentsUrl = getDocumentsDirectory()
let destinationUrl = documentsUrl.appendingPathComponent(url.lastPathComponent)
URLSession.shared.downloadTask(with: url) { (tempLocalUrl, response, error) in
guard let tempLocalUrl = tempLocalUrl, error == nil else {
print("Error downloading bundle: (String(describing: error))")
completion(nil, error)
return
}
do {
try FileManager.default.moveItem(at: tempLocalUrl, to: destinationUrl)
print("File moved to destination [(destinationUrl.path)]")
completion(destinationUrl, nil)
} catch {
print("Error moving file to destination: (error)")
completion(nil, error)
}
}.resume()
}
func makeUIView(context: Context) -> UIView {
var rootView : RCTRootView?
downloadBundle(url: jsCodeLocation) { _url, error in
guard let localURL = _url, error == nil else {
print("error saving file")
return
}
rootView = RCTRootView(
bundleURL: localURL,
moduleName: "RNHighScores",
initialProperties: mockData as [NSObject: AnyObject],
launchOptions: nil
)
}
return rootView ?? UIView()
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
use code
@objc private func showReactNativeView() {
let reactNativeViewController = UIHostingController(rootView: ReactNativeView())
present(reactNativeViewController, animated: true, completion: nil)
}
...
containerButton.rx.tap
.asDriverOnErrorJustComplete()
.drive(onNext: { [weak self] in
self?.showReactNativeView()
})
.disposed(by: disposeBag)
Finally, if you already have a downloaded file, I want to erase or overwrite it.
2
Answers
your
func makeUIView(context: Context) -> UIView
returns nil due to the async operation. This is fundamentally bad as you should never assign a returned variable in an async operation in a sync context to return the result.There are many ways of solving this:
RCTRootView
so it can update itself without requiring SwiftUI to do anythingfunc makeUIView(context: Context) -> UIView
, then once the operation is done, createRCTRootView
and add it to the container view@State
property to track the progress (loading/finished) and update the view infunc updateUIView(_ uiView: UIView, context: Context)
using that State property.It appears that there is a problem with the asynchronous download of the bundle and the RCTRootView creation in your ReactNativeView. Because downloading is asynchronous, it’s possible that the makeUIView function will return before the download is finished, leaving you with a blank screen.