What is the counterpart of ViewController class in a project with interface of type SwiftUI?
I am following this documentation to initialize adLoader from Google AdMob in a SwiftUI project. All examples in the document are within a ViewController : UIViewController class. However, I saw that ViewController is only available when I create a project with storyboard as the interface. But not SwiftUI. Examples in this documentation are initializing the adLoader in the following way by assignment using the keyword self, to parameters rootViewController and adLoader.delegate. My project was created with interface as SwiftUI and does not have a ViewController class. I have a main app class that implements App protocol with #main annotation. I then have a view that is being loaded from the main app class. Please help me understand what I can use in place of the keyword self to initialize the adLoader object.
Code from the google document I am trying to work with:
var adLoader: GADAdLoader!
var nativeAdView: GADNativeAdView!
adLoader = GADAdLoader(adUnitID: YOUR_AD_UNIT_ID, rootViewController: self,
adTypes: [.native],
options: [multipleAdsOptions])
adLoader.delegate = self
Documentation for GADAdLoader
Sample code in project
MyApp.swift
#main
struct MyApp: App {
init() {}
var body: some Scene {
WindowGroup{
HomeView()
}
}
HomeView.swift
struct HomeView: View {
var body: some View {
customMainNavBar
.onAppear(){
------
------
}
}
}
Errors I get when I put this code in the View or the main App.
Cannot convert value of type 'HomeView' to expected argument type 'UIViewController?'
Cannot assign value of type 'HomeView' to type 'GADAdLoaderDelegate?'
Cannot convert value of type 'MyApp' to expected argument type 'UIViewController?'
Cannot assign value of type 'MyApp' to type 'GADAdLoaderDelegate?'
Related
My problem is that I don't understand how to resolve this warning: ⚠️ Warning: Instance will be immediately deallocated because property 'delegate' is 'weak'
Swift docs state:
You must assign your delegate object to the UNUserNotificationCenter object before your app finishes launching.
In a SwiftUI app that doesn't use AppDelegate, that means I should assign it in App.init().
import SwiftUI
import UserNotifications
class UNCDelegate: NSObject, UNUserNotificationCenterDelegate {
// this is where I think I can respond to the user's tapping on a notification
}
#main
struct MyApp: App {
#StateObject private var dataController: DataController()
init() {
let UNC = UNUserNotificationCenter.current()
UNC.delegate = UNCDelegate() // ⚠️ Warning: Instance will be immediately deallocated because property 'delegate' is 'weak'
// Here I would want to share the UNC across the app by putting it into the SwiftUI environment somehow, so I can schedule notifications whenever I want and still use the handlers in the delegate
}
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, dataController.container.viewContext)
}
}
}
I wouldn't be surprised if my approach is all wrong. There aren't many examples online that match my use case. My ultimate goal is to open a specific route when user taps on a notification. But with the intention of keeping the question specific, I want to understand how to assign the delegate.
The warning is telling you that the delegate property is weak, so after that line, nothing will be holding a strong reference to the newly created UNCDelegate object, and it will be deallocated.
So just put it in MyApp instead, so that it doesn't get deallocated.
#main
struct MyApp: App {
#StateObject var uncDelegate = UNCDelegate()
init() {
let UNC = UNUserNotificationCenter.current()
UNC.delegate = uncDelegate
}
...
}
The View protocol requires a body property:
public protocol View {
associatedtype Body : View
#ViewBuilder var body: Self.Body { get }
}
Why have some built-in Views in SwiftUI no body?
#frozen public struct EmptyView : View {
#inlinable public init()
public typealias Body = Never
}
#frozen public struct VStack<Content> : View where Content : View {
#inlinable public init(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil, #ViewBuilder content: () -> Content)
public typealias Body = Never
}
have no body at all..
let emptyView = EmptyView().body
// Value of type 'EmptyView' has no member 'body'
let vStackView = VStack { Text("some text")}.body
// Value of type 'VStack<Text>' has no member 'body'
How are these Views implemented?
Each primitive view does have a body, but it isn't directly accessible at compile-time. The official Swift forums have a thread about this topic. I'll reproduce my analysis here.
Let's consider Text.
A Text value has a body property, as all Views do. Text's body calls fatalError:
import SwiftUI
func body<V: View>(of view: V) -> V.Body { view.body }
body(of: Text("hello"))
// runtime output:
SwiftUI/View.swift:94: Fatal error: body() should not be called on Text.
However, if we attempt to access the body property directly, the compiler rejects the program:
import SwiftUI
Text("hello").body
Output:
The compiler rejects the program because SwiftUI's swiftinterface file doesn't declare a body property for Text.
Why doesn't Text's declaration include body? I don't know for sure because I don't have access to the SwiftUI source code, but I have discovered that we can use #_spi to omit a declaration from a .swiftinterface file.
I put the following into MyView.swift:
import SwiftUI
public struct MyView: View {
#_spi(Private)
public var body: Never { fatalError() }
}
Then I compile it as follows, based on the “Directly invoking the compiler” instructions found here:
swiftc MyView.swift -module-name MyLib -emit-module -emit-library -emit-module-interface -enable-library-evolution
The compiler writes MyLib.swiftinterface as follows, omitting the body declaration:
// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 5.7 (swiftlang-5.7.0.123.7 clang-1400.0.29.50)
// swift-module-flags: -target arm64-apple-macosx12.0 -enable-objc-interop -enable-library-evolution -module-name MyLib
import Swift
import SwiftUI
import _Concurrency
import _StringProcessing
public struct MyView : SwiftUI.View {
public typealias Body = Swift.Never
}
And it writes MyLib.private.swiftinterface as follows, containing the body declaration:
// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 5.7 (swiftlang-5.7.0.123.7 clang-1400.0.29.50)
// swift-module-flags: -target arm64-apple-macosx12.0 -enable-objc-interop -enable-library-evolution -module-name MyLib
import Swift
import SwiftUI
import _Concurrency
import _StringProcessing
public struct MyView : SwiftUI.View {
#_spi(Private) #_Concurrency.MainActor(unsafe) public var body: Swift.Never {
get
}
public typealias Body = Swift.Never
}
So my best guess is that SwiftUI applies the #_spi attribute to the body property of each primitive View.
I'm not an expert but this seems very logical. Imagine that no views are offered by SwiftUI and you want to create the very first view. This view has the computed property body that is expecting you to a return a type that conforms to the View protocol (i.e. should have the body property). This will go forever. Hence, there has to be a View without the body property.
I’m following this tutorial for SwiftUI amplify app where I came across this error when creating a final class which conforms to Bindable object.
Error:Use of undeclared type 'BindableObject'
import Combine
import SwiftUI
import AWSAppSync
final class TalkStore: BindableObject {
/*
Required by SwiftUI
*/
let didChange = PassthroughSubject<TalkStore, Never>()
var listTalks: [ListTodosQuery.Data.ListTodo.Item] {
didSet {
didChange.send(self)
}
}
//We will be using this later.
private let appSyncClient: AWSAppSyncClient!
/*
Init if running app is using SwiftUI Content View
*/
init(talks: [ListTodosQuery.Data.ListTodo.Item]) {
self.appSyncClient = nil
self.listTalks = talks
}
}
Is it possible that Apple has changed the class name?
How do I find that out?
BindableObject has been renamed ObservableObject
BindableObject is replaced by the ObservableObject protocol from the Combine framework. (50800624)
Source: https://developer.apple.com/documentation/ios_ipados_release_notes/ios_13_release_notes
I have an existing Xcode project. I need to create a new view. I was going to use File > New > File... > View, but then I noticed there is another option SwiftUI View. So I decided to give it a shot. I went ahead and chose SwiftUI View.
It created the following starter code:
import SwiftUI
struct FooBar: View {
var body: some View {
Text("Hello, World!")
}
}
struct FooBar_Previews: PreviewProvider {
static var previews: some View {
FooBar()
}
}
And it has the following compiler errors:
I tried cleaning, and selecting the iPhone 11 Pro simulator as the destination target, but it still has the same compiler errors.
For search-ability, here are all the errors:
Inheritance from non-protocol type 'View' (aka 'UIView')
Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
Cannot convert return expression of type 'Text' to return type 'some View'
Type 'FooBar_Previews' does not conform to protocol 'PreviewProvider'
Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
Cannot convert return expression of type 'FooBar' to return type 'some View'
I'm on macOS Catalina, Xcode 11.2.1, and the deployment target is iOS 13.0.
What am I doing wrong?
It seems to us there is a typealias View = UIView some where to mess the View with UIView.
I have a project using the NVActivityIndicatorView library and I am trying to extract some logic from two view controllers. Both view controllers conform to NVActivityIndicatorViewable whose definition is:
// From the libaray. I don't want to modify this.
public protocol NVActivityIndicatorViewable {}
public extension NVActivityIndicatorViewable where Self: UIViewController
{
func startAnimating( ... ) { ... }
func stopAnimating() { ... }
}
And as a result, I expected to be able to pass one of these view controllers in and use the startAnimation and stopAnimation methods on it.
func sharedLogic(sender: NVActivityIndicatorViewable)
{
sender.startAnimating( ... )
sender.stopAnimating()
}
However, this fails with the compiler error 'NVActivityIndicatorViewable' requires that 'NVActivityIndicatorViewable' inherit from 'UIViewController'
Trying this with sender: UIViewController, this fails with the compile time error Value of 'UIViewController' has no member 'startAnimating' as I expected.
func sharedLogic(sender: UIViewController)
{
sender.startAnimating( ... )
sender.stopAnimating()
}
I have found two potential solutions:
Create an empty subclass that specifies both these types: (This new type doesn't contain any logic)
class ActivityIndicatorViewController: UIViewController, NVActivityIndicatorViewable { }
Use an extension to specify all view controllers can be activity indicators: (This causes redundant conformance errors on many classes)
extension UIViewController: NVActivityIndicatorViewable { }
Can I accomplish this without creating a new type?
Environment settings:
Xcode version: 10.1
iOS Deployment Target: 9.0
Swift version: 3
What you want here is a composition type - a type that both inherits from UIViewController and conforms to NVActivityIndicatorViewable:
UIViewController & NVActivityIndicatorViewable
You can use this directly as the parameter type for your method:
func sharedLogic(sender: UIViewController & NVActivityIndicatorViewable)
Or you can create a typealias for it (though I can't think of a shorter name):
typealias SomeShorterName = UIViewController & NVActivityIndicatorViewable
And then you can use SomeShorterName as the parameter type.
You set constraint on the protocol and extended the constrained one. So if you want those two functions, you need a UIViewContoller conformed to your protocol.
Move functions to the original protocol to get what you need.
public protocol NVActivityIndicatorViewable {
func startAnimating( ... )
func stopAnimating()
}
Update due to comment:
If you want to leave the original Protocol untouched, use composition type for the function:
func sharedLogic(sender: UIViewController & NVActivityIndicatorViewable) {
sender.startAnimating()
sender.stopAnimating()
}