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

Add extension points for global node behaviors, potentially replacing interface state delegates #1229

Merged
merged 2 commits into from Feb 14, 2019

Conversation

Adlai-Holler
Copy link
Member

@Adlai-Holler Adlai-Holler commented Nov 10, 2018

right at the outset:

  • Causes no compiler warnings
  • Does nothing unsafe or undocumented

Node controllers were added as a way for clients to customize node behavior globally. If you subclass ASTextNode and ASImageNode, you have to duplicate any common logic you want to add.

Node controllers however introduce additional complexity and duplication – if ASNetworkImageNode can fire off network requests, why does it need a controller? Nodes are in fact a sort of tiny view controller and we should stick with the original abstraction instead of adding a different one in.

This diff does something pretty strange, but I think useful. It modifies the node API to (1) offer a public ivar to ASDisplayNode and (2) guarantee that certain ASDisplayNode+Subclass category methods e.g. interface state methods are not implemented by the base class, and thus can be overridden safely in categories. To make it safe, we put in empty stubs for methods that they don't choose to implement.

I modified CatDealsCollectionView to demonstrate how this extension point could be used to add visibility logging events at the application level.

I propose that this API informally deprecates the Beta ASNodeController and the interfaceStateDelegates APIs. Those systems will be removed.

@Adlai-Holler Adlai-Holler changed the title Add extension points for global node behaviors, potentially replacing node controller Add extension points for global node behaviors, potentially replacing interface state delegates Nov 10, 2018
@Adlai-Holler
Copy link
Member Author

cc #1227

@TextureGroup TextureGroup deleted a comment Nov 10, 2018
@TextureGroup TextureGroup deleted a comment Nov 10, 2018
@TextureGroup TextureGroup deleted a comment Nov 10, 2018
@TextureGroup TextureGroup deleted a comment Nov 10, 2018
@TextureGroup TextureGroup deleted a comment Nov 10, 2018
@TextureGroup TextureGroup deleted a comment Nov 10, 2018
@TextureGroup TextureGroup deleted a comment Nov 10, 2018
@appleguy
Copy link
Member

@Adlai-Holler the rationale seems strong; Node Controllers were indeed added to allow global extension to ASDisplayNode subclasses by non-Texture / app-specific users. Indeed we have also found difficulties with Node Controllers as most infrastructure operates on e.g. ASCellNode specifically.

For some use cases it may still be required to have this state tracking done outside of the ASDisplayNode instance, if there is the possibility of that instance itself being swapped out. This introduces the potential complication of a layer we don't want to be in the layout tree, but should have jurisdiction over node children (e.g. like a component). If this approach allows for that, then we should be good!

@TextureGroup TextureGroup deleted a comment Nov 10, 2018
@Adlai-Holler
Copy link
Member Author

@appleguy Thanks for reviewing this! This approach is agnostic toward what you mentioned – if the client ever wanted to carry state information between nodes when the nodes are swapped out, it is straightforward and easy to do.

@Adlai-Holler
Copy link
Member Author

I'm curious to hear @maicki's take as well as @wiseoldduck and @wsdwsd0829 if they have time.

@wiseoldduck
Copy link
Member

I think if we can accomplish what we want via the delegates, that would be better from an OOD standpoint. The problems you describe & we have encountered are typical to a customize-through-subclasses approach and are why customization-via-composition (the pattern which the delegates approach attempts) is generally considered better than customization-via-inheritance.

Categories help some but require the clients to understand the state of the node including locking, which seems less friendly than figuring out how to call the delegates from a consistent state where API re-entry is safe.

@Adlai-Holler
Copy link
Member Author

OK I changed the names to baseDidInit and baseWillDealloc to be I think very clear. @maicki @wiseoldduck please review.

@wiseoldduck
Copy link
Member

wiseoldduck commented Feb 1, 2019

Well I am a lot more friendly to this now that I have seen what a $@#%*&(^ it will be to make the delegate & override methods safe for re-entry (always unlocked)

@Adlai-Holler
Copy link
Member Author

We still should not call out with the lock held unless we document that for specific cases 😭

Source/ASDisplayNode.h Outdated Show resolved Hide resolved
Copy link
Member

@nguyenhuy nguyenhuy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it’s ok to merge this diff as is. However, I’m a bit concerned about the proposal to deprecate and (eventually) remove interface state delegates because of 2 reasons:

  • The arguments that node controllers introduce additional complexity and that we should stick with node being a thin view controller abstraction are strong to me. However, I’m not sure if the arguments are equally applicable to interface state delegates?
  • Providing hooks/extensions via delegates is a concept familiar to people coming from UIKit world. On top of that, there are certain use cases that the delegate approach works great and is simpler than the new context approach. For example, one can have an impression tracker that cares about a subset of nodes over a short period of time (visible nodes during app cold start, for example). In this case, the tracker is a separate entity -- detached from the node tree or any other tree -- and isn’t interested in extending nodes on a global scale. As such, I believe it’s beneficial to provide both approaches and let users decide on which one works best for them in a particular use case.

Source/ASDisplayNode.mm Show resolved Hide resolved
@garrettmoon
Copy link
Member

I want to echo Huy that removing the interface state tracking in a follow up PR would cause us a bit of a burden and I think it's useful.

@garrettmoon
Copy link
Member

Additionally something to think through:

If someone decides to make a framework (let's say SuperAwesomeGridNode) that implements one of these categories, they're going to have to know that they need to explicitly warn their users not to also implement the category. And potentially create a different method that their category calls that's similar to this.

So if SuperAwesomeGridNode (ASDisplayNode) implements didEnterPreloadState they would have to document that they've done so, and implement another method `SAGNDidEnterPreloadState that they would have to stub out and call so clients could still hook in.

Long story short if we're going to hack this it should be documented with an @warning at the very least…

@Adlai-Holler
Copy link
Member Author

Adlai-Holler commented Feb 14, 2019 via email

@Adlai-Holler
Copy link
Member Author

Thanks for the reviews guys. We'll keep interface state delegates around and land this to start removing reliance on node controllers. I'm pretty stoked about this because it's been a long-standing limitation of the framework!

@Adlai-Holler Adlai-Holler merged commit 6e7cdea into master Feb 14, 2019
@Adlai-Holler Adlai-Holler deleted the AHGlobalExtensions branch February 14, 2019 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants