Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Musings on how to make it easier to control the flow of data for updating your UI #39

Open
ScottRobbins opened this issue Mar 31, 2019 · 8 comments
Milestone

Comments

@ScottRobbins
Copy link
Owner

originally brought up in #31

I also think this wrapper should think much harder about how to handle data to make updating state and having that trigger updates to the view easy.

@ScottRobbins ScottRobbins added this to the 0.8.0 milestone Mar 31, 2019
@ScottRobbins ScottRobbins removed this from the 0.8.0 milestone Mar 31, 2019
@ScottRobbins
Copy link
Owner Author

ScottRobbins commented Mar 31, 2019

Ignoring settings colors and non views/constraints, one possible direction is to build if statements into the layout:

ViewLayout(view: self.view) { (com) in
    com.stackView { (com) in 
        com.if(/*some condition*/) { (com) in 
            com.arrangedView(self.myView) { (com) in 
                ...
            }
        }.elseif(/*some condition*/) { (com) in 
            ...
        }.else(/*some condition*/) { (com) in
            ...
        }
    }
}

One part of this is that you'd no longer be explicitly calling to update your layout. You're describing it here. This function that describes the layout gets executed once.

So how could this work?

The condition statements inside of the if() function could require what is passed in to be a function. Something that we can store and later re-evaluate. Ideally these functions would be pure.

One of the tricks already to this framework is that at the call site, no constraints have been activated and no constraints. It also stores all of the decisions about view hierarchy and constraints in a tree of components.

The if statements would mark components under it to be active or inactive. This could be used to get the active constraints and current view layout that could be re-run through the diffing algorithm.


The next thing to solve is how to know when to re-evaluate the layout to make adjustments. The simplest implementation of this is to have each view layout store a single "ViewState" or "ViewModel" object. Require that for this framework to work, the functions inside of if() functions use data from only the view model. When the view layout's view state changes, it can re-run the decision functions to find what components should be active and automatically update things.

@ScottRobbins
Copy link
Owner Author

ScottRobbins commented Mar 31, 2019

What I see as the pros of this:

  • It's more declarative since you don't really control code inside this description. You don't get the opportunity for some other unknown state to cause differences in your UI. (I mean you could make that mistake, but it would be a mistake)
  • It doesn't require that during every call to layout that constraints be re-created. Also things like equatable could probably be sped up knowing there's a higher likelihood it actually gets the exact same constraint instance again (instead of comparing equatable to every property of the constraint)
  • In the future changes wouldn't require diffing the entire new layout to the entire old layout. could be smarter about what actually needs to be diffed.
  • I think that this whole thing could actually be its own subclass to set up strong barriers between the view function and data (so you'd have it in its own file since all data is dependent on this view state.

@ScottRobbins
Copy link
Owner Author

ScottRobbins commented Mar 31, 2019

Some cons:

  • It doesn't feel quite right that i'm re-implementing built-in swift control flow (like if statements). Do I need switches? ternary operators? etc
  • because the if statements require functions, you can't make some of those decisions on previous lines, you have to separate that to a function or pass in a closure. These lines could start to get long.
  • Is this really a feature for users, or is this a feature to improve performance? Does that performance really matter? Is there another way to achieve the declarative nature without re-making if statements? (like things like react/elm do)

@ScottRobbins
Copy link
Owner Author

ScottRobbins commented Mar 31, 2019

There are also a couple concerns:

  • How do things like animations work
  • Does navigation fit anywhere in this?
  • to really be declarative there has to be some consideration to view properties like colors, text, etc.

@ScottRobbins
Copy link
Owner Author

to handle the other UI stuff, we can probably make some kind of bindings interface: https://www.swiftbysundell.com/posts/bindable-values-in-swift

This doesn't work for all UIKit things because a lot has to be done with closures because UIKit sometimes relies on method calls. But a closure API could be tacked onto that.

I think this would work really well for UI configuration, but can it also work for deciding what path to go down for constraints/subviews being on screen? It's similar to the .if() I had there.

@ScottRobbins
Copy link
Owner Author

I think that if I go down this route I should remove the component returns from the APIs. The library will have strong enough opinions that making an opinionated library around it won't be easily possible.

@ScottRobbins
Copy link
Owner Author

There are also going to be times when you want to bind on multiple properties on a view state and we can't really do variadic generics in that way so you'd need some other type of API.

@ScottRobbins
Copy link
Owner Author

I think that going with a purely functional approach as I mocked up elsewhere with enums/structs, and using something like .if(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant