This is the 7th day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021
preface
I wrote a long time ago about the shortcomings of Flutter: Enumerations, presumably to poke fun at the weakness of the Dart language level of enumeration, which prevents some of the UI’s state-altering capabilities from working with Flutter.
I’ll leave it at that, but I’ve actually explored and thought about it a little bit in Swift, but I haven’t fully practiced it in RxSwift per se.
Write down some notes here.
The data model returned by network requests is defined through generics
HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse: HttpResponse:
Struct HttpRespone<T: Codable> {/// let code: String? // Let message: String? Let data: T? }Copy the code
Use UI components in protocol and UIKit
I’m trying to write an empty protocol like this and make UIView adhere to it
protocol Widget {}
extension UIView: Widget {}
Copy the code
We all know that Any is also a null protocol, and that all defined types implicitly obey it, so Any can represent Any type.
And we know that basically all OF the UI components are derived by inheriting UIView, making UIView comply with the Widget protocol, so you can think of all the components in UIKit flattening out at once.
Note: We say almost all UI components here because there are some UI components that do not inherit UIView, such as UIBarButtonItem:
open class UIBarButtonItem : UIBarItem, NSCoding {}
open class UIBarItem : NSObject, NSCoding, UIAppearance {}
For UIBarItem, we can try to handle it separately:
extension UIBarItem: Widget {}
Copy the code
Define closures to build pages from data
typealias BuilderWidget<T: Codable> = (HttpRespone<T>) -> Widget
Copy the code
This closure is typically used to build the UI out of data, pointing to widgets as returns, and UIView complies with the Widget protocol.
Defining state enumeration
Enumerated the main body
In general, a page is divided into the following situations:
-
loading
-
error
-
Success:
-
hasContent
-
noData
-
So our enumeration can be defined as follows:
enum ViewState<T: Codable> {
case loading
case error
case success(ViewSuccess)
enum ViewSuccess {
case noData
case content(BuilderWidget<T>, HttpRespone<T>)
}
}
Copy the code
Here I use the features of Swift enumeration — the enumeration is parameterized, with ViewSuccess in the SUCCESS state, and the closure and data to build the UI parameterized in viewSuccess.content for ease of call.
Enumerate the extended properties of a classification
In the enumeration category, I want to build different UI components using state values. This is just a preliminary validation case, because the custom Widget protocol is returned, so the compilation does not report an error:
extension ViewState {
var view: Widget {
switch self {
case .error:
return UIButton()
case .loading:
return UIActivityIndicatorView(style: .gray)
case .success(let successState):
switch successState {
case .noData:
return UILabel()
case .content(let builderWidget, let response):
return builderWidget(response)
}
}
}
}
Copy the code
Alternatively, we can extend the data property to specifically get network request values in enumeration state:
extension ViewState {
var data: T? {
switch self {
case .error:
return nil
case .loading:
return nil
case .success(let successState):
switch successState {
case .noData:
return nil
case .content(_, let response):
return response.data
}
}
}
}
Copy the code
Thinking of application direction
-
I consider using MVVM mode to program in THE RxSwift framework. In the ViewModel layer, the obtained network data is converted into a sequence of view states, and then the ViewModel and ViewController have chemical reactions.
-
Consider the idea of building pages with this kind of enumeration in SwiftUI.
SwiftUI
I tried to consider building pages in this form in SwiftUI, of course, it may also be because my SwiftUI is too clumsy and I don’t have a good idea at present:
Import SwiftUI @available(iOS 13.0, *) TypeAlias BuilderView = () -> View @Available (iOS 13.0, *) Extension ViewState: View {/// This some View returns some View /// but it must be of a unique type. For example, if you return EmptyView() in.error, you will immediately get an error. Var body: some View {switch self {case.error: return Text("") case.loading: return Text("") case .success(let successState): switch successState { case .noData: return Text("") case .content(_): return Text("") } } } }Copy the code
Typealias BuilderView = () -> View closure typeAlias BuilderView = () -> View closure
typealias BuilderView = () -> some View
Copy the code
Error reporting directly:
‘some’ types are only implemented for the declared type of properties and subscripts and the return type of functions
Var body: The return type of some View must be the same as that of some Viwe. For example, in the loading state, Text is returned, so in other states, Text is returned.
I consider wrapping an intermediate Container layer here to convert Text to Container(Text) and Button to Container(Button) to ensure that the var body: Type consistency in some View returns is still under consideration. How to define an input parameter constructor to do this, or how to define multiple static functions?
Here is an example and idea:
@available(iOS 13.0, *) struct Container: View {let text: String var body: some View {return text (text)}}Copy the code
Reference documentation
Flutter: The disadvantage of enumeration
conclusion
This article may be a little crossover, or a little unreliable, there are a lot of places to practice and thinking.
Later, I may introduce you to the practice of this idea with Flutter.
If you have any good ideas, welcome to exchange.