Skip to content

Commit 2d45af3

Browse files
Paolo ArdiaPaolo Ardia
authored andcommitted
Merge branch 'feature/segue_generation' into develop
2 parents 94e99e4 + 0f50bcf commit 2d45af3

10 files changed

Lines changed: 402 additions & 7 deletions

File tree

ResourceObjC.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
06056E581ED7570500DFCF79 /* ImagesGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 06056E571ED7570500DFCF79 /* ImagesGenerator.m */; };
1313
0617A98A1ED9B46A007ECB92 /* StoryboardsGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0617A9891ED9B46A007ECB92 /* StoryboardsGenerator.m */; };
1414
0673D4E91EDCBFD900880207 /* GeneratedStructures.m in Sources */ = {isa = PBXBuildFile; fileRef = 0673D4E81EDCBFD900880207 /* GeneratedStructures.m */; };
15+
0673D4EC1EDE999600880207 /* SeguesGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0673D4EB1EDE999600880207 /* SeguesGenerator.m */; };
1516
06803EB11ED5BDEE0006208A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 06803EB01ED5BDEE0006208A /* main.m */; };
1617
06803EBA1ED5C53F0006208A /* ResourceFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = 06803EB91ED5C53F0006208A /* ResourceFinder.m */; };
1718
06803EC91ED5EC630006208A /* StringsGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 06803EC81ED5EC630006208A /* StringsGenerator.m */; };
@@ -46,6 +47,8 @@
4647
0617A9891ED9B46A007ECB92 /* StoryboardsGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StoryboardsGenerator.m; sourceTree = "<group>"; };
4748
0673D4E71EDCBFD900880207 /* GeneratedStructures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedStructures.h; sourceTree = "<group>"; };
4849
0673D4E81EDCBFD900880207 /* GeneratedStructures.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedStructures.m; sourceTree = "<group>"; };
50+
0673D4EA1EDE999600880207 /* SeguesGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SeguesGenerator.h; sourceTree = "<group>"; };
51+
0673D4EB1EDE999600880207 /* SeguesGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SeguesGenerator.m; sourceTree = "<group>"; };
4952
06803EAD1ED5BDED0006208A /* Robjc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Robjc; sourceTree = BUILT_PRODUCTS_DIR; };
5053
06803EB01ED5BDEE0006208A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
5154
06803EB81ED5C53F0006208A /* ResourceFinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourceFinder.h; sourceTree = "<group>"; };
@@ -127,6 +130,8 @@
127130
ADD731DF1ED8C8A2006B0E51 /* ThemesGenerator.m */,
128131
0617A9881ED9B46A007ECB92 /* StoryboardsGenerator.h */,
129132
0617A9891ED9B46A007ECB92 /* StoryboardsGenerator.m */,
133+
0673D4EA1EDE999600880207 /* SeguesGenerator.h */,
134+
0673D4EB1EDE999600880207 /* SeguesGenerator.m */,
130135
0673D4E71EDCBFD900880207 /* GeneratedStructures.h */,
131136
0673D4E81EDCBFD900880207 /* GeneratedStructures.m */,
132137
);
@@ -264,6 +269,7 @@
264269
06803EC91ED5EC630006208A /* StringsGenerator.m in Sources */,
265270
ADDA17981ED6E75700AA0C51 /* CommonUtils.m in Sources */,
266271
06056E581ED7570500DFCF79 /* ImagesGenerator.m in Sources */,
272+
0673D4EC1EDE999600880207 /* SeguesGenerator.m in Sources */,
267273
06803EBA1ED5C53F0006208A /* ResourceFinder.m in Sources */,
268274
AD8D6EDC1ED77538009CF948 /* FormattedStringParser.m in Sources */,
269275
ADD731E01ED8C8A2006B0E51 /* ThemesGenerator.m in Sources */,

ResourceObjC/Sources/GeneratedStructures.m

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,15 @@ - (NSString *)generateStructure
106106
}
107107
[completeSign appendFormat:@"- (%@)", self.returnType];
108108

