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
[swift] Remove view registry #974
Conversation
2b98700
to
3476fb7
Compare
private lazy var trailingContainerView: ContainerView = .init() | ||
|
||
private var currentRatio: CGFloat { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got rid of these redundant copies of the values while I was in here
|
||
update(with: screen) | ||
} | ||
fileprivate final class LoginViewController: UIViewController { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made this one not be a subclass of ScreenViewController
just to show that is’s not necessary as a superclass
@@ -19,51 +19,60 @@ | |||
import UIKit | |||
|
|||
|
|||
internal final class AnyScreenViewController: ScreenViewController<AnyScreen> { | |||
public final class DescribedViewController: UIViewController { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn’t a rename, but a rewrite. I suppose they do perform mostly the same job, though.
@@ -19,24 +19,22 @@ | |||
import UIKit | |||
|
|||
public struct AnyScreen: Screen { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to see this collapse into just a passthrough for the view controller description
internal func buildViewController() -> UIViewController { | ||
let viewController = build() | ||
assert(canUpdate(viewController: viewController), "View controller description built a view controller it cannot update (\(viewController) is not exactly type \(viewControllerType))") | ||
return viewController |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I initially considered calling update with the view controller after building it. Technically that’s redundant for our ScreenViewController subclasses (since they all take the screen in in init), but I’m worried that omitting it will cause a lot of people to initially build a view controller that’s not fully populated without realizing it. Anyone opposed to calling update
eagerly here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could that cause an issue if an update
implementation expects the view controller to be inside a window?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
canUpdate
is just the type check, the update
implementation doesn’t play in here. Though, if someone wrote an update
method that required being in a window, I’d be very sad at them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I totally misattributed your question and thought it was about the assert(canUpdate…
. So, yeah doing an update eagerly here would break a view controller’s ability to assume it’s in a window when it gets updated. But I strongly believe that’s not an assumption we should allow people to make.
This removes the view registry in favor of having screens provide their own view controllers (via `ViewControllerDescription`). This improves compile-time safety as well as allows for easier container types (we can now have generic screen types, and pass-through containers no longer must provide a wrapper view controller).
6d748b3
to
c01b2a8
Compare
/// - build: Closure that produces a new instance of the view controller | ||
/// - update: Closure that updates the given view controller | ||
public init<VC: UIViewController>(type: VC.Type = VC.self, build: @escaping () -> VC, update: @escaping (VC) -> Void) { | ||
self.viewControllerType = type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of doing this, what if we hold-on to the canUpdate
block?
self.canUpdate = { untypedVC in
return untypedVC is VC
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I played with that locally, but found it really useful for debugging to have the type around in a printable manner. I also don’t think we want is
because we only want exact matches, not subclasses IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious why we wouldn't want to allow subclasses in this context (out of ignorance, I'm not familiar enough yet to know why or why not to allow it :) )
I could see a use case for something like subclasses altering layout of the same views, and using the same screen to update the super class and the subclass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since a single view controller description is responsible for building and updating, I can’t imagine a scenario where you’d reasonably build a type of a superclass and then be passed a value of a subclass, except by mistake.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually want to be able to pass in something other than the default (VC.self
) here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case of the extension on ScreenViewController
, it was inferring a type of ScreenViewController<FooScreen>
instead of FooViewController
with older Swift versions (where I couldn’t do Self(screen: screen)
and instead had to do self.init(screen: screen)
. Adding this hinting allowed me to fix that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome, thank you!
swift/Samples/SplitScreenContainer/Sources/SplitScreenContainerViewController.swift
Show resolved
Hide resolved
swift/WorkflowUI/Sources/ViewControllerDescription/DescribedViewController.swift
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really really great! Thank you! Just left a few comments.
Cases where we want to change the resulting view controller type (e.g. adapt a Also, do we care that this requires that we use view controllers as the currency globally (as opposed to e.g. views)? |
Yep, that’s the intended pattern should we need it. It would also be valid for a workflow to render a type that’s not a
Not personally. It’s already the case today with view registry. We could provide a pretty easy convenience for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome, but how bad is updating existing code to use this going to be? Should we leave the ViewRegistry
parameters in for now and do a release with support for the new technique, so we can migrate existing code over incrementally?
We tried a few approaches (#816) which would allow us to migrate incrementally, however there were some risks with this approach. Since we'll need the same The hard-removal approach is a lot more up-front work to do the migration, however we'll have compile-time safety and will be a cleaner migration. |
This removes the view registry in favor of having screens provide their own view controllers (via
ViewControllerDescription
).This improves compile-time safety as well as allows for easier container types (we can now have generic screen types, and pass-through containers no longer must provide a wrapper view
controller).
Closes #815