diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj index 35e4b72..c0b888d 100644 --- a/Pods/Pods.xcodeproj/project.pbxproj +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -3,12 +3,17 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 033700BD14168E5C5614FDB4DF37B748 /* StringsFileParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35F5C044723C7653D18C10BF6AAEF8D4 /* StringsFileParser.swift */; }; 040B29D0120B5B450B98FC67FEC958B8 /* PathKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8DA196CD2A6D1E393506CF19AB5B6C09 /* PathKit-dummy.m */; }; + 0726EEE81E7D593E0035922E /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0726EEE31E7D593E0035922E /* Protocols.swift */; }; + 0726EEE91E7D593E0035922E /* StoryboardParser.Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0726EEE41E7D593E0035922E /* StoryboardParser.Cell.swift */; }; + 0726EEEA1E7D593E0035922E /* StoryboardParser.Enums.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0726EEE51E7D593E0035922E /* StoryboardParser.Enums.swift */; }; + 0726EEEB1E7D593E0035922E /* StoryboardParser.Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0726EEE61E7D593E0035922E /* StoryboardParser.Scene.swift */; }; + 0726EEEC1E7D593E0035922E /* StoryboardParser.Segue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0726EEE71E7D593E0035922E /* StoryboardParser.Segue.swift */; }; 089D6407EB1EEFC90043E9E54AD49B20 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5217ABF3BDB5EC6A276AF9B64AA38C5 /* Cocoa.framework */; }; 0BE4A6E9E72DEA5E50CCCD991721D814 /* ColorsContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54477499963F9BB5465900CFB5D3EABD /* ColorsContext.swift */; }; 1DBE65BBA7CC2D0C93544B240F73D17E /* StoryboardsContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B5D27F549BC27FA003F01438250995 /* StoryboardsContext.swift */; }; @@ -58,6 +63,11 @@ /* Begin PBXFileReference section */ 06E5680F77898FA7B456DBF80297AB23 /* Pods-Tests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Tests-frameworks.sh"; sourceTree = ""; }; + 0726EEE31E7D593E0035922E /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = ""; }; + 0726EEE41E7D593E0035922E /* StoryboardParser.Cell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardParser.Cell.swift; sourceTree = ""; }; + 0726EEE51E7D593E0035922E /* StoryboardParser.Enums.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardParser.Enums.swift; sourceTree = ""; }; + 0726EEE61E7D593E0035922E /* StoryboardParser.Scene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardParser.Scene.swift; sourceTree = ""; }; + 0726EEE71E7D593E0035922E /* StoryboardParser.Segue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardParser.Segue.swift; sourceTree = ""; }; 10977DE0E866EF9C79EEC597CBA9E4C3 /* AssetsCatalogParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AssetsCatalogParser.swift; sourceTree = ""; }; 1143DC189DEAEC4A1278249254968FB1 /* Pods-Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Tests.debug.xcconfig"; sourceTree = ""; }; 1CF8AF2446D00EFF6BC384F0188571B7 /* PathKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PathKit-prefix.pch"; sourceTree = ""; }; @@ -66,13 +76,13 @@ 2E6EDE0590FA1A030C93F83C2EEFC65C /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Tests.release.xcconfig"; sourceTree = ""; }; 35F5C044723C7653D18C10BF6AAEF8D4 /* StringsFileParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StringsFileParser.swift; sourceTree = ""; }; 394708266BE40343609A83EF49CA09CB /* PathKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PathKit.swift; path = Sources/PathKit.swift; sourceTree = ""; }; - 4072D484B16EEBD81EF9991C8ED66ADD /* PathKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = PathKit.modulemap; sourceTree = ""; }; + 4072D484B16EEBD81EF9991C8ED66ADD /* PathKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = PathKit.modulemap; sourceTree = ""; }; 4073060E93266F46D895562DC8E1B5BB /* SwiftGenKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftGenKit-prefix.pch"; sourceTree = ""; }; 4309AA7478DD6B254C478AE5335748BA /* Pods-Tests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Tests-acknowledgements.plist"; sourceTree = ""; }; - 4F35C03EE539651D8168B79F18A1DF5B /* Pods-Tests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = "Pods-Tests.modulemap"; sourceTree = ""; }; + 4F35C03EE539651D8168B79F18A1DF5B /* Pods-Tests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = "Pods-Tests.modulemap"; sourceTree = ""; }; 52E08CA96B2F20724148FF5361AD7206 /* Pods-Tests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Tests-umbrella.h"; sourceTree = ""; }; 54477499963F9BB5465900CFB5D3EABD /* ColorsContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ColorsContext.swift; sourceTree = ""; }; - 57BEF236ED0935C9190818809B5658A0 /* PathKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = PathKit.framework; path = PathKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 57BEF236ED0935C9190818809B5658A0 /* PathKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PathKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5E95D3A9D0AA426730ABADF589227018 /* SwiftGenKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftGenKit-dummy.m"; sourceTree = ""; }; 60889E57CE4B4802D50272BE5861F810 /* StringsContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StringsContext.swift; sourceTree = ""; }; 61B5D27F549BC27FA003F01438250995 /* StoryboardsContext.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StoryboardsContext.swift; sourceTree = ""; }; @@ -83,9 +93,9 @@ 7A47779BD13411D93D59545E2E320338 /* Command.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; 7D849BEE3993512EB3DC1DB84A51D5FB /* SwiftGenKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftGenKit.xcconfig; sourceTree = ""; }; 8DA196CD2A6D1E393506CF19AB5B6C09 /* PathKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "PathKit-dummy.m"; sourceTree = ""; }; - 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; AF964CF5CB6052CB65EB873ADC4C5908 /* ColorsFileParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ColorsFileParser.swift; sourceTree = ""; }; - B6D0D3405A260E50A632D90481AD939E /* Pods_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Tests.framework; path = "Pods-Tests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + B6D0D3405A260E50A632D90481AD939E /* Pods_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BB116B04D23E71A2DEB352995B9A4043 /* PathKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PathKit.xcconfig; sourceTree = ""; }; BFE531A4BA3F8D5F7B6C9BD6D217DA29 /* Pods-Tests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Tests-acknowledgements.markdown"; sourceTree = ""; }; C0ABA9994C05CE2F400945671B7038BA /* Pods-Tests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Tests-dummy.m"; sourceTree = ""; }; @@ -94,10 +104,10 @@ D3AE389C53E9B01E985C9E18B23C4655 /* StoryboardParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StoryboardParser.swift; sourceTree = ""; }; D9F3535A5FE4365AC94D730F3917F739 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E3462B237C0B07B0193729BBED188874 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - E54EE79893AE60BDD4725EE82D2CD3CD /* SwiftGenKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; path = SwiftGenKit.modulemap; sourceTree = ""; }; + E54EE79893AE60BDD4725EE82D2CD3CD /* SwiftGenKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = SwiftGenKit.modulemap; sourceTree = ""; }; EC112BC570440F79F5D325EE264243AD /* SwiftGenKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftGenKit-umbrella.h"; sourceTree = ""; }; F5217ABF3BDB5EC6A276AF9B64AA38C5 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; - FBC70719DFB58EAECC4DCBA06C2586DF /* SwiftGenKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftGenKit.framework; path = SwiftGenKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FBC70719DFB58EAECC4DCBA06C2586DF /* SwiftGenKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftGenKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -138,6 +148,18 @@ name = "Targets Support Files"; sourceTree = ""; }; + 0726EEE21E7D593E0035922E /* StoryboardParserModel */ = { + isa = PBXGroup; + children = ( + 0726EEE31E7D593E0035922E /* Protocols.swift */, + 0726EEE51E7D593E0035922E /* StoryboardParser.Enums.swift */, + 0726EEE61E7D593E0035922E /* StoryboardParser.Scene.swift */, + 0726EEE71E7D593E0035922E /* StoryboardParser.Segue.swift */, + 0726EEE41E7D593E0035922E /* StoryboardParser.Cell.swift */, + ); + path = StoryboardParserModel; + sourceTree = ""; + }; 33D60351AC18B2DA603A389F697B9513 /* Support Files */ = { isa = PBXGroup; children = ( @@ -159,9 +181,9 @@ AF964CF5CB6052CB65EB873ADC4C5908 /* ColorsFileParser.swift */, 6D099D9232A07F0FE8E90ADF80821C6B /* FontsFileParser.swift */, D3AE389C53E9B01E985C9E18B23C4655 /* StoryboardParser.swift */, + 0726EEE21E7D593E0035922E /* StoryboardParserModel */, 35F5C044723C7653D18C10BF6AAEF8D4 /* StringsFileParser.swift */, ); - name = Parsers; path = Parsers; sourceTree = ""; }; @@ -252,7 +274,6 @@ 394708266BE40343609A83EF49CA09CB /* PathKit.swift */, 33D60351AC18B2DA603A389F697B9513 /* Support Files */, ); - name = PathKit; path = PathKit; sourceTree = ""; }; @@ -263,7 +284,6 @@ E007FD1113D89B52054CBEF4E7D84853 /* Stencil */, F358D0378A0787E39C037B62585E469F /* Utils */, ); - name = Sources; path = Sources; sourceTree = ""; }; @@ -284,7 +304,6 @@ 61B5D27F549BC27FA003F01438250995 /* StoryboardsContext.swift */, 60889E57CE4B4802D50272BE5861F810 /* StringsContext.swift */, ); - name = Stencil; path = Stencil; sourceTree = ""; }; @@ -293,7 +312,6 @@ children = ( 7A47779BD13411D93D59545E2E320338 /* Command.swift */, ); - name = Utils; path = Utils; sourceTree = ""; }; @@ -441,8 +459,13 @@ 32828B158518F56B137CC015B0ADBFDD /* FontsFileParser.swift in Sources */, A03C593A5675875DD832EB79EA5DC346 /* StoryboardParser.swift in Sources */, 1DBE65BBA7CC2D0C93544B240F73D17E /* StoryboardsContext.swift in Sources */, + 0726EEEB1E7D593E0035922E /* StoryboardParser.Scene.swift in Sources */, + 0726EEEC1E7D593E0035922E /* StoryboardParser.Segue.swift in Sources */, + 0726EEEA1E7D593E0035922E /* StoryboardParser.Enums.swift in Sources */, + 0726EEE81E7D593E0035922E /* Protocols.swift in Sources */, 84349FC390D9D0D9836345C172D4CB77 /* StringsContext.swift in Sources */, 033700BD14168E5C5614FDB4DF37B748 /* StringsFileParser.swift in Sources */, + 0726EEE91E7D593E0035922E /* StoryboardParser.Cell.swift in Sources */, 5E5FC679DE8A404241F31D47EFFDE7D6 /* SwiftGenKit-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Sources/Parsers/StoryboardParser.swift b/Sources/Parsers/StoryboardParser.swift index 5ece2ed..cad6d84 100644 --- a/Sources/Parsers/StoryboardParser.swift +++ b/Sources/Parsers/StoryboardParser.swift @@ -7,90 +7,214 @@ import Foundation import PathKit -public final class StoryboardParser { - struct InitialScene { - let objectID: String - let tag: String - let customClass: String? - let customModule: String? +public enum StoryboardParserError: Error, CustomStringConvertible { + case popoverPresentationSegueWithoutAnchorView + case unwindSegueWithoutUnwindAction + case relationshipSegueWithoutRelationship + + public var description: String { + switch self { + case .popoverPresentationSegueWithoutAnchorView: + return "Invalid storyboard file: A popoverPresentation type segue without " + case .unwindSegueWithoutUnwindAction: + return "Invalid strings file" + case .relationshipSegueWithoutRelationship: + return "Invalid strings file" + } } +} - struct Scene { - let storyboardID: String - let tag: String - let customClass: String? - let customModule: String? +// swiftlint:disable type_body_length +public final class StoryboardParser { + var storyboardFiles = [String: Set]() + var segueDestinationIds = Set() + var modules = Set() + + public struct Scene: Element { + let identifier: String + let storyboardId: String + let type: StoryboardParser.SceneElement + let isInitial: Bool + let reuseIdentifiers: Set + let segues: Set } - struct Segue { + public struct Segue: Element { let identifier: String - let customClass: String? - let customModule: String? + let storyboardId: String + let type: StoryboardParser.SegueElement + let destinationId: String + let kind: SegueKind + let popoverPresentationAnchorView: String? + let unwindAction: String? + let relationship: String? } - var initialScenes = [String: InitialScene]() - var storyboardsScenes = [String: Set]() - var storyboardsSegues = [String: Set]() - var modules = Set() + public struct Cell: Element { + let identifier: String + let storyboardId: String + let type: StoryboardParser.CellElement + var cellType: StoryboardParser.CellType { + return StoryboardParser.CellType(rawValue: type.tag)! + } + var dictionary: [String:Any] { + return [ + "storyboardId": storyboardId, + "tag": type.tag, + "class": type.klass, + "module": type.module, + "type": type.type + ] + } + } public init() {} private class ParserDelegate: NSObject, XMLParserDelegate { - var initialViewControllerObjectID: String? - var initialScene: InitialScene? + private var initialViewControllerObjectID: String? + private var storyboardPlatform: StoryboardParser.Platform = .iOS var scenes = Set() - var segues = Set() - var inScene = false - var readyForFirstObject = false - var readyForConnections = false + var segueDestinationIds = Set() + var modules = Set() + private var inScene = false + private var readyForFirstObject = false + private var readyForConnections = false + private var readyForTableView = false + private var readyForCollectionView = false + private var sceneIdentifier: String? + private var sceneTag: String? + private var sceneStoryboardID: String? + private var sceneClass: String? + private var sceneModule: String? + private var sceneIsInitial = false + private var sceneCells = Set() + private var sceneSegues = Set() + private func resetScene() { + sceneIdentifier = nil + sceneTag = nil + sceneStoryboardID = nil + sceneClass = nil + sceneModule = nil + sceneIsInitial = false + sceneCells = Set() + sceneSegues = Set() + } + // swiftlint:disable cyclomatic_complexity + // swiftlint:disable function_body_length @objc func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { + // swiftlint:enable function_body_length switch elementName { case "document": initialViewControllerObjectID = attributeDict["initialViewController"] + storyboardPlatform = Platform(rawValue: attributeDict["targetRuntime"]!)! case "scene": inScene = true case "objects" where inScene: readyForFirstObject = true case let tag where (readyForFirstObject && tag != "viewControllerPlaceholder"): - let customClass = attributeDict["customClass"] - let customModule = attributeDict["customModule"] - if let objectID = attributeDict["id"], objectID == initialViewControllerObjectID { - initialScene = InitialScene(objectID: objectID, - tag: tag, - customClass: customClass, - customModule: customModule) + if + let objectID = attributeDict["id"] + { + sceneIdentifier = objectID + sceneTag = tag + sceneStoryboardID = attributeDict["storyboardIdentifier"] + sceneClass = attributeDict["customClass"] + sceneModule = attributeDict["customModule"] + if let module = sceneModule { + modules.insert(module) + } + sceneIsInitial = initialViewControllerObjectID != nil && sceneIdentifier == initialViewControllerObjectID + readyForFirstObject = false } - if let storyboardID = attributeDict["storyboardIdentifier"] { - scenes.insert(Scene(storyboardID: storyboardID, - tag: tag, - customClass: customClass, - customModule: customModule)) + case "tableView" where inScene: + readyForTableView = true + case "tableViewCell" where readyForTableView: + if let reuseIdentifier = attributeDict["reuseIdentifier"] { + sceneCells.insert( + Cell(identifier: attributeDict["id"]!, + reuseIdentifier: reuseIdentifier, + platform: storyboardPlatform, + customClass: attributeDict["customClass"], + customModule: attributeDict["customModule"], + cellType: .tableViewCell) + ) + if let module = attributeDict["customModule"] { + modules.insert(module) + } } - readyForFirstObject = false - case "connections": + case "collectionView" where inScene: + readyForCollectionView = true + case "collectionViewCell" where readyForCollectionView: + if let reuseIdentifier = attributeDict["reuseIdentifier"] { + sceneCells.insert( + Cell(identifier: attributeDict["id"]!, + reuseIdentifier: reuseIdentifier, + platform: storyboardPlatform, + customClass: attributeDict["customClass"], + customModule: attributeDict["customModule"], + cellType: .collectionViewCell) + ) + if let module = attributeDict["customModule"] { + modules.insert(module) + } + } + case "connections" where inScene: readyForConnections = true case "segue" where readyForConnections: - if let segueID = attributeDict["identifier"] { - let customClass = attributeDict["customClass"] - let customModule = attributeDict["customModule"] - segues.insert(Segue(identifier: segueID, customClass: customClass, customModule: customModule)) + if attributeDict["identifier"] != nil { + if let segue = Segue(platform: storyboardPlatform, attributes: attributeDict) { + sceneSegues.insert(segue) + if segue.kind != .unwind { + segueDestinationIds.insert(segue.destinationId) + } + if let module = attributeDict["customModule"] { + modules.insert(module) + } + } } default: break } + } + // swiftlint:enable cyclomatic_complexity @objc func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { switch elementName { case "scene": inScene = false + defer { + resetScene() + } + guard + let sceneId = sceneIdentifier, + let tag = sceneTag + else { + break + } + + scenes.insert( + Scene(identifier: sceneId, + storyboardId: sceneStoryboardID, + platform: storyboardPlatform, + tag: tag, + customClass: sceneClass, + customModule: sceneModule, + isInitial: sceneIsInitial, + reuseIdentifiers: sceneCells, + segues: sceneSegues) + ) case "objects" where inScene: readyForFirstObject = false + case "tableView": + readyForTableView = false + case "collectionView": + readyForCollectionView = false case "connections": readyForConnections = false default: @@ -107,11 +231,9 @@ public final class StoryboardParser { parser.parse() let storyboardName = path.lastComponentWithoutExtension - initialScenes[storyboardName] = delegate.initialScene - storyboardsScenes[storyboardName] = delegate.scenes - storyboardsSegues[storyboardName] = delegate.segues - - modules.formUnion(collectModules(initial: delegate.initialScene, scenes: delegate.scenes, segues: delegate.segues)) + storyboardFiles[storyboardName] = delegate.scenes + segueDestinationIds.formUnion(delegate.segueDestinationIds) + modules.formUnion(delegate.modules) } public func parseDirectory(at path: Path) throws { @@ -123,43 +245,42 @@ public final class StoryboardParser { } } } - - private func collectModules(initial: InitialScene?, scenes: Set, segues: Set) -> Set { - var result = Set() - - if let module = initial?.customModule { - result.insert(module) - } - result.formUnion(Set(scenes.flatMap { $0.customModule })) - result.formUnion(Set(segues.flatMap { $0.customModule })) - - return result - } } +// swiftlint:enable type_body_length -extension StoryboardParser.Scene: Equatable { } -func == (lhs: StoryboardParser.Scene, rhs: StoryboardParser.Scene) -> Bool { - return lhs.storyboardID == rhs.storyboardID && - lhs.tag == rhs.tag && - lhs.customClass == rhs.customClass && - lhs.customModule == rhs.customModule +extension StoryboardParser.Scene: Hashable, Equatable { + public var hashValue: Int { + return (identifier.hashValue ^ + storyboardId.hashValue ^ + type.hashValue ^ + isInitial.hashValue ^ + reuseIdentifiers.hashValue ^ + segues.hashValue) + } } -extension StoryboardParser.Scene: Hashable { - var hashValue: Int { - return storyboardID.hashValue ^ tag.hashValue ^ (customModule?.hashValue ?? 0) ^ (customClass?.hashValue ?? 0) +extension StoryboardParser.Segue: Hashable, Equatable { + public var hashValue: Int { + return (identifier.hashValue ^ + storyboardId.hashValue ^ + type.hashValue ^ + destinationId.hashValue ^ + kind.hashValue ^ + (popoverPresentationAnchorView?.hashValue ?? 0) ^ + (unwindAction?.hashValue ?? 0) ^ + (relationship?.hashValue ?? 0) + ) } } -extension StoryboardParser.Segue: Equatable { } -func == (lhs: StoryboardParser.Segue, rhs: StoryboardParser.Segue) -> Bool { - return lhs.identifier == rhs.identifier && - lhs.customClass == rhs.customClass && - lhs.customModule == rhs.customModule -} +// MARK: - Extensions -extension StoryboardParser.Segue: Hashable { - var hashValue: Int { - return identifier.hashValue ^ (customModule?.hashValue ?? 0) ^ (customClass?.hashValue ?? 0) +extension String { + var capitalizedFirstLetter: String { + let first = String(characters.prefix(1)).capitalized + let other = String(characters.dropFirst()) + return first + other } } + +// swiftlint:enable file_length diff --git a/Sources/Parsers/StoryboardParserModel/Protocols.swift b/Sources/Parsers/StoryboardParserModel/Protocols.swift new file mode 100644 index 0000000..c5d59d7 --- /dev/null +++ b/Sources/Parsers/StoryboardParserModel/Protocols.swift @@ -0,0 +1,37 @@ +// +// SwiftGenKit +// Copyright (c) 2017 SwiftGen +// MIT Licence +// + +protocol Typable { + var platform: StoryboardParser.Platform { get } + var tag: String { get } + var customClass: String? { get } + var customModule: String? { get } + var klass: String { get } +} + +extension Typable { + var module: String { + let defaultModule: String + switch platform { + case .iOS: + defaultModule = "UIKit" + case .macOS: + defaultModule = "Cocoa" + } + return customModule ?? defaultModule + } + + var type: String { + return "\(module).\(klass)" + } +} + +protocol Element { + associatedtype ElementType: Typable + var identifier: String { get } + var storyboardId: String { get } + var type: ElementType { get } +} diff --git a/Sources/Parsers/StoryboardParserModel/StoryboardParser.Cell.swift b/Sources/Parsers/StoryboardParserModel/StoryboardParser.Cell.swift new file mode 100644 index 0000000..eea5e8e --- /dev/null +++ b/Sources/Parsers/StoryboardParserModel/StoryboardParser.Cell.swift @@ -0,0 +1,99 @@ +// +// SwiftGenKit +// Copyright (c) 2017 SwiftGen +// MIT Licence +// + +import Foundation + +extension StoryboardParser { + public struct CellElement: Typable { + let platform: StoryboardParser.Platform + let tag: String + let customClass: String? + let customModule: String? + var klass: String { + let defaultClass: String + switch platform { + case .iOS: + defaultClass = StoryboardParser.CellType(rawValue: tag) == .tableViewCell ? + "UITableViewCell" : "UICollectionViewCell" + case .macOS: + defaultClass = StoryboardParser.CellType(rawValue: tag) == .tableViewCell ? + "NSTableViewCell" : "NSCollectionViewCell" + } + return customClass ?? defaultClass + } + } +} + +extension StoryboardParser.CellElement { + public init(platform: StoryboardParser.Platform, + cellType: StoryboardParser.CellType, + customClass: String?, + customModule: String?) { + self.platform = platform + self.tag = cellType.rawValue + self.customClass = customClass + self.customModule = customModule + } +} + +extension StoryboardParser.CellElement: Hashable, Equatable { + public var hashValue: Int { + return (platform.hashValue ^ tag.hashValue ^ klass.hashValue) + } +} + +public func == (lhs: StoryboardParser.CellElement, rhs: StoryboardParser.CellElement) -> Bool { + return lhs.platform == rhs.platform && + lhs.tag == rhs.tag && + lhs.klass == rhs.klass +} + +extension StoryboardParser.Cell: Hashable, Equatable { + public var hashValue: Int { + return (identifier.hashValue ^ storyboardId.hashValue ^ type.hashValue) + } +} + +public func == (lhs: StoryboardParser.Cell, rhs: StoryboardParser.Cell) -> Bool { + return lhs.identifier == rhs.identifier && + lhs.storyboardId == rhs.storyboardId && + lhs.type == rhs.type +} + +extension StoryboardParser.Cell { + public init?(platform: StoryboardParser.Platform, + cellType: StoryboardParser.CellType, + attributes attributeDict: [String: String]) { + guard + let identifier = attributeDict["id"], + let reuseIdentifier = attributeDict["reuseIdentifier"] + else { + return nil + } + self.init(identifier: identifier, + reuseIdentifier: reuseIdentifier, + platform: platform, + customClass: attributeDict["customClass"], + customModule: attributeDict["customModule"], + cellType: cellType) + } +} + +extension StoryboardParser.Cell { + public init(identifier: String, + reuseIdentifier: String, + platform: StoryboardParser.Platform, + customClass: String?, + customModule: String?, + cellType: StoryboardParser.CellType) { + self.identifier = identifier + self.storyboardId = reuseIdentifier + self.type = StoryboardParser.CellElement(platform: platform, + cellType: cellType, + customClass: customClass, + customModule: customModule) + } +} diff --git a/Sources/Parsers/StoryboardParserModel/StoryboardParser.Enums.swift b/Sources/Parsers/StoryboardParserModel/StoryboardParser.Enums.swift new file mode 100644 index 0000000..8cb5e5c --- /dev/null +++ b/Sources/Parsers/StoryboardParserModel/StoryboardParser.Enums.swift @@ -0,0 +1,36 @@ +// +// SwiftGenKit +// Copyright (c) 2017 SwiftGen +// MIT Licence +// + +import Foundation + +extension StoryboardParser { + + public enum Platform: String { + case macOS = "MacOSX.Cocoa" + case iOS = "iOS.CocoaTouch" + } + + public enum CellType: String { + case tableViewCell + case collectionViewCell + } + + public enum SegueKind: String { + case show + case showDetail + case presentModal = "presentation" + case popoverPresentation + case custom + case unwind + case relationship + // macOS + case popover + case modal + case sheet + case embed + } + +} diff --git a/Sources/Parsers/StoryboardParserModel/StoryboardParser.Scene.swift b/Sources/Parsers/StoryboardParserModel/StoryboardParser.Scene.swift new file mode 100644 index 0000000..0e15db9 --- /dev/null +++ b/Sources/Parsers/StoryboardParserModel/StoryboardParser.Scene.swift @@ -0,0 +1,69 @@ +// +// SwiftGenKit +// Copyright (c) 2017 SwiftGen +// MIT Licence +// + +import Foundation + +extension StoryboardParser { + public struct SceneElement: Typable { + let platform: StoryboardParser.Platform + let tag: String + let customClass: String? + let customModule: String? + var klass: String { + let defaultClass: String + switch platform { + case .iOS: + defaultClass = "UI\(tag.capitalizedFirstLetter)" + case .macOS: + defaultClass = "NS\(tag.capitalizedFirstLetter)" + } + return customClass ?? defaultClass + } + } +} + +extension StoryboardParser.SceneElement: Hashable, Equatable { + public var hashValue: Int { + return (platform.hashValue ^ tag.hashValue ^ klass.hashValue) + } +} + +public func == (lhs: StoryboardParser.SceneElement, rhs: StoryboardParser.SceneElement) -> Bool { + return lhs.platform == rhs.platform && + lhs.tag == rhs.tag && + lhs.klass == rhs.klass +} + +public func == (lhs: StoryboardParser.Scene, rhs: StoryboardParser.Scene) -> Bool { + return lhs.identifier == rhs.identifier && + lhs.storyboardId == rhs.storyboardId && + lhs.type == rhs.type && + lhs.isInitial == rhs.isInitial && + lhs.reuseIdentifiers == rhs.reuseIdentifiers && + lhs.segues == rhs.segues +} + +extension StoryboardParser.Scene { + public init(identifier: String, + storyboardId: String?, + platform: StoryboardParser.Platform, + tag: String, + customClass: String?, + customModule: String?, + isInitial: Bool, + reuseIdentifiers: Set, + segues: Set) { + self.identifier = identifier + self.storyboardId = storyboardId ?? "" + self.type = StoryboardParser.SceneElement(platform: platform, + tag: tag, + customClass: customClass, + customModule: customModule) + self.isInitial = isInitial + self.reuseIdentifiers = reuseIdentifiers + self.segues = segues + } +} diff --git a/Sources/Parsers/StoryboardParserModel/StoryboardParser.Segue.swift b/Sources/Parsers/StoryboardParserModel/StoryboardParser.Segue.swift new file mode 100644 index 0000000..304f69a --- /dev/null +++ b/Sources/Parsers/StoryboardParserModel/StoryboardParser.Segue.swift @@ -0,0 +1,125 @@ +// +// SwiftGenKit +// Copyright (c) 2017 SwiftGen +// MIT Licence +// + +import Foundation + +extension StoryboardParser { + public struct SegueElement: Typable { + let platform: StoryboardParser.Platform + let tag: String + let customClass: String? + let customModule: String? + var klass: String { + let defaultClass: String + switch platform { + case .iOS: + defaultClass = "UIStoryboardSegue" + case .macOS: + defaultClass = "NSStoryboardSegue" + } + return customClass ?? defaultClass + } + } +} + +extension StoryboardParser.SegueElement { + public init(platform: StoryboardParser.Platform, customClass: String?, customModule: String?) { + self.platform = platform + self.tag = "segue" + self.customClass = customClass + self.customModule = customModule + } +} + +extension StoryboardParser.SegueElement: Hashable, Equatable { + public var hashValue: Int { + return (platform.hashValue ^ tag.hashValue ^ klass.hashValue) + } +} + +public func == (lhs: StoryboardParser.SegueElement, rhs: StoryboardParser.SegueElement) -> Bool { + return lhs.platform == rhs.platform && + lhs.tag == rhs.tag && + lhs.klass == rhs.klass +} + +public func == (lhs: StoryboardParser.Segue, rhs: StoryboardParser.Segue) -> Bool { + return (lhs.identifier == rhs.identifier && + lhs.storyboardId == rhs.storyboardId && + lhs.type == rhs.type && + lhs.destinationId == rhs.destinationId && + lhs.kind == rhs.kind && + lhs.popoverPresentationAnchorView == rhs.popoverPresentationAnchorView && + lhs.unwindAction == rhs.unwindAction && + lhs.relationship == rhs.relationship) +} + +extension StoryboardParser.Segue { + public init?(platform: StoryboardParser.Platform, + attributes attributeDict: [String: String]) { + guard + let identifier = attributeDict["id"], + let segueID = attributeDict["identifier"], + let destinationId = attributeDict["destination"], + let kind = StoryboardParser.SegueKind(rawValue: attributeDict["kind"]!) + else { + return nil + } + + try? self.init(identifier: identifier, + segueID: segueID, + platform: platform, + customClass: attributeDict["customClass"], + customModule: attributeDict["customModule"], + destinationId: destinationId, + kind: kind, + popoverPresentationAnchorView: attributeDict["popoverAnchorView"], + unwindAction: attributeDict["unwindAction"], + relationship: attributeDict["relationship"]) + } +} + +extension StoryboardParser.Segue { + public init(identifier: String, + segueID: String, + platform: StoryboardParser.Platform, + customClass: String?, + customModule: String?, + destinationId: String, + kind: StoryboardParser.SegueKind, + popoverPresentationAnchorView: String? = nil, + unwindAction: String? = nil, + relationship: String? = nil) throws { + + switch kind { + case .popoverPresentation: + if popoverPresentationAnchorView == nil { + throw StoryboardParserError.popoverPresentationSegueWithoutAnchorView + } + case .unwind: + if unwindAction == nil { + throw StoryboardParserError.unwindSegueWithoutUnwindAction + } + case .relationship: + if relationship == nil { + throw StoryboardParserError.relationshipSegueWithoutRelationship + } + default: + break + } + + self.identifier = identifier + self.storyboardId = segueID + self.type = StoryboardParser.SegueElement(platform: platform, + customClass: customClass, + customModule: customModule) + self.destinationId = destinationId + self.kind = kind + self.popoverPresentationAnchorView = popoverPresentationAnchorView + self.unwindAction = unwindAction + self.relationship = relationship + } +} diff --git a/Sources/Stencil/StoryboardsContext.swift b/Sources/Stencil/StoryboardsContext.swift index 711ef24..b326153 100644 --- a/Sources/Stencil/StoryboardsContext.swift +++ b/Sources/Stencil/StoryboardsContext.swift @@ -36,59 +36,156 @@ private func uppercaseFirst(_ string: String) -> String { - `class`: `String` (absent if generic UIStoryboardSegue) */ extension StoryboardParser { + // swiftlint:disable function_body_length public func stencilContext(sceneEnumName: String = "StoryboardScene", - segueEnumName: String = "StoryboardSegue") -> [String: Any] { - let storyboards = Set(storyboardsScenes.keys).union(storyboardsSegues.keys).sorted(by: <) + segueEnumName: String = "StoryboardSegue", + newFormat: Bool = false) -> [String: Any] { + let storyboards = Set(storyboardFiles.keys).sorted(by: <) let storyboardsMap = storyboards.map { (storyboardName: String) -> [String:Any] in - var sbMap: [String:Any] = ["name": storyboardName] - // Initial Scene - if let initialScene = initialScenes[storyboardName] { - let initial: [String:Any] - if let customClass = initialScene.customClass { - initial = ["customClass": customClass, "customModule": initialScene.customModule ?? ""] - } else { - initial = [ - "baseType": uppercaseFirst(initialScene.tag), - // NOTE: This is a deprecated variable - "isBaseViewController": initialScene.tag == "viewController" - ] - } - sbMap["initialScene"] = initial + let allScenes: Set = storyboardFiles.values + .reduce(Set()) { + return $0.union(Set($1)) } - // All Scenes - if let scenes = storyboardsScenes[storyboardName] { - sbMap["scenes"] = scenes - .sorted(by: {$0.storyboardID < $1.storyboardID}) - .map { (scene: Scene) -> [String:Any] in - if let customClass = scene.customClass { - return [ - "identifier": scene.storyboardID, - "customClass": customClass, - "customModule": scene.customModule ?? "" - ] - } else if scene.tag == "viewController" { + + let sceneIdClassDict: [String: String] = allScenes + .reduce([String: String]()) { previous, next -> [String: String] in + var dict: [String:String] = previous + dict[next.identifier] = next.type.type + return dict + } + + var segueDestinationClasseDict: [String: String] = segueDestinationIds + .reduce([String: String]()) { previous, next -> [String: String] in + var dict: [String:String] = previous + dict[next] = sceneIdClassDict[next] + return dict + } + + var sbMap: [String:Any] = ["name": storyboardName] + // All Storyboards + if let storyboard = storyboardFiles[storyboardName] { + // Initial Scene + if let initialScene = storyboard.first(where: { $0.isInitial == true }) { + let initial: [String:Any] + if let customClass = initialScene.type.customClass { + initial = ["customClass": customClass, "customModule": initialScene.type.customModule ?? ""] + } else { + initial = [ + "baseType": uppercaseFirst(initialScene.type.tag), + // NOTE: This is a deprecated variable + "isBaseViewController": initialScene.type.tag == "viewController" + ] + } + sbMap["initialScene"] = initial + } + + if newFormat { + // All Scenes + sbMap["scenes"] = storyboard + .filter { $0.storyboardId != "" } + .sorted(by: {$0.storyboardId < $1.storyboardId}) + .map { (scene: Scene) -> [String:Any] in + + let segues: [[String:String]] = scene.segues + .reduce([[String: String]]()) { + var array = $0 + array.append([ + "storyboardId": $1.storyboardId, + "class": $1.type.klass, + "module": $1.type.module, + "type": $1.type.type, + "destinationType": segueDestinationClasseDict[$1.destinationId] ?? "", + "kind": $1.kind.rawValue + ]) + return array + } + return [ - "identifier": scene.storyboardID, - "baseType": uppercaseFirst(scene.tag), + "identifier": scene.storyboardId, + "class": scene.type.klass, + "module": scene.type.module, + "type": scene.type.type, + "isInitial": scene.isInitial, + "reuseIdentifiers": scene.reuseIdentifiers.map { $0.dictionary }, + "segues": segues, // NOTE: This is a deprecated variable - "isBaseViewController": scene.tag == "viewController" + "isBaseViewController": scene.type.tag == "viewController" + ] - } - return ["identifier": scene.storyboardID, "baseType": uppercaseFirst(scene.tag)] + +// if let customClass = scene.type.customClass { +// return [ +// "identifier": scene.storyboardId, +// "customClass": customClass, +// "customModule": scene.type.customModule ?? "" +// ] +// } else if scene.type.tag == "viewController" { +// return [ +// "identifier": scene.storyboardId, +// "baseType": uppercaseFirst(scene.type.tag), +// +// // NOTE: This is a deprecated variable +// "isBaseViewController": scene.type.tag == "viewController" +// ] +// } +// return ["identifier": scene.storyboardId, "baseType": uppercaseFirst(scene.type.tag)] + } + + } else { + + // All Scenes + sbMap["scenes"] = storyboard + .filter { $0.storyboardId != "" } + .sorted(by: {$0.storyboardId < $1.storyboardId}) + .map { (scene: Scene) -> [String:Any] in + if let customClass = scene.type.customClass { + return [ + "identifier": scene.storyboardId, + "customClass": customClass, + "customModule": scene.type.customModule ?? "" + ] + } else if scene.type.tag == "viewController" { + return [ + "identifier": scene.storyboardId, + "baseType": uppercaseFirst(scene.type.tag), + + // NOTE: This is a deprecated variable + "isBaseViewController": scene.type.tag == "viewController" + ] + } + return ["identifier": scene.storyboardId, "baseType": uppercaseFirst(scene.type.tag)] + } + + // All Segues + let allSegues = storyboard + .map { (scene: Scene) -> Set in scene.segues } + .reduce(Set()) { $0.union($1) } + .filter { $0.storyboardId != "" } + .reduce(Set()) { (previous: Set, next: Segue) -> Set in + let notInPrevious = previous + .filter { $0.storyboardId == next.storyboardId } + .isEmpty + if notInPrevious { + var previousSet = previous + previousSet.insert(next) + return previousSet + } + return previous + } + + sbMap["segues"] = allSegues + .sorted(by: {$0.storyboardId < $1.storyboardId}) + .map { (segue: Segue) -> [String:String] in + ["identifier": segue.storyboardId, "customClass": segue.type.customClass ?? ""] + } } } - // All Segues - if let segues = storyboardsSegues[storyboardName] { - sbMap["segues"] = segues - .sorted(by: {$0.identifier < $1.identifier}) - .map { (segue: Segue) -> [String:String] in - ["identifier": segue.identifier, "customClass": segue.customClass ?? ""] - } - } + return sbMap } + return [ "sceneEnumName": sceneEnumName, "segueEnumName": segueEnumName, @@ -99,4 +196,5 @@ extension StoryboardParser { "extraImports": modules.sorted() ] } + // swiftlint:enable function_body_length } diff --git a/SwiftGenKit.xcodeproj/project.pbxproj b/SwiftGenKit.xcodeproj/project.pbxproj index 66e09a9..8884b8c 100644 --- a/SwiftGenKit.xcodeproj/project.pbxproj +++ b/SwiftGenKit.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 0726EEEE1E7DE0970035922E /* StoryboardsNewFormatiOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0726EEED1E7DE0970035922E /* StoryboardsNewFormatiOSTests.swift */; }; + 0726EEF61E7E028B0035922E /* StoryboardsNewFormatMacTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0726EEF51E7E028B0035922E /* StoryboardsNewFormatMacTests.swift */; }; 82EF0CC0752D216C67279A16 /* Pods_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BF798509C76E5A9ACE03491 /* Pods_Tests.framework */; }; DDF573231E6103DC0033C01B /* ColorsCLRFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF573171E6103DC0033C01B /* ColorsCLRFileTests.swift */; }; DDF573241E6103DC0033C01B /* ColorsJSONFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF573181E6103DC0033C01B /* ColorsJSONFileTests.swift */; }; @@ -23,6 +25,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 0726EEED1E7DE0970035922E /* StoryboardsNewFormatiOSTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardsNewFormatiOSTests.swift; sourceTree = ""; }; + 0726EEF51E7E028B0035922E /* StoryboardsNewFormatMacTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardsNewFormatMacTests.swift; sourceTree = ""; }; 09A87B501BCCA2C600D9B9F5 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 47888DD528DEC4C84FD8F15B /* Pods-Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.debug.xcconfig"; sourceTree = ""; }; 4B3D39DBCD15D8F6BB891D92 /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = ""; }; @@ -112,6 +116,8 @@ DDF5731C1E6103DC0033C01B /* ImagesTests.swift */, DDF5731F1E6103DC0033C01B /* StoryboardsiOSTests.swift */, DDF573201E6103DC0033C01B /* StoryboardsMacOSTests.swift */, + 0726EEED1E7DE0970035922E /* StoryboardsNewFormatiOSTests.swift */, + 0726EEF51E7E028B0035922E /* StoryboardsNewFormatMacTests.swift */, DDF573211E6103DC0033C01B /* StringsTests.swift */, DDF573221E6103DC0033C01B /* TestsHelper.swift */, ); @@ -273,6 +279,8 @@ DDF573281E6103DC0033C01B /* ImagesTests.swift in Sources */, DDF5732C1E6103DC0033C01B /* StringsTests.swift in Sources */, DDF573241E6103DC0033C01B /* ColorsJSONFileTests.swift in Sources */, + 0726EEF61E7E028B0035922E /* StoryboardsNewFormatMacTests.swift in Sources */, + 0726EEEE1E7DE0970035922E /* StoryboardsNewFormatiOSTests.swift in Sources */, DDF5732A1E6103DC0033C01B /* StoryboardsiOSTests.swift in Sources */, DDF573271E6103DC0033C01B /* FontsTests.swift in Sources */, ); diff --git a/Tests/Resources b/Tests/Resources index 35a8748..bc193d7 160000 --- a/Tests/Resources +++ b/Tests/Resources @@ -1 +1 @@ -Subproject commit 35a8748f00c11859505ae83e943abb7318919487 +Subproject commit bc193d740e462554f4943f92ae73518182958edf diff --git a/Tests/SwiftGenKitTests/StoryboardsNewFormatMacTests.swift b/Tests/SwiftGenKitTests/StoryboardsNewFormatMacTests.swift new file mode 100644 index 0000000..3bd6bdd --- /dev/null +++ b/Tests/SwiftGenKitTests/StoryboardsNewFormatMacTests.swift @@ -0,0 +1,84 @@ +// +// SwiftGenKit +// Copyright (c) 2017 Olivier Halligon +// MIT Licence +// + +import XCTest +import SwiftGenKit + +/** + * Important: In order for the "*.storyboard" files in fixtures/ to be copied as-is in the test bundle + * (as opposed to being compiled when the test bundle is compiled), a custom "Build Rule" has been added to the target. + * See Project -> Target "UnitTests" -> Build Rules -> « Files "*.storyboard" using PBXCp » + */ + +class StoryboardsNewFormatMacTests: XCTestCase { + + func testEmpty() { + let parser = StoryboardParser() + + let result = parser.stencilContext(newFormat: true) + let expected = Fixtures.context(for: "empty.plist", sub: .storyboardsNewMacOS) + + XCTDiffContexts(result, expected) + } + + func testMessageStoryboardWithDefaults() { + let parser = StoryboardParser() + do { + try parser.addStoryboard(at: Fixtures.path(for: "Message.storyboard", sub: .storyboardsMacOS)) + } catch { + print("Error: \(error.localizedDescription)") + } + + let result = parser.stencilContext(newFormat: true) + let expected = Fixtures.context(for: "messages.plist", sub: .storyboardsNewMacOS) + + XCTDiffContexts(result, expected) + } + + func testAnonymousStoryboardWithDefaults() { + let parser = StoryboardParser() + do { + try parser.addStoryboard(at: Fixtures.path(for: "Anonymous.storyboard", sub: .storyboardsMacOS)) + } catch { + print("Error: \(error.localizedDescription)") + } + + let result = parser.stencilContext(newFormat: true) + let expected = Fixtures.context(for: "anonymous.plist", sub: .storyboardsNewMacOS) + + XCTDiffContexts(result, expected) + } + + func testAllStoryboardsWithDefaults() { + let parser = StoryboardParser() + do { + try parser.parseDirectory(at: Fixtures.directory(sub: .storyboardsMacOS)) + } catch { + print("Error: \(error.localizedDescription)") + } + + let result = parser.stencilContext(newFormat: true) + let expected = Fixtures.context(for: "all.plist", sub: .storyboardsNewMacOS) + + XCTDiffContexts(result, expected) + } + + func testAllStoryboardsWithCustomName() { + let parser = StoryboardParser() + do { + try parser.parseDirectory(at: Fixtures.directory(sub: .storyboardsMacOS)) + } catch { + print("Error: \(error.localizedDescription)") + } + + let result = parser.stencilContext(sceneEnumName: "XCTStoryboardsScene", + segueEnumName: "XCTStoryboardsSegue", newFormat: true) + let expected = Fixtures.context(for: "customname.plist", sub: .storyboardsNewMacOS) + + XCTDiffContexts(result, expected) + } + +} diff --git a/Tests/SwiftGenKitTests/StoryboardsNewFormatiOSTests.swift b/Tests/SwiftGenKitTests/StoryboardsNewFormatiOSTests.swift new file mode 100644 index 0000000..dc3c35a --- /dev/null +++ b/Tests/SwiftGenKitTests/StoryboardsNewFormatiOSTests.swift @@ -0,0 +1,84 @@ +// +// SwiftGenKit +// Copyright (c) 2017 Olivier Halligon +// MIT Licence +// + +import XCTest +import SwiftGenKit + +/** + * Important: In order for the "*.storyboard" files in fixtures/ to be copied as-is in the test bundle + * (as opposed to being compiled when the test bundle is compiled), a custom "Build Rule" has been added to the target. + * See Project -> Target "UnitTests" -> Build Rules -> « Files "*.storyboard" using PBXCp » + */ + +class StoryboardsNewFormatiOSTests: XCTestCase { + + func testEmpty() { + let parser = StoryboardParser() + + let result = parser.stencilContext(newFormat: true) + let expected = Fixtures.context(for: "empty.plist", sub: .storyboardsNewiOS) + + XCTDiffContexts(result, expected) + } + + func testMessageStoryboardWithDefaults() { + let parser = StoryboardParser() + do { + try parser.addStoryboard(at: Fixtures.path(for: "Message.storyboard", sub: .storyboardsiOS)) + } catch { + print("Error: \(error.localizedDescription)") + } + + let result = parser.stencilContext(newFormat: true) + let expected = Fixtures.context(for: "messages.plist", sub: .storyboardsNewiOS) + + XCTDiffContexts(result, expected) + } + + func testAnonymousStoryboardWithDefaults() { + let parser = StoryboardParser() + do { + try parser.addStoryboard(at: Fixtures.path(for: "Anonymous.storyboard", sub: .storyboardsiOS)) + } catch { + print("Error: \(error.localizedDescription)") + } + + let result = parser.stencilContext(newFormat: true) + let expected = Fixtures.context(for: "anonymous.plist", sub: .storyboardsNewiOS) + + XCTDiffContexts(result, expected) + } + + func testAllStoryboardsWithDefaults() { + let parser = StoryboardParser() + do { + try parser.parseDirectory(at: Fixtures.directory(sub: .storyboardsiOS)) + } catch { + print("Error: \(error.localizedDescription)") + } + + let result = parser.stencilContext(newFormat: true) + let expected = Fixtures.context(for: "all.plist", sub: .storyboardsNewiOS) + + XCTDiffContexts(result, expected) + } + + func testAllStoryboardsWithCustomName() { + let parser = StoryboardParser() + do { + try parser.parseDirectory(at: Fixtures.directory(sub: .storyboardsiOS)) + } catch { + print("Error: \(error.localizedDescription)") + } + + let result = parser.stencilContext(sceneEnumName: "XCTStoryboardsScene", + segueEnumName: "XCTStoryboardsSegue", newFormat: true) + let expected = Fixtures.context(for: "customname.plist", sub: .storyboardsNewiOS) + + XCTDiffContexts(result, expected) + } + +} diff --git a/Tests/SwiftGenKitTests/TestsHelper.swift b/Tests/SwiftGenKitTests/TestsHelper.swift index 918ad34..7fce8df 100644 --- a/Tests/SwiftGenKitTests/TestsHelper.swift +++ b/Tests/SwiftGenKitTests/TestsHelper.swift @@ -85,6 +85,8 @@ class Fixtures { case storyboardsiOS = "Storyboards-iOS" case storyboardsMacOS = "Storyboards-macOS" case strings = "Strings" + case storyboardsNewiOS = "Storyboards-new-iOS" + case storyboardsNewMacOS = "Storyboards-new-macOS" } private static let testBundle = Bundle(for: Fixtures.self)