Environment Object Method error in Scene Delegate - mvvm

SO Community
I have been self-teaching myself SwiftUI and I've just come across a problem while creating an application in the Scene Delegate. When I add an environment object method to the scene delegate to assign an object to the environment of a view hierarchy, Xcode sends these errors on my let contentView = ContentView().environmentObject(delegate.myData) line of code, "Expected member name following '.' ", and "Missing argument for parameter 'appData' in call ".
I will also attach a picture of my ContentView.swift File to show where I placed my #EnviromentObject reference within the body Content View. If you guys have any questions can you please try to help me out, this has been a bum time. Thanks.
Link to Scene Delegate.Swift file : enter image description here
Link to ContentView.Swift file : enter image description here
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options
connectionOptions: UIScene.ConnectionOptions) {
let app = UIApplication.shared
let delegate = app.delegate as! AppDelegate
let contentView = ContentView()
.environmentObject(delegate.myData)
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}

EnvironmentObjects are not passed via parameter like you're trying to do in your ContentView initializer. Instead, they are passed "magically" behind-the-scenes and exposed via the #EnvironomentObject property wrapper.
My suggestion is to get rid of your init method and use onAppear { } to set your contentData fields:
var body: some View {
VStack {
//...
}.onAppear {
// do initial setup
}
}
You should also make sure that you're using the same capitalization of appData -- you're using appdata sometimes, which is leading to many of your compilation errors.

Related

Error when requesting review using SKStoreReviewController

When I use SKStoreReviewController.requestReview() to request a review from the Scene Delegate in my SwiftUI application, I get the following message:
Error in UIKit client: -[UIWindow setScreen:] should not be called if the client adopts UIScene lifecycle. Call -[UIWindow setWindowScene:] instead..
I am using the following code in the scene delegate to request the review:
if let windowScene = scene as? UIWindowScene {
if UserDefaults.standard.integer(forKey: "launchCount") == 10 {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: homeView)
self.window = window
window.makeKeyAndVisible()
if #available(iOS 14, *) {
SKStoreReviewController.requestReview(in: windowScene)
}
else {
SKStoreReviewController.requestReview()
}
}
}
This code is inside the scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) method of the Scene Delegate. The method for iOS 14 is working where the review is being requested from the UIWindowScene, but the method for earlier versions is not working and returns the message specified previously. Is there any way to fix this? Thanks in advance for all the help.
You can use my small wrapper around SKStoreReviewController which solves this problem.
import SwiftUI
import AppReview
struct ContentView: View {
var body: some View {
VStack {
Text("SwiftUI")
}.onAppear {
AppReview.requestIf(launches: 3)
}
}
}
https://github.com/mezhevikin/AppReview

How to send parameters in Firebase Dynamic Links

I am developing an iOS app with Swift.
I want to send a parameter with Firebase Dynamic Links.
I created a Dynamic Link called https://test.page.link/test1.
I want to attach parameters as below.
https://test.page.link/test1?id=000000
How can I get this parameter when I click on the link and launch the app?
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let rootView = ListView()
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: rootView)
self.window = window
window.makeKeyAndVisible()
}
guard let userActivity = connectionOptions.userActivities.first(where: { $0.webpageURL != nil }) else { return }
print("url: \(userActivity.webpageURL!)") // <- No parameters could be obtained from here.
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
print("url: \(userActivity.webpageURL!)") // <- No parameters could be obtained from here.
}
...
}
So you can not pass parameters by short-version dynamic link.
https://PROJECTNAME.page.link/LINKNAME is short-version dynamic link.
You can pass get-query-parameters by using long-version dynamic link.
Here is an example for passing {param1: 'one', param2: 'two'} to app.
https://PROJECTNAME.page.link/?link=https%3A%2F%2FPROJECTNAME.page.link/LINKNAME%3Fparam1%3Done&param2%3Dtwo&apn=XXXXXX&isi=XXXXX&ibi=XXXXXX
You can find your long-version dynamic link by clicking the ⋮ menu next to the link on the console, going in Link Details and using your 'Long Dynamic Link'. Add parameters to the inner link and they will come through in the app.

Xcode 11.4 issue: Context in environment is not connected to a persistent store coordinator

Code was working in Xcode 11.3 but after updating to 11.4, I am now getting two errors:
Context in environment is not connected to a persistent store coordinator.
Thread 1: Fatal error: No ObservableObject of type UpdateTagView found. A View.environmentObject(_:) for UpdateTagView may be missing as an ancestor of this view.
This is how I add the Objects to the Environment:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
let em = EventManager()
let tagger = UpdateTagView()
let contentView = ContentView()
.environment(\.managedObjectContext, context)
.environmentObject(em)
.environmentObject(tagger)
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
I call them inside the View with #EnvironmentObject:
struct ViewName: View {
#EnvironmentObject var em: EventManager
#EnvironmentObject var tagger: UpdateTagView
Here is an example of what is in the ObservableObject:
class UpdateTagView: ObservableObject {
#Published var counter: Int = 0
#Published var language: String = "English"
}
Cheers~
I observed exactly the same issue when compiling our app in Release configuration. In Xcode 11.3 it worked but when we moved to Xcode 11.4 it stopped with exactly the same crash (#EnvironmentObject was not propagated in the hierarchy)
It looks like a bug in Swift compiler in metadata stripping algorithm. Temporarily you can either switch configuration to debug or manually switch Reflection Metadata Level (in your target Build Settings) from None to Without Names.

How do I rename the initial programmatic ViewController class for my iOS app without involving storyboards

I have been looking for a while and cannot find an answer to this question that does not involve storyboards.
It's quite simple. When you build a new iOS app in Xcode you are given the ViewController.swift file that contains the initial ViewController() class for the app. This view controller is the main view for the app.
If I rename the class to say ViewControllerTest(), when I build and run the app now only loads a blank screen since it cannot find the ViewController class as I renamed it.
How do I set the project in Xcode to use the new ViewControllerTest() class as the initial view controller?
Thanks
If you want to disregard using the storyboard entirely, which I prefer because then all code is in one place, you need to add the following in your AppDelegate or SceneDelegate depending on what version Xcode you are creating your project with.
Xcode 10 and earlier
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
// Or whatever you want to name your view controller class
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window // retain instance
return true
}
}
for Xcode 10 and earlier you should also change the value of the UIMainStoryboardFile property to an empty string in your Info.plist.
Xcode 11 and later
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
// Or whatever you want to name your view controller class
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window // retain instance
}
}
for Xcode 11 projects and newer, you should populate the Application Scene Manifest property as shown here:

SwiftUI: Access #EnvironmentObject from AppDelegate

I want to use the applicationWillTerminate function to save some user defaults before the app closes. The data I want to save is stored in an EnvironmentObject.
How can I access it from the AppDelegate class?
An #EnvironmentObject doesn't need to be instantiated directly in your SwiftUI objects; rather, it can be allocated somewhere else (for example, your UISceneDelegate) and then passed through using the .environment(…) function.
You could also allocate it on your AppDelegate, and pass that object though to your views in UISceneDelegate.scene(_:willConectTo:options:) method.
Paul Hudson has a good description of all of this at https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views
In case someone needs code example for this answer.
1.Create class that conforms to ObservableObject
class Test: ObservableObject{ }
2.in AppDelegate.Swift declare var myVar = Test()
class AppDelegate: UIResponder, UIApplicationDelegate {
var myVar = Test()
//****
}
3.in SceneDelegate.swift in "if let windowScene = scene as? UIWindowScene {" change the code like this :
if let windowScene = scene as? UIWindowScene {
let myVar = (UIApplication.shared.delegate as! AppDelegate).myVar
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(myVar))
self.window = window
window.makeKeyAndVisible()
}