Why SwiftUI's View Protocol use PAT? - swift

View protocol is defined like this:
public protocol View : _View {
/// The type of view representing the body of this view.
///
/// When you create a custom view, Swift infers this type from your
/// implementation of the required `body` property.
associatedtype Body : View
/// Declares the content and behavior of this view.
var body: Self.Body { get }
}
so View is now a PATed protocol, which can't be used as a return type directly, though swift 5.1's opaque return type can handle this, but why declare a associatedtype Body : View, not var body: View { get } directly?

Because if it is just a var body: Self.Body { get } - your entity, that implements View protocol, will not know the type of the body.
struct MyView: View {
var body: MyAnotherView {
//implementation...
}
}
This code will not compile and you would have to write this:
struct MyView: View {
var body: View {
//implementation...
}
}
And I think behind the scenes SwiftUI has to know the exact type of a View, not just a protocol

Prior to SwiftUI Swift doesn’t allow us to use protocols with associated types as return types but We can use “regular” protocols.
Compiler let you restrict by showing below error:
“Protocol can only be used as a generic constraint because it has Self or associated type requirements.”
What does it mean?
Compiler cannot infer the associated type from this definition and the return type would be incomplete.
Whenever we call the that function it always return different concrete type instead if same concrete type.
Compiler will not let you to perform the swapping, equal, compare operation on this concrete type. Even they adopt the same Protocol ( i.e it's PAT).
Because concret type may have different associated type that they fulfilled or use.
To avoid the different concrete type as return type at each call we use some keyword as opaque return type.
Opaque return type:
It's reverse of generic type.
It always return same concrete type.you and compiler know it.
If we use an opaque result type instead, we enforce that the function will always return the same concrete type.
Inside function while performing the operation we know the generic type.

Related

How can I know the source object of dot with no prefix, is it SwiftUI or is it Swift?

So, basic question probably but I would like to understand it clearly.
I don't understand the dot something syntax with no prefix. For example the navigationViewStyle below, I can guess that it is a method of the NavigationView which is a struct.
We didn't declare a Navigation View Struct because it's SwiftUI and that is style in this programmatic UI?
In that case when we write dot something how does it know which object to relate to? am I referencing the struct definition or a this instance of the struct?
If I write Self. or self. none of the methods show up.
Also for the .stack, I just picked .stack. When I checked the navigationViewStyle documentation, I did not find the three options (stack, automatic, columns) which appear when coding. What is the origin of the three options? Is it inherited from a higher class or a combintation of multiple protocols? In that case the logic scales up and down, how do you plan out what to do?
My problem is that when I read an already written code it seems organic but my issue is if I want to write code from scratch then unless I memorize the pattern I have no idea how to reach the same decision on what to write.
Also is this just for SwiftUI or in Swift in general?
import SwiftUI
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
SymbolGrid()
}
.navigationViewStyle(.stack)
}
}
}
To complete #hallo answer, when you have a method that have a parameter of some type, then you can use any static var that are define in the type without needing to specify the type.
struct MyType {
var myVar: Int
static var one = MyType(myVar:1)
static var two = MyType(myVar:2)
}
// suppose you have somewhere a func defined as :
func doSomething(with aVar: MyType {
// do something
…
}
// you can call the function :
doSomethin(with: MyType.one)
// or
doSomething(with: .one)
In the second call the compiler know that the type of data must be MyType so it will deduce that .one is MyType.one, so you do not have to specify MyType. Just use « .one ». This is some syntaxic sugar from Swift.
This also work for enum where you do not have to specify the type in such case.
.navigationViewStyle() takes anything conforming to NavigationViewStyle and the available styles are also defined as static computed variables for convenience. You can also write .navigationViewStyle(StackNavigationViewStyle()) for the same result, but that seems much less readable in my opinion and therefore you just write .navigationViewStyle(.stack)
You can always just ⌘ (command) + click on anything and then "Jump to Definition" to see (or ⌃⌘ + click) to see the underlying definition
#available(iOS 13.0, tvOS 13.0, watchOS 7.0, *)
#available(macOS, unavailable)
extension NavigationViewStyle where Self == StackNavigationViewStyle {
/// A navigation view style represented by a view stack that only shows a
/// single top view at a time.
public static var stack: StackNavigationViewStyle { get }
}
As you see NavigationViewStyle.stack will return a StackNavigationViewStyle

How to use any Object and pass it into a Generic SwiftUI View?

Context
I have a Protocol called Component and a Computed Variable, that returns a specific Object conforming to Component as any Component.
I also have a Generic SwiftUI View accepting a Component.
Compiler Error: Type 'any Component' cannot conform to 'Component'
Code
protocol Component: ObservableObject { var name: String }
struct ComponentView<C: Component>: View {
#ObservedObject var component: C
var body: some View { Text(c.name) }
}
struct RootView: View {
var body: some View {
ComponentView(component: component) // Compiler Error
}
private var component: any Component { // Returns any Component }
}
Question
I understand that any Component and Component are two different Types. However, I am looking for a possibility to use the given Component in the ComponentView. How can I achieve this goal of using the returned Component inside ComponentView?
The var component: any Component declaration means that the property can virtually hold any value that conforms to the protocol, meaning the exact type of the value stored there is to be determined at runtime.
On the other hand, ComponentView<C: Component> is a compile-time construct, and the compiler needs to know which type will fill in the blanks, something that is not possible due to the any construct.
You'll have to either
propagate any downstream - from RootView to ComponentView, and remove the generic on ComponentView, or
propagate the generic requirement upstream - from ComponentView to RootView, and make RootView generic.

Having an VStack as a property in a Struct

I need to have a VStack as a property in a struct like this
struct Str
{
let view: VStack
....
}
The compiler says:
"Reference to generic type 'VStack' requires arguments in <...>
Insert '<<#Content: View#>>'
"
Xcodes "fix" produces:
struct Str
{
let view: VStack<Content: View>
...
}
But the compiler still complains:
"Expected '>' to complete generic argument list"
Is it allowed to have a VStack in a struct?
What you have discovered is that VStack is not really a type. We can more accurately describe it as a “type constructor”. It is declared like this:
struct VStack<Content> where Content : View {
...
}
You give it some other type as its Content argument, and it gives you a type. You give it the type Text and you get back the type VStack<Text>. You give it the type Image and you get back the type VStack<Image>. The types you get back are different types! A object of type VStack<Text> is not an object of type VStack<Image>.
So you just need to know what type to pass as the argument, right? Well, the problem is that lots of SwiftUI “types” are actually generic type constructors. So the type gets complex. Here's an example from an app I'm working on:
var body: some View {
VStack(spacing: 0) {
scene
if store.model.latestError != nil {
Divider()
ErrorView(store: errorStore)
.padding(20)
}
} //
.frame(width: 450)
}
What's the type of body? I can print it at runtime to find out:
print(type(of: AppView(store: store).body))
The output is
ModifiedContent<VStack<TupleView<(AnyView, Optional<TupleView<(Divider, ModifiedContent<ErrorView, _PaddingLayout>)>>)>>, _FrameLayout>
Notice that the type of the object returned by body exposes lots of details about body's implementation. For example, because the ErrorView has a padding modifier, the body object's type includes ModifiedContent<ErrorView, _PaddingLayout>. I can change the padding amount from 20 to 30 without changing the type. But if I remove the padding modifier entirely, it changes the type (by changing ModifiedContent<ErrorView, _PaddingLayout> to just ErrorView).
So how do you solve this? One way is to use AnyView:
struct Str {
let view: AnyView
...
}
let str = Str(view: AnyView(VStack {
...
}))
Note that sometimes you will hear advice that you should avoid AnyView. Here's Joe Groff explaining why:
You can use the AnyView wrapper to explicitly wrap up types where they can dynamically change.
However, the animation system will have an easier time if you can push this down the view graph as far as possible. You could have one Layout type that takes .portrait/.landscape, apply rotation, translation, etc. differently in response, and then the transition can animate

SwiftUI - PageView - Pass in different Views

I successfully implemented PageView within SwiftUI via thread:
How to implement PageView in SwiftUI?
Passing in multiple Views via an Array works like a charm, as long as all views are of the same struct.
PageView([TestView(), TestView()]).
However, I'd like to pass in different views.
PageView([TestView(), AnotherView(), DifferentView()]).
All views are of SwiftUI type:
struct NAME : View { code }
When I try to add different structs to an array I get the following error message:
var pageViewViewArray = [TestView(), AnotherView(), DifferentView()]
Heterogeneous collection literal could only be inferred to '[Any]';
add explicit type annotation if this is intentional.
Insert' as [Any]
By casting it to:
var pageViewViewArray = [TestView(), AnotherView(), DifferentView()] as! [Any]
PageView(pageViewViewArray)
PageView will say:
Protocol type 'Any' cannot conform to 'View' because only concrete types can conform to protocols
I'll greatly appreciate any ideas.
Try using type erasure by casting every view to AnyView:
var pageViewViewArray: [AnyView] = [AnyView(TestView()), AnyView(AnotherView()), AnyView(DifferentView())]
Documentation here, and an example of using it here.
There is a more efficient way to do it, without type erasure. You should create a common view in which you inject an enum value based on which you then return a desired view. Take a look at an example below:
/// The first type of a view that accepts and shows a number
struct ViewA: View {
let number: Int
var body: some View {
Text("\(number)")
}
}
/// The second type of a view that accepts and shows a string
struct ViewB: View {
let string: String
var body: some View {
Text(string)
}
}
/// An enum type used for dependency injection
enum ViewType {
case viewA(number: Int)
case viewB(string: String)
}
/// A common view which internally handles different kind of views based on injected enum value
struct CommonView: View {
let viewType: ViewType
var body: some View {
switch viewType {
case .viewA(let number):
ViewA(number: number)
case .viewB(let string):
ViewB(string: string)
}
}
}
// Views used for page view controller, that are now of same type:
var pageViewViewArray = [
CommonView(viewType: .viewA(number: 1)),
CommonView(viewType: .viewB(string: "Hello, World!"))
]

Statically typed properties in Swift protocols

I'm trying to use Protocol-Oriented Pgrogramming for model layer in my application.
I've started with defining two protocols:
protocol ParseConvertible {
func toParseObject() -> PFObject?
}
protocol HealthKitInitializable {
init?(sample: HKSample)
}
And after implementing first model which conforms to both I've noticed that another model will be basically similar so I wanted to create protocol inheritance with new one:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
var value: AnyObject { get set }
}
A you can see this protocol has one additional thing which is value but I want this value to be type independent... Right now I have models which use Double but who knows what may show up in future. If I leave this with AnyObject I'm sentenced to casting everything I want to use it and if I declare it as Double there's no sense in calling this BasicModel but rather BasicDoubleModel or similar.
Do you have some hints how to achieve this? Or maybe I'm trying to solve this the wrong way?
You probably want to define a protocol with an "associated type",
this is roughly similar to generic types.
From "Associated Types" in the Swift book:
When defining a protocol, it is sometimes useful to declare one or
more associated types as part of the protocol’s definition. An
associated type gives a placeholder name (or alias) to a type that is
used as part of the protocol. The actual type to use for that
associated type is not specified until the protocol is adopted.
Associated types are specified with the typealias keyword.
In your case:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
typealias ValueType
var value: ValueType { get set }
}
Then classes with different types for the value property can
conform to the protocol:
class A : BasicModel {
var value : Int
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
class B : BasicModel {
var value : Double
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
For Swift 2.2/Xcode 7.3 and later, replace typealias in the
protocol definition by associatedtype.