-
Notifications
You must be signed in to change notification settings - Fork 232
Add AutoRegistering protocol for automatic cell registration #63
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
base: main
Are you sure you want to change the base?
Conversation
AliSoftware
left a comment
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.
Some nitpickings.
Also, I'm not sure about that generic constraints limitation you talked about that forced you to duplicate the code, can you elaborate about the error you get if you don't duplicate?
|
The generic constraints issue relates to the Swift doesn't seem to like conditionally casting to a protocol, so this was the simple workaround. There might be a non-duplicating way around this that I haven't thought of though! |
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.
Looking good!
I think maybe we could still find a way to factorize the two same methods (which differ only from their where clause) to make them call a common private method.
After all the only difference is that each variant of this method calls a different variant of register(cellType:).
So if for example we provide a private helper method which takes a register: (() -> Void)? closure as a parameter (and no autoRegister: Bool), and do all the common stuff common to these two methods in there, then our two variants of the real public functions will just need to call that private helper function with registerClosure: autoRegister ? { register(cellType: cellType) } : nil — avoiding to duplicate the common code. Even if we'd still need to duplicate the methods, at least the implementations would be factorized that way
What do you think?
|
FYI the CI failure is due to the following linter errors: |
|
@apptekstudios I took the liberty to push a commit on your fork to show my idea of mutualising the code. (Also added missing documentation on the new method) There are still things missing in that PR imho:
So maybe it would be more powerful, flexible, and less error-prone for the end user, to declare a That way users of the Reusable library would just have to just add |
|
I've just pushed a commit to show you the idea of the alternative solution. I hope you like it 😉 I'll let you take over to complete the PR:
👍 |
|
Thanks for implementing the protocol - I agree that it's definitely a much clearer solution. I've implemented the AutoRegistering for Headers/Footers and added an example 👍. Hopefully I'll find time to write a quick documentation update tomorrow 🙂 |
|
Regarding UICollectionView: it seems that there's no API we can utilise to determine whether a class has been registered yet or not. A far less elegant solution would be to simply call register each time - although that would surely impact performance... (this depends on whether Apple's underlying implementation of the |
|
Ah, bummer. (I now remember why I'm personally not a fan of doing the auto registering dance in my own projects but prefer explicit registration 😅) Ok maybe we could indeed register every time before dequeuing (but only for collection views then), but we'd need to clearly document that and the performance trade-off in the documentation to make it crystal clear to users of Reusable then. Another way would be to keep our own list of registered cell classes per collection view (maybe using ObjC's associated objects?) so we can test before registration. But I wonder if that's not gonna be a similar performance impact anyway… |
|
I think associated objects is a good way to do it. I went ahead and implemented it 👍 Shouldn't have any performance impact, because all that is done is creating a single object per CollectionView which then tracks whether the cellType has been registered by us yet. |
AliSoftware
left a comment
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.
Linting 'InfoViewController.swift' (17/26)
/Users/distiller/project/Example/ReusableDemo iOS/CollectionViewController.swift:19: warning: Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace)
/Users/distiller/project/Example/ReusableDemo iOS/CollectionViewController.swift:23: warning: Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace
- Updated examples - Updated docs
|
Disclaimer: Did not checked the code so you may be already doing this. In a project I was doing mostly the same thing. For checking if the cell is registered I was trying to dequeue. If it throw -> catch, register then dequeue again |
|
@Alex293 but doesn't it throw an ObjC (*) well, unless if you create an ObjC helper method which catches the |
|
@apptekstudios What do you think about the alternate solution of catching the ObjC exception mentioned above (instead of going the whole way of using associated objects)? Something like: // ObjCException.h
#import <Foundation/Foundation.h>
@interface ObjC : NSObject
+ (BOOL)catch:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error;
@end
// ObjCException.m
#import "ObjCException.h"
@implementation ObjCException
+ (BOOL)catch:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error {
@try {
tryBlock();
return YES;
}
@catch (NSException *exception) {
*error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:exception.userInfo];
return NO;
}
}
@endThen using it like this from Swift: let cell = try? ObjCException.catch {
return collectionView.dequeue(…)
}I'm not sure which one is best, as:
What do you think? |
|
I had a go at catching the objective C exceptions, however couldn't get anything to compile properly with the objective C included in the pod. My opinion definitely leans toward keeping the library swift-only. It's a pity apple hasn't yet bridged ObjC-exceptions in UIKit methods across to swift errors... |
|
All right then. Could you just address the nitpickings I made in my latest review? I'll look into merging that next. |
|
Sorry for taking so long to get back to this - I've fixed those minor issues, pulled all changes from your master repo, and updated the changelog. |
| registerClosure: (() -> Void)?) -> T | ||
| where T: Reusable { | ||
| if let registerClosure = registerClosure { | ||
| if let bareCell = self.dequeueReusableCell(withIdentifier: cellType.reuseIdentifier) as? T { |
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.
Shouldn't we pass for: indexPath in that call here?
We implemented auto-Registration of tableViewCell types. This was done such that it is an optional, non-breaking change. The features is disabled by default, although that could be changed without breaking anything.
When calling dequeueReusableCell(...), one can now specify autoRegister: true, and it will take care of ensuring that the class/nib is registered. Note that in order to get around generic constraints this required duplicating the dequeueReusableCell function for each of Reusable and NibReusable.
Hopefully this is useful to someone!