109-
NSUInteger startIndex = 0;
109+
NSUInteger startIndex = -1;
110110
if (self.arguments.count > 0)
111111
{
112112
NSRange argRange = [self.signature rangeOfString:@":"];
113113
for (int i = 0; i < self.arguments.count; i++)
114114
{
115115
if (argRange.location != NSNotFound)
116116
{
117-
[completeSign appendString:[self.signature substringWithRange:NSMakeRange(startIndex, argRange.location - startIndex + 1)]];
117+
[completeSign appendString:[self.signature substringWithRange:NSMakeRange(startIndex + 1, argRange.location - startIndex)]];
118118

119119
[completeSign appendString:[self.arguments[i] generateStructure]];
120120
if (i < self.arguments.count-1)
@@ -123,7 +123,7 @@ - (NSString *)generateStructure
123123
}
124124
startIndex = argRange.location;
125125

126-
argRange = [self.signature rangeOfString:@":" options:NSLiteralSearch range:NSMakeRange(startIndex, argRange.location - startIndex) locale:nil];
126+
argRange = [self.signature rangeOfString:@":" options:0 range:NSMakeRange(startIndex + 1, self.signature.length - startIndex - 1) locale:nil];
127127
}
128128
}
129129
}
@@ -151,7 +151,8 @@ - (instancetype)initWithReturnType:(NSString *)type signature:(NSString *)signat
151151

152152
- (NSString *)generateStructure
153153
{
154-
NSMutableString* impl = [NSMutableString stringWithString:[super generateStructure]];
154+
NSMutableString* impl = [NSMutableString stringWithString:self.indent ? @"\n" : @""];
155+
[impl appendString:[super generateStructure]];
155156
[impl replaceOccurrencesOfString:@";" withString:@"" options:0 range:[impl rangeOfString:impl]];
156157
[impl appendFormat:@"%@{%@%@%@}%@", (self.indent ? @"\n":@" "), (self.indent ? @"\t":@" "), self.implementation, (self.indent ? @"\n":@" "), (self.indent ? @"\n":@"")];
157158
return [impl copy];
@@ -175,7 +176,7 @@ @implementation RClassMethodImplementation
175176
- (NSString *)generateStructure
176177
{
177178
NSMutableString *retval = [NSMutableString stringWithString:[super generateStructure]];
178-
[retval replaceCharactersInRange:NSMakeRange(0, 1) withString:@"+"];
179+
[retval replaceCharactersInRange:NSMakeRange(self.indent ? 1 : 0, 1) withString:@"+"];
179180
return [retval copy];
180181
}
181182

ResourceObjC/Sources/RGenerator.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#import "ImagesGenerator.h"
1919
#import "ThemesGenerator.h"
2020
#import "StoryboardsGenerator.h"
21+
#import "SeguesGenerator.h"
2122

2223
@implementation RGenerator
2324

@@ -55,6 +56,11 @@ - (BOOL)generateResourceFileWithError:(NSError *__autoreleasing *)error
5556
[generators addObject:[[StoryboardsGenerator alloc] initWithResourceFinder:self.finder]];
5657
}
5758

