From 6c44e443e98bb87c955663c45c1245921338de1e Mon Sep 17 00:00:00 2001 From: Yarden Eitan Date: Mon, 12 Mar 2018 12:51:30 -0400 Subject: [PATCH 1/5] added a new metadata property that will hold all the key/values for that node. Also code refactoring (#27) --- example/Podfile.lock | 10 +- src/CBCCatalogExample.h | 30 ++++-- src/CBCNodeListViewController.h | 27 +++++- src/CBCNodeListViewController.m | 162 +++++++++++++++++--------------- src/private/CBCRuntime.h | 25 +---- src/private/CBCRuntime.m | 138 +++++++++++++++++---------- 6 files changed, 226 insertions(+), 166 deletions(-) diff --git a/example/Podfile.lock b/example/Podfile.lock index 93973d6..809cea3 100644 --- a/example/Podfile.lock +++ b/example/Podfile.lock @@ -22,11 +22,11 @@ EXTERNAL SOURCES: :path: components/Resistor SPEC CHECKSUMS: - CatalogByConvention: 1df2d770271921f668a99245c7c4c129e78941ee - CatalogExamples: cafe3e4eae3abc948d96beb626657455c1dfb327 - CatalogUnitTests: b7a746f12abb31a905654521ee926ea007ab7275 - Resistor: 36a9ae98666be3b4f34d8133fad442fa87fdbce2 + CatalogByConvention: f4b95f8905470807a5022eabd1d3d9ce07f6a66f + CatalogExamples: 7a95e6ea7befbd43c5ceb1427a9b161f6d8fc34e + CatalogUnitTests: 2fbf7f2e894dd3777f11573a7a5314adb1e4fad7 + Resistor: a17e39cab5f42993c2b3ede22ce3829b707a9ac8 PODFILE CHECKSUM: bb59c09c71f8777bbe79af5ae920e3d58849ab41 -COCOAPODS: 1.3.1 +COCOAPODS: 1.4.0 diff --git a/src/CBCCatalogExample.h b/src/CBCCatalogExample.h index 31deb60..7e3000c 100644 --- a/src/CBCCatalogExample.h +++ b/src/CBCCatalogExample.h @@ -25,35 +25,47 @@ */ @protocol CBCCatalogExample +/** + Returns a dictionary with metaata information for the example. + */ ++ (nonnull NSDictionary *)catalogMetadata; + +@optional + /** Return a list of breadcrumbs defining the navigation path taken to reach this example. */ -+ (nonnull NSArray *)catalogBreadcrumbs; ++ (nonnull NSArray *)catalogBreadcrumbs + __attribute__((deprecated("use catalogMetadata[CBCBreadcrumbs] instead."))); /** Return a BOOL stating whether this example should be treated as the primary demo of the component. */ -+ (BOOL)catalogIsPrimaryDemo; ++ (BOOL)catalogIsPrimaryDemo + __attribute__((deprecated("use catalogMetadata[CBCIsPrimaryDemo] instead.")));; /** Return a BOOL stating whether this example is presentable and should be part of the catalog app. */ -+ (BOOL)catalogIsPresentable; ++ (BOOL)catalogIsPresentable + __attribute__((deprecated("use catalogMetadata[CBCIsPresentable] instead."))); /** Return a BOOL stating whether this example is in debug mode and should appear as the initial view controller. */ -+ (BOOL)catalogIsDebug; - -@optional ++ (BOOL)catalogIsDebug + __attribute__((deprecated("use catalogMetadata[CBCIsDebug] instead."))); /** Return the name of a UIStoryboard from which the example's view controller should be instantiated. */ -- (nonnull NSString *)catalogStoryboardName; +- (nonnull NSString *)catalogStoryboardName + __attribute__((deprecated("use catalogMetadata[CBCStoryboardName] instead."))); /** Return a description of the example. */ -- (nonnull NSString *)catalogDescription; +- (nonnull NSString *)catalogDescription + __attribute__((deprecated("use catalogMetadata[CBCDescription] instead."))); /** Return a link to related information or resources. */ -- (nonnull NSURL *)catalogRelatedInfo; +- (nonnull NSURL *)catalogRelatedInfo + __attribute__((deprecated("use catalogMetadata[CBCRelatedInfo] instead."))); @end diff --git a/src/CBCNodeListViewController.h b/src/CBCNodeListViewController.h index f0a4028..c510a32 100644 --- a/src/CBCNodeListViewController.h +++ b/src/CBCNodeListViewController.h @@ -16,6 +16,21 @@ #import +/** This key represents a strings array of the breadcrumbs showing the hierarchy of the example */ +FOUNDATION_EXTERN NSString *_Nonnull const CBCBreadcrumbs; +/** This key represents a boolean value if the example is for debugging */ +FOUNDATION_EXTERN NSString *_Nonnull const CBCIsDebug; +/** This key represents a string for the description for the example */ +FOUNDATION_EXTERN NSString *_Nonnull const CBCDescription; +/** This key represents a boolean value if to present the example in the Catalog app or not */ +FOUNDATION_EXTERN NSString *_Nonnull const CBCIsPresentable; +/** This key represents a boolean value if the example is the primary demo */ +FOUNDATION_EXTERN NSString *_Nonnull const CBCIsPrimaryDemo; +/** This key represents an NSURL value providing related info for the example */ +FOUNDATION_EXTERN NSString *_Nonnull const CBCRelatedInfo; +/** This key represents a string value of the storyboard name for the example */ +FOUNDATION_EXTERN NSString *_Nonnull const CBCStoryboardName; + @class CBCNode; /** @@ -69,9 +84,6 @@ FOUNDATION_EXTERN CBCNode *_Nonnull CBCCreatePresentableNavigationTree(void); /** The title for this node. */ @property(nonatomic, copy, nonnull, readonly) NSString *title; -/** The description for this node. */ -@property(nonatomic, copy, nonnull, readonly) NSString *nodeDescription; - /** The children of this node. */ @property(nonatomic, strong, nonnull) NSArray *children; @@ -83,6 +95,13 @@ FOUNDATION_EXTERN CBCNode *_Nonnull CBCCreatePresentableNavigationTree(void); */ @property(nonatomic, strong, nullable) CBCNode *debugLeaf; +/** + This NSDictionary holds all the metadata related to this CBCNode. + If it is an example noe, a primary demo, related info, + if presentable in Catalog, etc. + */ +@property(nonatomic, strong, nonnull) NSDictionary *metadata; + /** Returns YES if this is an example node. */ - (BOOL)isExample; @@ -111,7 +130,7 @@ FOUNDATION_EXTERN CBCNode *_Nonnull CBCCreatePresentableNavigationTree(void); Check that isExample returns YES before invoking. */ -- (nonnull NSString *)exampleDescription; +- (nullable NSString *)exampleDescription; /** Returns a link to related information for the example. */ - (nullable NSURL *)exampleRelatedInfo; diff --git a/src/CBCNodeListViewController.m b/src/CBCNodeListViewController.m index e7f5f84..d4553e7 100644 --- a/src/CBCNodeListViewController.m +++ b/src/CBCNodeListViewController.m @@ -19,22 +19,21 @@ #import "CBCCatalogExample.h" #import "private/CBCRuntime.h" -void CBCAddNodeFromBreadCrumbs(CBCNode *tree, NSArray *breadCrumbs, Class aClass); +@interface CBCNode() +@property(nonatomic, strong, nullable) NSMutableDictionary *map; +@property(nonatomic, strong, nullable) Class exampleClass; +@end @implementation CBCNode { - NSMutableDictionary *_map; NSMutableArray *_children; - Class _exampleClass; - BOOL _isPresentable; } - (instancetype)initWithTitle:(NSString *)title { self = [super init]; if (self) { _title = [title copy]; - _map = [NSMutableDictionary dictionary]; + self.map = [NSMutableDictionary dictionary]; _children = [NSMutableArray array]; - _isPresentable = NO; CBCFixViewDebuggingIfNeeded(); } return self; @@ -45,22 +44,10 @@ - (NSComparisonResult)compare:(CBCNode *)otherObject { } - (void)addChild:(CBCNode *)child { - _map[child.title] = child; + self.map[child.title] = child; [_children addObject:child]; } -- (NSDictionary *)map { - return _map; -} - -- (void)setExampleClass:(Class)exampleClass { - _exampleClass = exampleClass; -} - -- (void)setIsPresentable:(Class)exampleClass { - _isPresentable = CBCCatalogIsPresentableFromClass(exampleClass); -} - - (void)finalizeNode { _children = [[_children sortedArrayUsingSelector:@selector(compare:)] mutableCopy]; } @@ -68,34 +55,49 @@ - (void)finalizeNode { #pragma mark Public - (BOOL)isExample { - return _exampleClass != nil; + return self.exampleClass != nil; } - (NSString *)exampleViewControllerName { + NSAssert(self.exampleClass != nil, @"This node has no associated example."); return NSStringFromClass(_exampleClass); } - (UIViewController *)createExampleViewController { - NSAssert(_exampleClass != nil, @"This node has no associated example."); - return CBCViewControllerFromClass(_exampleClass); + NSAssert(self.exampleClass != nil, @"This node has no associated example."); + return CBCViewControllerFromClass(self.exampleClass, self.metadata); } - (NSString *)exampleDescription { - NSAssert(_exampleClass != nil, @"This node has no associated example."); - return CBCDescriptionFromClass(_exampleClass); + NSString *description = [self.metadata objectForKey:CBCDescription]; + if (description != nil && [description isKindOfClass:[NSString class]]) { + return description; + } + return nil; } - (NSURL *)exampleRelatedInfo { - NSAssert(_exampleClass != nil, @"This node has no associated example."); - return CBCRelatedInfoFromClass(_exampleClass); + NSURL *relatedInfo = [self.metadata objectForKey:CBCRelatedInfo]; + if (relatedInfo != nil && [relatedInfo isKindOfClass:[NSURL class]]) { + return relatedInfo; + } + return nil; } - (BOOL)isPrimaryDemo { - return CBCCatalogIsPrimaryDemoFromClass(_exampleClass); + id isPrimaryDemo; + if ((isPrimaryDemo = [self.metadata objectForKey:CBCIsPrimaryDemo]) != nil) { + return [isPrimaryDemo boolValue]; + } + return NO; } - (BOOL)isPresentable { - return _isPresentable; + id isPresentable; + if ((isPresentable = [self.metadata objectForKey:CBCIsPresentable]) != nil) { + return [isPresentable boolValue]; + } + return NO; } @end @@ -103,14 +105,14 @@ - (BOOL)isPresentable { @implementation CBCNodeListViewController - (instancetype)initWithNode:(CBCNode *)node { - NSAssert(!_node.isExample, @"%@ cannot represent example nodes.", + NSAssert(!self.node.isExample, @"%@ cannot represent example nodes.", NSStringFromClass([self class])); self = [super initWithNibName:nil bundle:nil]; if (self) { _node = node; - self.title = _node.title; + self.title = self.node.title; } return self; } @@ -154,7 +156,7 @@ - (void)viewDidAppear:(BOOL)animated { #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return (NSInteger)[_node.children count]; + return (NSInteger)[self.node.children count]; } - (UITableViewCell *)tableView:(UITableView *)tableView @@ -164,7 +166,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; } - cell.textLabel.text = [_node.children[(NSUInteger)indexPath.row] title]; + cell.textLabel.text = [self.node.children[(NSUInteger)indexPath.row] title]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; return cell; } @@ -172,7 +174,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - CBCNode *node = _node.children[(NSUInteger)indexPath.row]; + CBCNode *node = self.node.children[(NSUInteger)indexPath.row]; UIViewController *viewController = nil; if ([node isExample]) { viewController = [node createExampleViewController]; @@ -184,30 +186,61 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath @end -static CBCNode *CBCCreateTreeWithOnlyPresentable(BOOL onlyPresentable) { - NSArray *allClasses = CBCGetAllClasses(); - NSArray *breadcrumbClasses = CBCClassesRespondingToSelector(allClasses, - @selector(catalogBreadcrumbs)); - NSArray *classes; - if (onlyPresentable) { - classes = [breadcrumbClasses filteredArrayUsingPredicate: - [NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) { - return CBCCatalogIsPresentableFromClass(object); - }]]; - } else { - classes = breadcrumbClasses; +static void CBCAddNodeFromBreadCrumbs(CBCNode *tree, + NSArray *breadCrumbs, + Class aClass, + NSDictionary *metadata) { + // Walk down the navigation tree one breadcrumb at a time, creating nodes along the way. + + CBCNode *node = tree; + for (NSUInteger ix = 0; ix < [breadCrumbs count]; ++ix) { + NSString *title = breadCrumbs[ix]; + BOOL isLastCrumb = ix == [breadCrumbs count] - 1; + + // Don't walk the last crumb + if (node.map[title] && !isLastCrumb) { + node = node.map[title]; + continue; + } + + CBCNode *child = [[CBCNode alloc] initWithTitle:title]; + [node addChild:child]; + child.metadata = metadata; + if ([[child.metadata objectForKey:CBCIsPrimaryDemo] boolValue] == YES) { + node.metadata = child.metadata; + } + if ([[child.metadata objectForKey:CBCIsDebug] boolValue] == YES) { + tree.debugLeaf = child; + } + node = child; } - CBCNode *tree = [[CBCNode alloc] initWithTitle:@"Root"]; - for (Class aClass in classes) { - // Each example view controller defines its own "breadcrumbs". - NSArray *breadCrumbs = CBCCatalogBreadcrumbsFromClass(aClass); + node.exampleClass = aClass; +} + +static CBCNode *CBCCreateTreeWithOnlyPresentable(BOOL onlyPresentable) { + NSArray *allClasses = CBCGetAllCompatibleClasses(); + NSArray *filteredClasses = [allClasses filteredArrayUsingPredicate: + [NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) { + NSDictionary *metadata = CBCCatalogMetadataFromClass(object); + id breadcrumbs = [metadata objectForKey:CBCBreadcrumbs]; + BOOL validObject = breadcrumbs != nil && [breadcrumbs isKindOfClass:[NSArray class]]; + if (onlyPresentable) { + validObject &= ([[metadata objectForKey:CBCIsPresentable] boolValue] == YES); + } + return validObject; + }]]; + CBCNode *tree = [[CBCNode alloc] initWithTitle:@"Root"]; + for (Class aClass in filteredClasses) { + // Each example view controller defines its own breadcrumbs (metadata[CBCBreadcrumbs]). + NSDictionary *metadata = CBCCatalogMetadataFromClass(aClass); + NSArray *breadCrumbs = [metadata objectForKey:CBCBreadcrumbs]; if ([[breadCrumbs firstObject] isKindOfClass:[NSString class]]) { - CBCAddNodeFromBreadCrumbs(tree, breadCrumbs, aClass); + CBCAddNodeFromBreadCrumbs(tree, breadCrumbs, aClass, metadata); } else if ([[breadCrumbs firstObject] isKindOfClass:[NSArray class]]) { for (NSArray *parallelBreadCrumb in breadCrumbs) { - CBCAddNodeFromBreadCrumbs(tree, parallelBreadCrumb, aClass); + CBCAddNodeFromBreadCrumbs(tree, parallelBreadCrumb, aClass, metadata); } } } @@ -232,30 +265,3 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath CBCNode *CBCCreatePresentableNavigationTree(void) { return CBCCreateTreeWithOnlyPresentable(YES); } - -void CBCAddNodeFromBreadCrumbs(CBCNode *tree, NSArray *breadCrumbs, Class aClass) { - // Walk down the navigation tree one breadcrumb at a time, creating nodes along the way. - - CBCNode *node = tree; - for (NSUInteger ix = 0; ix < [breadCrumbs count]; ++ix) { - NSString *title = breadCrumbs[ix]; - BOOL isLastCrumb = ix == [breadCrumbs count] - 1; - - // Don't walk the last crumb - - if (node.map[title] && !isLastCrumb) { - node = node.map[title]; - continue; - } - - CBCNode *child = [[CBCNode alloc] initWithTitle:title]; - [node addChild:child]; - [node setIsPresentable:aClass]; - if (CBCCatalogIsDebugLeaf(aClass)) { - tree.debugLeaf = child; - } - node = child; - } - - node.exampleClass = aClass; -} diff --git a/src/private/CBCRuntime.h b/src/private/CBCRuntime.h index aad0663..085755d 100644 --- a/src/private/CBCRuntime.h +++ b/src/private/CBCRuntime.h @@ -17,24 +17,15 @@ #import #import -#pragma mark Breadcrumb retrieval +#pragma mark Class invocations -/** Invokes +catalogBreadcrumbs on the class and returns the corresponding array of strings. */ -FOUNDATION_EXTERN NSArray *CBCCatalogBreadcrumbsFromClass(Class aClass); - -/** Invokes +catalogIsPrimaryDemo on the class and returns the BOOL value. */ -FOUNDATION_EXTERN BOOL CBCCatalogIsPrimaryDemoFromClass(Class aClass); - -/** Invokes +catalogIsPresentable on the class and returns the BOOL value. */ -FOUNDATION_EXTERN BOOL CBCCatalogIsPresentableFromClass(Class aClass); - -/** Invokes +catalogIsDebug on the class and returns the BOOL value. */ -FOUNDATION_EXTERN BOOL CBCCatalogIsDebugLeaf(Class aClass); +/** Invokes +catalogMetadata on the class and returns the NSDictionary value */ +FOUNDATION_EXTERN NSDictionary *CBCCatalogMetadataFromClass(Class aClass); #pragma mark Runtime enumeration /** Returns all Objective-C and Swift classes available to the runtime. */ -FOUNDATION_EXTERN NSArray *CBCGetAllClasses(void); +FOUNDATION_EXTERN NSArray *CBCGetAllCompatibleClasses(void); /** Returns an array of classes that respond to a given static method selector. */ FOUNDATION_EXTERN NSArray *CBCClassesRespondingToSelector(NSArray *classes, @@ -55,13 +46,7 @@ void CBCCatalogInvokeFromClassAndSelector(Class aClass, SEL selector, void *retV be created with the returned name. The returned view controller will be instantiated by invoking -instantiateInitialViewController on the UIStoryboard instance. */ -FOUNDATION_EXTERN UIViewController *CBCViewControllerFromClass(Class aClass); - -/** Create a description from the provided class. **/ -FOUNDATION_EXTERN NSString *CBCDescriptionFromClass(Class aClass); - -/** Create a link to related information from the provided class. **/ -FOUNDATION_EXTERN NSURL *CBCRelatedInfoFromClass(Class aClass); +FOUNDATION_EXTERN UIViewController *CBCViewControllerFromClass(Class aClass, NSDictionary *metadata); #pragma mark Fix View Debugging diff --git a/src/private/CBCRuntime.m b/src/private/CBCRuntime.m index 738748a..39e3fee 100644 --- a/src/private/CBCRuntime.m +++ b/src/private/CBCRuntime.m @@ -15,58 +15,112 @@ */ #import "CBCRuntime.h" - #import "CBCCatalogExample.h" - #import -#pragma mark Breadcrumb retrieval +#pragma mark Metadata keys -NSArray *CBCCatalogBreadcrumbsFromClass(Class aClass) { - return [aClass performSelector:@selector(catalogBreadcrumbs)]; -} +NSString *const CBCBreadcrumbs = @"breadcrumbs"; +NSString *const CBCIsDebug = @"debug"; +NSString *const CBCDescription = @"description"; +NSString *const CBCIsPresentable = @"presentable"; +NSString *const CBCIsPrimaryDemo = @"primaryDemo"; +NSString *const CBCRelatedInfo = @"relatedInfo"; +NSString *const CBCStoryboardName = @"storyboardName"; -#pragma mark Primary demo check - -void CBCCatalogInvokeFromClassAndSelector(Class aClass, SEL selector, void *retValue) { - if ([aClass respondsToSelector:selector]) { - NSMethodSignature *signature = - [aClass methodSignatureForSelector:selector]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; - invocation.selector = selector; - invocation.target = aClass; - [invocation invoke]; - [invocation getReturnValue:retValue]; - } +#pragma mark Class invocations + +static NSArray *CBCCatalogBreadcrumbsFromClass(Class aClass) { + return [aClass performSelector:@selector(catalogBreadcrumbs)]; } -BOOL CBCCatalogIsPrimaryDemoFromClass(Class aClass) { +static BOOL CBCCatalogIsPrimaryDemoFromClass(Class aClass) { BOOL isPrimary = NO; - CBCCatalogInvokeFromClassAndSelector(aClass, - @selector(catalogIsPrimaryDemo), - &isPrimary); + if ([aClass respondsToSelector:@selector(catalogIsPrimaryDemo)]) { + isPrimary = [aClass catalogIsPrimaryDemo]; + } return isPrimary; } -BOOL CBCCatalogIsPresentableFromClass(Class aClass) { +static BOOL CBCCatalogIsPresentableFromClass(Class aClass) { BOOL isPresentable = NO; - CBCCatalogInvokeFromClassAndSelector(aClass, - @selector(catalogIsPresentable), - &isPresentable); + if ([aClass respondsToSelector:@selector(catalogIsPresentable)]) { + isPresentable = [aClass catalogIsPresentable]; + } return isPresentable; } -BOOL CBCCatalogIsDebugLeaf(Class aClass) { +static BOOL CBCCatalogIsDebugLeaf(Class aClass) { BOOL isDebugLeaf = NO; - CBCCatalogInvokeFromClassAndSelector(aClass, - @selector(catalogIsDebug), - &isDebugLeaf); + if ([aClass respondsToSelector:@selector(catalogIsDebug)]) { + isDebugLeaf = [aClass catalogIsDebug]; + } return isDebugLeaf; } +static NSURL *CBCRelatedInfoFromClass(Class aClass) { + NSURL *catalogRelatedInfo = nil; + if ([aClass respondsToSelector:@selector(catalogRelatedInfo)]) { + catalogRelatedInfo = [aClass catalogRelatedInfo]; + } + return catalogRelatedInfo; +} + +static NSString *CBCDescriptionFromClass(Class aClass) { + NSString *catalogDescription = nil; + if ([aClass respondsToSelector:@selector(catalogDescription)]) { + catalogDescription = [aClass catalogDescription]; + } + return catalogDescription; +} + +static NSString *CBCStoryboardNameFromClass(Class aClass) { + NSString *catalogStoryboardName = nil; + if ([aClass respondsToSelector:@selector(catalogStoryboardName)]) { + catalogStoryboardName = [aClass catalogStoryboardName]; + } + return catalogStoryboardName; +} + +static NSDictionary *CBCConstructMetadataFromMethods(Class aClass) { + NSMutableDictionary *catalogMetadata = [NSMutableDictionary new]; + if ([aClass respondsToSelector:@selector(catalogBreadcrumbs)]) { + [catalogMetadata setObject:CBCCatalogBreadcrumbsFromClass(aClass) forKey:CBCBreadcrumbs]; + [catalogMetadata setObject:[NSNumber numberWithBool:CBCCatalogIsPrimaryDemoFromClass(aClass)] + forKey:CBCIsPrimaryDemo]; + [catalogMetadata setObject:[NSNumber numberWithBool:CBCCatalogIsPresentableFromClass(aClass)] + forKey:CBCIsPresentable]; + [catalogMetadata setObject:[NSNumber numberWithBool:CBCCatalogIsDebugLeaf(aClass)] + forKey:CBCIsDebug]; + NSURL *relatedInfo; + if ((relatedInfo = CBCRelatedInfoFromClass(aClass)) != nil) { + [catalogMetadata setObject:CBCRelatedInfoFromClass(aClass) forKey:CBCRelatedInfo]; + } + NSString *description; + if ((description = CBCDescriptionFromClass(aClass)) != nil) { + [catalogMetadata setObject:CBCDescriptionFromClass(aClass) forKey:CBCDescription]; + } + NSString *storyboardName; + if ((storyboardName = CBCStoryboardNameFromClass(aClass)) != nil) { + [catalogMetadata setObject:CBCStoryboardNameFromClass(aClass) forKey:CBCStoryboardName]; + } + } + return catalogMetadata; +} + +NSDictionary *CBCCatalogMetadataFromClass(Class aClass) { + NSDictionary *catalogMetadata; + if ([aClass respondsToSelector:@selector(catalogMetadata)]) { + catalogMetadata = [aClass catalogMetadata]; + } else { + catalogMetadata = CBCConstructMetadataFromMethods(aClass); + } + return catalogMetadata; +} + #pragma mark Runtime enumeration -NSArray *CBCGetAllClasses(void) { +NSArray *CBCGetAllCompatibleClasses(void) { int numberOfClasses = objc_getClassList(NULL, 0); Class *classList = (Class *)malloc((size_t)numberOfClasses * sizeof(Class)); objc_getClassList(classList, numberOfClasses); @@ -117,9 +171,9 @@ BOOL CBCCatalogIsDebugLeaf(Class aClass) { #pragma mark UIViewController instantiation -UIViewController *CBCViewControllerFromClass(Class aClass) { - if ([aClass respondsToSelector:@selector(catalogStoryboardName)]) { - NSString *storyboardName = [aClass catalogStoryboardName]; +UIViewController *CBCViewControllerFromClass(Class aClass, NSDictionary *metadata) { + if ([metadata objectForKey:CBCStoryboardName]) { + NSString *storyboardName = [metadata objectForKey:CBCStoryboardName]; NSBundle *bundle = [NSBundle bundleForClass:aClass]; UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:bundle]; NSCAssert(storyboard, @"expecting a storyboard to exist at %@", storyboardName); @@ -130,22 +184,6 @@ BOOL CBCCatalogIsDebugLeaf(Class aClass) { return [[aClass alloc] init]; } -NSString *CBCDescriptionFromClass(Class aClass) { - if ([aClass respondsToSelector:@selector(catalogDescription)]) { - NSString *catalogDescription = [aClass catalogDescription]; - return catalogDescription; - } - return nil; -} - -NSURL *CBCRelatedInfoFromClass(Class aClass) { - if ([aClass respondsToSelector:@selector(catalogRelatedInfo)]) { - NSURL *catalogRelatedInfo = [aClass catalogRelatedInfo]; - return catalogRelatedInfo; - } - return nil; -} - #pragma mark Fix View Debugging void CBCFixViewDebuggingIfNeeded(void) { From 56d39f33c29c46ba41f019ad4c3571765bdcda74 Mon Sep 17 00:00:00 2001 From: Yarden Eitan Date: Thu, 24 May 2018 14:58:39 -0400 Subject: [PATCH 2/5] Automatic changelog preparation for release. --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a4ac68..1862b59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# #develop# + + TODO: Enumerate changes. + + # 2.4.1 Add `exampleRelatedInfo` to the CBCNode header for external invocation. From bbad14c99675c48d38ad1684ec8001a946f13a01 Mon Sep 17 00:00:00 2001 From: Yarden Eitan Date: Thu, 24 May 2018 15:13:41 -0400 Subject: [PATCH 3/5] update changelog --- CHANGELOG.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1862b59..4325257 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,92 @@ -# #develop# +# 2.5.0 + +Thee is now a new `NSDictionary` property in `CBCNode` called metadata. It is meant to store all the information regarding an example +rather than using separate methods as previously done. With that said, we offer backwards compatibility and still allow the usage of methods to provide example information. + +Before: +``` ++ (NSArray *)catalogBreadcrumbs { + return @[ @"Activity Indicator", @"Activity Indicator" ]; +} + ++ (NSString *)catalogDescription { + return @"Activity Indicator is a visual indication of an app loading content. It can display how " + @"long an operation will take or visualize an unspecified wait time."; +} + ++ (BOOL)catalogIsPrimaryDemo { + return YES; +} + ++ (BOOL)catalogIsPresentable { + return YES; +} +``` + +After: +``` ++ (NSDictionary *)catalogMetadata { + return @{@"breadcrumbs": @[ @"Activity Indicator", @"Activity Indicator" ], + @"description": @"Activity Indicator is a visual indication of an app loading content. It can display how " + @"long an operation will take or visualize an unspecified wait time.", + @"primaryDemo": @YES, + @"presentable": @YES}; +} +``` - TODO: Enumerate changes. +## Source changes + + * [added a new metadata property that will hold all the key/values for that node. Also code refactoring (#27)](https://github.com/material-foundation/cocoapods-catalog-by-convention/commit/6c44e443e98bb87c955663c45c1245921338de1e) (Yarden Eitan) + +## API changes + +#### CBCBreadcrumbs + +*new* constant: `CBCBreadcrumbs` + +#### CBCNode + +*new* property: `metadata` in `CBCNode` + +*removed* property: `nodeDescription` in `CBCNode` + +*modified* method: `-exampleDescription` in `CBCNode` + +| Type of change: | Swift declaration | +|---|---| +| From: | `func exampleDescription() -> String` | +| To: | `func exampleDescription() -> String?` | + +*modified* method: `-exampleDescription` in `CBCNode` + +| Type of change: | Declaration | +|---|---| +| From: | `- (nonnull NSString *)exampleDescription;` | +| To: | `- (nullable NSString *)exampleDescription;` | + +#### CBCRelatedInfo + +*new* constant: `CBCRelatedInfo` + +#### CBCIsDebug + +*new* constant: `CBCIsDebug` + +#### CBCIsPresentable + +*new* constant: `CBCIsPresentable` + +#### CBCIsPrimaryDemo + +*new* constant: `CBCIsPrimaryDemo` + +#### CBCDescription + +*new* constant: `CBCDescription` + +#### CBCStoryboardName +*new* constant: `CBCStoryboardName` # 2.4.1 From 8285b6a093d75f37eeb807d8c48440deaf02eb7f Mon Sep 17 00:00:00 2001 From: Yarden Eitan Date: Thu, 24 May 2018 15:16:02 -0400 Subject: [PATCH 4/5] bump to 2.5.0 --- CatalogByConvention.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CatalogByConvention.podspec b/CatalogByConvention.podspec index ea31b9c..c8c6ee9 100644 --- a/CatalogByConvention.podspec +++ b/CatalogByConvention.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CatalogByConvention" - s.version = "2.4.1" + s.version = "2.5.0" s.authors = "Google Inc." s.summary = "Tools for building a Catalog by Convention." s.homepage = "https://github.com/material-foundation/cocoapods-catalog-by-convention" From d32136d73f7c91596d38bfbbf96924067fc71ce3 Mon Sep 17 00:00:00 2001 From: Yarden Eitan Date: Thu, 24 May 2018 15:29:42 -0400 Subject: [PATCH 5/5] typo update --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4325257..765549f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # 2.5.0 -Thee is now a new `NSDictionary` property in `CBCNode` called metadata. It is meant to store all the information regarding an example +There is now a new `NSDictionary` property in `CBCNode` called metadata. It is meant to store all the information regarding an example rather than using separate methods as previously done. With that said, we offer backwards compatibility and still allow the usage of methods to provide example information. Before: