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 generation of Table and Collection cell reuseIdentifiers #134

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
152fb43
Update #66 to latest master
phatblat Jun 5, 2016
852dbb7
Add .xib files containing only tableview and tableviewcell views
phatblat Jun 5, 2016
2bf40f5
Rename StoryboardCell and StoryboardCellType
phatblat Jun 5, 2016
a28e3d2
Add build rule to copy .xib files as-is into test bundle
phatblat Jun 5, 2016
2e8157a
Add custom class to table cell in .xib
phatblat Jun 5, 2016
ef09758
Remove TableView.xib
phatblat Jun 5, 2016
8874e1a
Add .xib test fixtures for collection view cells
phatblat Jun 5, 2016
a4a6ad2
Add tableview extension for dequeing cells using StoryboardCellType
phatblat Jun 5, 2016
f2e3858
Add initial table cell XIB parsing
phatblat Jun 5, 2016
fbaf32a
Remove CollectionReusableView.xib fixture
phatblat Jun 6, 2016
3c6a296
Remove readyForPrototypes
phatblat Jun 6, 2016
b42bb4c
Change table cell reuseIdentifier in xib
phatblat Jun 6, 2016
062047e
Rename Xibs-TableViewCell-Defaults.swift.out
phatblat Jun 6, 2016
5bc2573
Add CollectionViewCell.xib test
phatblat Jun 6, 2016
a7a354e
Add xib file parsing when given a directory path
phatblat Jun 6, 2016
e30890a
Add cellEnumName option to storyboards command
phatblat Jun 6, 2016
d34e636
Add standalone table and collection view cells
phatblat Jun 6, 2016
3c0774c
Change prefix to Xib
phatblat Jun 6, 2016
9721c9d
Add ReuseID to end of reuseIdentifiers
phatblat Jun 6, 2016
ad8079a
Fix parsing customClass attribute on collectionViewCell
phatblat Jun 6, 2016
1177964
Add extensions for all cells with customClass specifying their reuseI…
phatblat Jun 6, 2016
fb7a821
Handle path to .xib file
phatblat Jun 6, 2016
af6f4bf
Change reuseIdentifier to computed property in cell class extensions
phatblat Jun 6, 2016
6eba75c
Merge branch 'master' into table-collection-cells
phatblat Jun 9, 2016
3459468
Update Xib template expected output
phatblat Jun 9, 2016
ea89054
Merge branch 'gotta-return' into table-collection-cells
phatblat Jun 9, 2016
acf2eb1
Update test fixtures for change in #138
phatblat Jun 9, 2016
45355ae
Make reuseIdentifier public in default template
phatblat Jun 11, 2016
feb1794
Merge branch 'master' into table-collection-cells
phatblat Jun 11, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion GenumKit/Parsers/StoryboardParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ public final class StoryboardParser {
let customClass: String?
}

struct Cell {
let reuseID: String
let customClass: String?
}

var storyboardsScenes = [String: Set<Scene>]()
var storyboardsSegues = [String: Set<Segue>]()
var storyboardsCells = [String: Set<Cell>]()

public init() {}

private class ParserDelegate: NSObject, NSXMLParserDelegate {
var scenes = Set<Scene>()
var segues = Set<Segue>()
var cells = Set<Cell>()
var inScene = false
var readyForFirstObject = false
var readyForConnections = false
Expand All @@ -45,13 +52,23 @@ public final class StoryboardParser {
scenes.insert(Scene(storyboardID: storyboardID, tag: tag, customClass: customClass))
}
readyForFirstObject = false
case "tableViewCell":
if let reuseID = attributeDict["reuseIdentifier"] {
let customClass = attributeDict["customClass"]
cells.insert(Cell(reuseID: reuseID, customClass: customClass))
}
case "connections":
readyForConnections = true
case "segue" where readyForConnections:
if let segueID = attributeDict["identifier"] {
let customClass = attributeDict["customClass"]
segues.insert(Segue(segueID: segueID, customClass: customClass))
}
case "collectionViewCell":
if let reuseID = attributeDict["reuseIdentifier"] {
let customClass = attributeDict["customClass"]
cells.insert(Cell(reuseID: reuseID, customClass: customClass))
}
default:
break
}
Expand Down Expand Up @@ -82,12 +99,13 @@ public final class StoryboardParser {
let storyboardName = ((path as NSString).lastPathComponent as NSString).stringByDeletingPathExtension
storyboardsScenes[storyboardName] = delegate.scenes
storyboardsSegues[storyboardName] = delegate.segues
storyboardsCells[storyboardName] = delegate.cells
}

public func parseDirectory(path: String) {
if let dirEnum = NSFileManager.defaultManager().enumeratorAtPath(path) {
while let subPath = dirEnum.nextObject() as? NSString {
if subPath.pathExtension == "storyboard" {
if ["storyboard", "xib"].contains(subPath.pathExtension) {
self.addStoryboardAtPath((path as NSString).stringByAppendingPathComponent(subPath as String))
}
}
Expand Down Expand Up @@ -116,3 +134,14 @@ extension StoryboardParser.Segue: Hashable {
return "\(segueID);\(customClass)".hashValue
}
}

extension StoryboardParser.Cell: Equatable { }
func == (lhs: StoryboardParser.Cell, rhs: StoryboardParser.Cell) -> Bool {
return lhs.reuseID == rhs.reuseID && lhs.customClass == rhs.customClass
}

extension StoryboardParser.Cell: Hashable {
var hashValue: Int {
return "\(reuseID);\(customClass)".hashValue
}
}
16 changes: 15 additions & 1 deletion GenumKit/Stencil/Contexts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ extension AssetsCatalogParser {

- `sceneEnumName`: `String`
- `segueEnumName`: `String`
- `cellEnumName`: `String`
- `storyboards`: `Array` of:
- `name`: `String`
- `scenes`: `Array` (absent if empty)
Expand All @@ -73,7 +74,8 @@ extension AssetsCatalogParser {
*/
extension StoryboardParser {
public func stencilContext(sceneEnumName sceneEnumName: String = "StoryboardScene",
segueEnumName: String = "StoryboardSegue") -> Context {
segueEnumName: String = "StoryboardSegue",
cellEnumName: String = "StoryboardCell") -> Context {
let storyboards = Set(storyboardsScenes.keys).union(storyboardsSegues.keys).sort(<)
let storyboardsMap = storyboards.map { (storyboardName: String) -> [String:AnyObject] in
var sbMap: [String:AnyObject] = ["name": storyboardName]
Expand All @@ -100,12 +102,24 @@ extension StoryboardParser {
["identifier": segue.segueID, "class": segue.customClass ?? "UIStoryboardSegue"]
}
}
if let cells = storyboardsCells[storyboardName] {
sbMap["cells"] = cells
.sort({$0.reuseID < $1.reuseID})
.map { (cell: Cell) -> [String:String] in
if let customClass = cell.customClass {
return ["identifier": cell.reuseID, "class": customClass]
} else {
return ["identifier": cell.reuseID]
}
}
}
return sbMap
}
return Context(
dictionary: [
"sceneEnumName": sceneEnumName,
"segueEnumName": segueEnumName,
"cellEnumName": cellEnumName,
"storyboards": storyboardsMap
]
)
Expand Down
30 changes: 30 additions & 0 deletions SwiftGen.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@
EFFE62F81C27081A00FE9783 /* Dependency.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EFFE62F71C27081A00FE9783 /* Dependency.storyboard */; };
F24DA0455755C4EC4F1DDD96 /* Pods_swiftgen.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC28A16A3F1852C832A3B47E /* Pods_swiftgen.framework */; };
F4705617D891407B43B021A3 /* Pods_UnitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E15CBE3901E2C7C3240E93 /* Pods_UnitTests.framework */; };
F87FF1AB1D0460D60001ACED /* TableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F87FF1AA1D0460D60001ACED /* TableViewCell.xib */; };
F87FF1AF1D047D0D0001ACED /* CollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F87FF1AE1D047D0D0001ACED /* CollectionViewCell.xib */; };
F87FF1B31D04A2090001ACED /* Xibs-TableViewCell-Defaults.swift.out in Resources */ = {isa = PBXBuildFile; fileRef = F87FF1B21D04A2090001ACED /* Xibs-TableViewCell-Defaults.swift.out */; };
F87FF1B61D0516840001ACED /* Xibs-CollectionViewCell-Defaults.swift.out in Resources */ = {isa = PBXBuildFile; fileRef = F87FF1B51D0516840001ACED /* Xibs-CollectionViewCell-Defaults.swift.out */; };
/* End PBXBuildFile section */