59+
if ([[Session shared] resourcesToGenerate] & ResourceTypeSegues)
60+
{
61+
[generators addObject:[[SeguesGenerator alloc] initWithResourceFinder:self.finder]];
62+
}
63+
5864
// shared instance implementation
5965
RClassMethodImplementation* shared = [[RClassMethodImplementation alloc] initWithReturnType:@"instancetype" signature:@"sharedInstance" implementation:R_SHARED_INSTANCE];
6066
shared.indent = YES;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2016 Sysdata Digital
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import "BaseGenerator.h"
16+
17+
@interface SeguesGenerator : BaseGenerator
18+
19+
@end
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// Copyright 2016 Sysdata Digital
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import "SeguesGenerator.h"
16+
#import <XMLDictionary/XMLDictionary.h>
17+
18+
@interface SegueResource : NSObject
19+
@property (nonatomic, strong) NSMutableArray* segues;
20+
@property (nonatomic, strong) NSString* sourceClassName;
21+
@property (nonatomic, readonly) NSString* sourceClassType;
22+
@property (nonatomic, readonly) NSString* methodName;
23+
@end
24+
25+
@implementation SegueResource
26+
- (instancetype)init
27+
{
28+
self = [super init];
29+
if (self)
30+
{
31+
self.segues = [NSMutableArray new];
32+
}
33+
return self;
34+
}
35+
36+
- (void)setSourceClassName:(NSString *)sourceClassName
37+
{
38+
if (![sourceClassName hasSuffix:@"Segues"])
39+
{
40+
sourceClassName = [sourceClassName stringByAppendingString:@"Segues"];
41+
}
42+
_sourceClassName = sourceClassName;
43+
}
44+
45+
- (NSString *)classType
46+
{
47+
return [self.sourceClassName stringByAppendingString:@"*"];
48+
}
49+
50+
- (NSString *)methodName
51+
{
52+
return [CommonUtils methodNameFromFilename:self.sourceClassName removingExtension:@"Segues"];
53+
}
54+
55+
@end
56+
57+
@interface SeguesGenerator ()
58+
@property (nonatomic, strong) NSMutableArray<SegueResource*>* resources;
59+
@end
60+
61+
@implementation SeguesGenerator
62+
63+
- (instancetype)initWithResourceFinder:(ResourceFinder *)finder
64+
{
65+
self = [super initWithResourceFinder:finder];
66+
if (self)
67+
{
68+
self.resources = [NSMutableArray new];
69+
}
70+
return self;
71+
}
72+
73+
- (NSString *)className
74+
{
75+
return @"Segues";
76+
}
77+
78+
- (NSString *)propertyName
79+
{
80+
return @"segue";
81+
}
82+
83+
- (SegueResource*)resourceForViewController:(NSString*)className
84+
{
85+
NSUInteger index = [self.resources indexOfObjectPassingTest:^BOOL(SegueResource * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
86+
if ([obj.className isEqualToString:className])
87+
{
88+
*stop = YES;
89+
return YES;
90+
}
91+
return NO;
92+
}];
93+
94+
if (index != NSNotFound)
95+
{
96+
return self.resources[index];
97+
}
98+
99+
SegueResource* res = [SegueResource new];
100+
res.sourceClassName = className;
101+
[self.resources addObject:res];
102+
return res;
103+
}
104+
105+
- (BOOL)generateResourceFileWithError:(NSError *__autoreleasing *)error
106+
{
107+
[self mapStoryboards];
108+
109+
if (![self writeInResourceFileWithError:error])
110+
{
111+
return NO;
112+
}
113+
114+
return YES;
115+
}
116+
117+
- (void)mapStoryboards
118+
{
119+
NSArray<NSURL*>* urls = [self.finder filesWithExtension:@"storyboard"];
120+
121+
for (NSURL* url in urls)
122+
{
123+
NSDictionary* storyboard = [NSDictionary dictionaryWithXMLFile:url.path];
124+
125+
if (storyboard)
126+
{
127+
id scenes = [storyboard.childNodes[@"scenes"] arrayValueForKeyPath:@"scene"];
128+
129+
for (NSDictionary* scene in scenes)
130+
{
131+
NSDictionary* objects = scene[@"objects"];
132+
NSDictionary* viewController = objects[@"viewController"];
133+
if ([viewController isKindOfClass:[NSDictionary class]])
134+
{
135+
NSString* sourceClass = viewController[@"_customClass"] ?: @"UIViewController";
136+
if ([sourceClass isKindOfClass:[NSString class]])
137+
{
138+
SegueResource* res = [self resourceForViewController:sourceClass];
139+
140+
NSArray* segues = [viewController.childNodes[@"connections"] arrayValueForKeyPath:@"segue"];
141+
142+
for (NSDictionary* segue in segues)
143+
{
144+
NSString* identifier = segue[@"_identifier"];
145+
146+
if ([identifier isKindOfClass:[NSString class]])
147+
{
148+
[res.segues addObject:identifier];
149+
}
150+
else if (!identifier)
151+
{
152+
[CommonUtils log:@"warning in %@ storyboard: segue without identifier in view controller %@", url.lastPathComponent, viewController[@"_customClass"]];
153+
}
154+
else
155+
{
156+
[CommonUtils log:@"invalid %@ storyboard structure: segue identifier is not a string", url.lastPathComponent];
157+
}
158+
}
159+
}
160+
else
161+
{
162+
[CommonUtils log:@"invalid %@ storyboard structure: 'storyboardIdentifier' is not a string", url.lastPathComponent];
163+
}
164+
}
165+
else if (viewController != nil)
166+
{
167+
[CommonUtils log:@"invalid %@ storyboard structure: 'viewController' is not a dictionary", url.lastPathComponent];
168+
}
169+
}
170+
}
171+
}
172+
}
173+
174+
- (BOOL)writeInResourceFileWithError:(NSError *__autoreleasing *)error
175+
{
176+
// generate RSegue class
177+
RClass *clazz = [[RClass alloc] initWithName:@"RSegue"];
178+
[self.otherClasses addObject:clazz];
179+
180+
// generate methods declaration for RSegue
181+
RProperty* identifierProp = [[RProperty alloc] initWithClass:@"NSString*" name:@"identifier"];
182+
[clazz.interface.properties addObject:identifierProp];
183+
RMethodSignature* performMethod = [[RMethodSignature alloc] initWithReturnType:@"void" signature:@"performWithSource:sender:"];
184+
RMethodArgument* sourceArg = [[RMethodArgument alloc] initWithType:@"UIViewController*" name:@"sourceViewController"];
185+
RMethodArgument* senderArg = [[RMethodArgument alloc] initWithType:@"id" name:@"sender"];
186+
[performMethod.arguments addObjectsFromArray:@[sourceArg, senderArg]];
187+
[clazz.interface.methods addObject:performMethod];
188+
189+
// generate methods implementation for RSegue
190+
NSString* impl = @"[sourceViewController performSegueWithIdentifier:self.identifier sender:sender];";
191+
RMethodImplementation* performImpl = [[RMethodImplementation alloc] initWithReturnType:performMethod.returnType signature:performMethod.signature implementation:impl];
192+
[performImpl.arguments addObjectsFromArray:performMethod.arguments];
193+
[clazz.implementation.methods addObject:performImpl];
194+
195+
for (SegueResource* res in self.resources)
196+
{
197+
if (res.segues.count > 0)
198+
{
199+
// generates Segues interface methods, one for every view controller
200+
RMethodSignature* method = [[RMethodSignature alloc] initWithReturnType:res.classType signature:res.methodName];
201+
[self.clazz.interface.methods addObject:method];
202+
203+
// segue resource class
204+
RClass* clazz = [[RClass alloc] initWithName:res.sourceClassName];
205+
[self.otherClasses addObject:clazz];
206+
207+
// property declaration in extension and lazy getter implementation for every clazz
208+
NSString* codableKey = [CommonUtils codableNameFromString:res.methodName];
209+
210+
RProperty* property = [[RProperty alloc] initWithClass:res.classType name:codableKey];
211+
[self.clazz.extension.properties addObject:property];
212+
213+
RLazyGetterImplementation *lazy = [[RLazyGetterImplementation alloc] initReturnType:res.sourceClassName name:codableKey];
214+
[self.clazz.implementation.lazyGetters addObject:lazy];
215+
216+
// sort segues in alphabetic order
217+
NSArray* segues = [res.segues sortedArrayUsingSelector:@selector(compare:)];
218+
for (NSString* segue in segues)
219+
{
220+
codableKey = [CommonUtils codableNameFromString:segue];
221+
222+
// method declaration for segue
223+
method = [[RMethodSignature alloc] initWithReturnType:@"RSegue*" signature:codableKey];
224+
[clazz.interface.methods addObject:method];
225+
226+
// private property for segue
227+
RProperty* prop = [[RProperty alloc] initWithClass:@"RSegue*" name:codableKey];
228+
[clazz.extension.properties addObject:prop];
229+
230+
// lazy property getter
231+
NSMutableString* implString = [NSMutableString new];
232+
[implString appendFormat:@"\n"];
233+
[implString appendFormat:@"\tif (!_%@)\n", codableKey];
234+
[implString appendString:@"\t{\n"];
235+
[implString appendFormat:@"\t\t_%@ = [RSegue new];\n", codableKey];
236+
[implString appendFormat:@"\t\t_%@.identifier = @\"%@\";\n", codableKey, segue];
237+
[implString appendString:@"\t}\n"];
238+
[implString appendFormat:@"\treturn _%@;", codableKey];
239+
RMethodImplementation* impl = [[RMethodImplementation alloc] initWithReturnType:@"RSegue*" signature:codableKey implementation:implString];
240+
impl.indent = YES;
241+
[clazz.implementation.methods addObject:impl];
242+
}
243+
}
244+
}
245+
246+
return [self writeStringInRFilesWithError:error];
247+
}
248+
249+
@end

ResourceObjC/Sources/Session.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ typedef NS_OPTIONS(NSUInteger, ResourceType) {
1818
ResourceTypeStrings = 1 << 0,
1919
ResourceTypeImages = 1 << 1,
2020
ResourceTypeThemes = 1 << 2,
21-
ResourceTypeStoryboards = 1 << 3
21+
ResourceTypeStoryboards = 1 << 3,
22+
ResourceTypeSegues = 1 << 4
2223
};
2324

2425
@interface Session : NSObject

ResourceObjC/Sources/Session.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ - (NSURL *)baseURL
170170

171171
- (ResourceType)resourcesToGenerate
172172
{
173-
ResourceType result = ResourceTypeStrings | ResourceTypeImages | ResourceTypeStoryboards;
173+
ResourceType result = ResourceTypeStrings | ResourceTypeImages | ResourceTypeStoryboards | ResourceTypeSegues;
174174
if (self.isSysdataVersion)
175175
{
176176
result = result | ResourceTypeThemes;

0 commit comments

Comments
 (0)