/* Begin PBXBuildRule section */
Expand Down Expand Up @@ -118,6 +122,16 @@
);
script = "";
};
F87FF1AD1D0479520001ACED /* PBXBuildRule */ = {
isa = PBXBuildRule;
compilerSpec = com.apple.compilers.pbxcp;
filePatterns = "*.xib";
fileType = pattern.proxy;
isEditable = 1;
outputFiles = (
);
script = "# ibtool\n";
};
/* End PBXBuildRule section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -220,6 +234,10 @@
E5E15CBE3901E2C7C3240E93 /* Pods_UnitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_UnitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
EFFE62F31C27073700FE9783 /* Placeholder.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Placeholder.storyboard; sourceTree = "<group>"; };
EFFE62F71C27081A00FE9783 /* Dependency.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Dependency.storyboard; sourceTree = "<group>"; };
F87FF1AA1D0460D60001ACED /* TableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TableViewCell.xib; sourceTree = "<group>"; };
F87FF1AE1D047D0D0001ACED /* CollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CollectionViewCell.xib; sourceTree = "<group>"; };
F87FF1B21D04A2090001ACED /* Xibs-TableViewCell-Defaults.swift.out */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Xibs-TableViewCell-Defaults.swift.out"; sourceTree = "<group>"; };
F87FF1B51D0516840001ACED /* Xibs-CollectionViewCell-Defaults.swift.out */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Xibs-CollectionViewCell-Defaults.swift.out"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -319,6 +337,8 @@
09030C241B6D48FC00275BF5 /* Localizable.strings */,
09EB0A301BDC8F4100B2CF79 /* LocUTF8.strings */,
47CB53291CFC0B2100AA19C0 /* Fonts */,
F87FF1AE1D047D0D0001ACED /* CollectionViewCell.xib */,
F87FF1AA1D0460D60001ACED /* TableViewCell.xib */,
);
path = fixtures;
sourceTree = "<group>";
Expand Down Expand Up @@ -402,6 +422,8 @@
09EB0A241BDC60F000B2CF79 /* Storyboards-All-CustomName.swift.out */,
47CB533C1CFC170E00AA19C0 /* Storyboards-Anonymous-Swift3.swift.out */,
47CB533D1CFC170E00AA19C0 /* Storyboards-Wizard-Swift3.swift.out */,
F87FF1B51D0516840001ACED /* Xibs-CollectionViewCell-Defaults.swift.out */,
F87FF1B21D04A2090001ACED /* Xibs-TableViewCell-Defaults.swift.out */,
);
name = Storyboards;
sourceTree = "<group>";
Expand Down Expand Up @@ -505,6 +527,7 @@
09A87B721BCCA60900D9B9F5 /* PBXBuildRule */,
09A87B6B1BCCA52400D9B9F5 /* PBXBuildRule */,
09A87B6A1BCCA50000D9B9F5 /* PBXBuildRule */,
F87FF1AD1D0479520001ACED /* PBXBuildRule */,
);
dependencies = (
);
Expand Down Expand Up @@ -563,18 +586,22 @@
470A14EB1BEA6DAB0052EAC8 /* Storyboards-Anonymous-Default.swift.out in Resources */,
47CB53371CFC0B2100AA19C0 /* SFNSText-Heavy.otf in Resources */,
C24AFD0E1C924A29005FE3EF /* Fonts-Dir-CustomName.swift.out in Resources */,
C24AFD0E1C924A29005FE3EF /* Fonts-File-CustomName.swift.out in Resources */,
F87FF1AB1D0460D60001ACED /* TableViewCell.xib in Resources */,
C2265B591C8907CA00CA5C4F /* colors.json in Resources */,
C25A54341CEE7CEB00401BE5 /* Colors-Txt-File-CustomName.swift.out in Resources */,
09A87B671BCCA4E600D9B9F5 /* colors.txt in Resources */,
091150361BD3274E00EBC803 /* images-default.stencil in Resources */,
47CB533F1CFC170E00AA19C0 /* Storyboards-Wizard-Swift3.swift.out in Resources */,
470A14E71BEA691E0052EAC8 /* images-allvalues.stencil in Resources */,
F87FF1B31D04A2090001ACED /* Xibs-TableViewCell-Defaults.swift.out in Resources */,
09EB0A1A1BDC3AC400B2CF79 /* strings-default.stencil in Resources */,
09EB0A291BDC60F000B2CF79 /* Colors-List-Default.swift.out in Resources */,
09A87B701BCCA5F600D9B9F5 /* Strings-Lines-Default.swift.out in Resources */,
47CB53331CFC0B2100AA19C0 /* SFNSDisplay-Bold.otf in Resources */,
4761E0361BDE753000B9B05F /* Colors-Empty.swift.out in Resources */,
EFFE62F81C27081A00FE9783 /* Dependency.storyboard in Resources */,
F87FF1AF1D047D0D0001ACED /* CollectionViewCell.xib in Resources */,
09A87B5E1BCCA4D100D9B9F5 /* Message.storyboard in Resources */,
09A87B641BCCA4DC00D9B9F5 /* Images.xcassets in Resources */,
C24AFD041C92370D005FE3EF /* fonts-default.stencil in Resources */,
Expand All @@ -587,6 +614,9 @@
0911503B1BD3ECAE00EBC803 /* colors-default.stencil in Resources */,
09A87B6D1BCCA5F600D9B9F5 /* Strings-File-Default.swift.out in Resources */,
09EB0A2A1BDC60F000B2CF79 /* Images-Entries-Default.swift.out in Resources */,
F87FF1B61D0516840001ACED /* Xibs-CollectionViewCell-Defaults.swift.out in Resources */,
09A87B6D1BCCA5F600D9B9F5 /* Strings-File-Defaults.swift.out in Resources */,
09EB0A2A1BDC60F000B2CF79 /* Images-Entries-Defaults.swift.out in Resources */,
09A87B6E1BCCA5F600D9B9F5 /* Strings-File-CustomName.swift.out in Resources */,
4761E0371BDE753000B9B05F /* Images-Empty.swift.out in Resources */,
4761E0391BDE753000B9B05F /* Strings-Empty.swift.out in Resources */,
Expand Down
26 changes: 25 additions & 1 deletion UnitTests/TestSuites/StoryboardTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class StoryboardTests: XCTestCase {
parser.parseDirectory(self.fixturesDir)

let template = GenumTemplate(templateString: fixtureString("storyboards-default.stencil"))
let ctx = parser.stencilContext(sceneEnumName: "XCTStoryboardsScene", segueEnumName: "XCTStoryboardsSegue")
let ctx = parser.stencilContext(sceneEnumName: "XCTStoryboardsScene", segueEnumName: "XCTStoryboardsSegue", cellEnumName: "XCTStoryboardsCell")
let result = try! template.render(ctx)

let expected = self.fixtureString("Storyboards-All-CustomName.swift.out")
Expand Down Expand Up @@ -103,4 +103,28 @@ class StoryboardTests: XCTestCase {
let expected = self.fixtureString("Storyboards-Anonymous-Swift3.swift.out")
XCTDiffStrings(result, expected)
}

func testTableViewCellXibWithDefaults() {
let parser = StoryboardParser()
parser.addStoryboardAtPath(self.fixturePath("TableViewCell.xib"))

let template = GenumTemplate(templateString: fixtureString("storyboards-default.stencil"))
let ctx = parser.stencilContext()
let result = try! template.render(ctx)

let expected = self.fixtureString("Xibs-TableViewCell-Defaults.swift.out")
XCTDiffStrings(result, expected)
}

func testCollectionViewCellXibWithDefaults() {
let parser = StoryboardParser()
parser.addStoryboardAtPath(self.fixturePath("CollectionViewCell.xib"))

let template = GenumTemplate(templateString: fixtureString("storyboards-default.stencil"))
let ctx = parser.stencilContext()
let result = try! template.render(ctx)

let expected = self.fixtureString("Xibs-CollectionViewCell-Defaults.swift.out")
XCTDiffStrings(result, expected)
}
}
49 changes: 49 additions & 0 deletions UnitTests/expected/Storyboards-All-CustomName.swift.out
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,35 @@ extension StoryboardSceneType where Self: RawRepresentable, Self.RawValue == Str
}

protocol StoryboardSegueType: RawRepresentable { }
protocol StoryboardCellType: RawRepresentable { }

extension UIViewController {
func performSegue<S: StoryboardSegueType where S.RawValue == String>(segue: S, sender: AnyObject? = nil) {
performSegueWithIdentifier(segue.rawValue, sender: sender)
}
}

extension UITableView {
func dequeueReusableCellWithIdentifier<C: StoryboardCellType where C.RawValue == String>(identifier: C,
forIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return dequeueReusableCellWithIdentifier(identifier.rawValue, forIndexPath: indexPath)
}
}

extension UICollectionView {
func dequeueReusableCellWithReuseIdentifier<C: StoryboardCellType where C.RawValue == String>(identifier: C,
forIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return dequeueReusableCellWithReuseIdentifier(identifier.rawValue, forIndexPath: indexPath)
}
}

struct XCTStoryboardsScene {
enum Anonymous: StoryboardSceneType {
static let storyboardName = "Anonymous"
}
enum CollectionViewCell: StoryboardSceneType {
static let storyboardName = "CollectionViewCell"
}
enum Dependency: String, StoryboardSceneType {
static let storyboardName = "Dependency"

Expand Down Expand Up @@ -96,6 +114,9 @@ struct XCTStoryboardsScene {
return vc
}
}
enum TableViewCell: StoryboardSceneType {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Feels strange to conform a cell to some protocol named "…SceneType". Maybe it's time to rename that protocol StoryboardBasedType instead of StoryboardSceneType ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, it is a little weird since XIBs don't have "scenes". I think in this case the TableViewCell "scene" should just be suppressed because you can't instantiateInitialViewController from a XIB.

static let storyboardName = "TableViewCell"
}
enum Wizard: String, StoryboardSceneType {
static let storyboardName = "Wizard"

Expand Down Expand Up @@ -140,3 +161,31 @@ struct XCTStoryboardsSegue {
case ShowPassword = "ShowPassword"
}
}

extension XibCollectionViewCell { public static var reuseIdentifier: String { return "XibCollectionViewCellReuseID" } }
extension CustomCollectionViewCell { public static var reuseIdentifier: String { return "CustomCollectionViewCellReuseID" } }
extension CustomMessageCell { public static var reuseIdentifier: String { return "CustomMessageCellReuseID" } }
extension StandaloneCollectionViewCell { public static var reuseIdentifier: String { return "StandaloneCollectionViewCellReuseID" } }
extension StandaloneTableViewCell { public static var reuseIdentifier: String { return "StandaloneTableViewCellReuseID" } }
extension XibTableViewCell { public static var reuseIdentifier: String { return "XibTableViewCellReuseID" } }

struct XCTStoryboardsCell {
enum Anonymous: String, StoryboardCellType {
case ReuseCellID = "ReuseCellID"
}
enum CollectionViewCell: String, StoryboardCellType {
case XibCollectionViewCellReuseID = "XibCollectionViewCellReuseID"
Copy link
Member Author

@phatblat phatblat Jun 9, 2016

Choose a reason for hiding this comment

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

I think calling this CollectionViewCell a StoryboardCellType is the weird case as it was found in a XIB. Perhaps a XibCellType that simply extends StoryboardCellType?

}
enum Message: String, StoryboardCellType {
case CustomCollectionViewCellReuseID = "CustomCollectionViewCellReuseID"
case CustomMessageCellReuseID = "CustomMessageCellReuseID"
case StandaloneCollectionViewCellReuseID = "StandaloneCollectionViewCellReuseID"
case StandaloneTableViewCellReuseID = "StandaloneTableViewCellReuseID"
}
enum TableViewCell: String, StoryboardCellType {
case XibTableViewCellReuseID = "XibTableViewCellReuseID"
}
enum Wizard: String, StoryboardCellType {
case PrefCell = "PrefCell"
}
}
Loading