├── fastlane ├── .env ├── Scanfile ├── .env.default ├── .env.gitsubmodules ├── .env.cocoapod ├── .env.deploy ├── README.md └── Fastfile ├── Cocoapod ├── Podfile ├── StoryboardKit.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── CocoapodTests.xcscheme │ └── project.pbxproj ├── StoryboardKit.xcworkspace │ └── contents.xcworkspacedata └── Podfile.lock ├── Gemfile ├── .gitmodules ├── StoryboardKitTests ├── Images.xcassets │ └── Zzzzzz.imageset │ │ ├── Zzzzzz.png │ │ └── Contents.json ├── Info.plist ├── StoryboardInfoTests.swift ├── ClassInfoTests.swift ├── StoryboardFileParserTests.swift ├── TableViewInstanceInfoTests.swift ├── ApplicationInfoTests.swift ├── StoryboardInstanceInfoTests.swift ├── ViewInstanceInfoTests.swift └── StoryboardKit.storyboard ├── StoryboardKit.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── StoryboardKit.xcscmblueprint └── xcshareddata │ └── xcschemes │ └── StoryboardKit.xcscheme ├── Tools ├── generateDocs.sh └── matchVersiontoBranch.sh ├── .gitignore ├── StoryboardKit ├── StoryboardKit.h ├── Views │ ├── TableViewClassInfo.swift │ ├── CollectionViewClassInfo.swift │ ├── ViewClassInfo.swift │ ├── TableViewInstanceInfo.swift │ ├── ViewInstanceInfo.swift │ └── CollectionViewInstanceInfo.swift ├── ViewControllers │ ├── TabBarControllerClassInfo.swift │ ├── NavigationControllerClassInfo.swift │ ├── TabBarControllerInstanceInfo.swift │ ├── NavigationControllerInstanceInfo.swift │ ├── ViewControllerClassInfo.swift │ └── ViewControllerInstanceInfo.swift ├── ViewControllerLayoutGuideInstanceInfo.swift ├── Info.plist ├── NavigationItemInstanceInfo.swift ├── SegueClassInfo.swift ├── ClassInfo.swift ├── Utility.swift ├── StoryboardFileVersionedParsers │ └── 3.0 │ │ ├── StoryboardFile3_0Parser_Scenes.swift │ │ ├── StoryboardFile3_0Parser.swift │ │ ├── StoryboardFile3_0Parser_Storyboard.swift │ │ ├── StoryboardFile3_0Parser_Segues.swift │ │ ├── StoryboardFile3_0Parser_ViewControllers.swift │ │ └── StoryboardFile3_0Parser_Views.swift ├── StoryboardInstanceInfo.swift ├── StoryboardFileParser.swift ├── SegueInstanceInfo.swift └── ApplicationInfo.swift ├── StoryboardKit.podspec ├── LICENSE.md ├── .travis.yml ├── .overcommit.yml ├── CHANGELOG.md ├── README.md └── Gemfile.lock /fastlane/.env: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fastlane/Scanfile: -------------------------------------------------------------------------------- 1 | clean true 2 | code_coverage true -------------------------------------------------------------------------------- /fastlane/.env.default: -------------------------------------------------------------------------------- 1 | MAC_SDK=macosx10.11 2 | 3 | CONFIGURATION=Release 4 | SCAN_SDK=$MAC_SDK 5 | -------------------------------------------------------------------------------- /fastlane/.env.gitsubmodules: -------------------------------------------------------------------------------- 1 | SCAN_PROJECT="StoryboardKit.xcodeproj" 2 | SCAN_SCHEME="StoryboardKit" -------------------------------------------------------------------------------- /Cocoapod/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'CocoapodTests' do 4 | pod 'StoryboardKit', :path => '../' 5 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'cocoapods' 4 | gem 'github_changelog_generator' 5 | gem 'fastlane' -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Libraries/SWXMLHash"] 2 | path = Libraries/SWXMLHash 3 | url = https://github.com/drmohundro/SWXMLHash.git 4 | -------------------------------------------------------------------------------- /fastlane/.env.cocoapod: -------------------------------------------------------------------------------- 1 | SCAN_WORKSPACE="Cocoapod/StoryboardKit.xcworkspace" 2 | SCAN_SCHEME="CocoapodTests" 3 | SCAN_DESTINATION="arch=x86_64" -------------------------------------------------------------------------------- /StoryboardKitTests/Images.xcassets/Zzzzzz.imageset/Zzzzzz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Adorkable/StoryboardKit/master/StoryboardKitTests/Images.xcassets/Zzzzzz.imageset/Zzzzzz.png -------------------------------------------------------------------------------- /StoryboardKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Cocoapod/StoryboardKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tools/generateDocs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | jazzy -o docs --swift-version 2.1 4 | find ./docs/Classes/*.html | xargs grep 'Undocumented' 5 | find ./docs/Classes/**/*.html | xargs grep 'Undocumented' 6 | find ./docs/Extensions/*.html | xargs grep 'Undocumented' 7 | -------------------------------------------------------------------------------- /Cocoapod/StoryboardKit.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /StoryboardKitTests/Images.xcassets/Zzzzzz.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Zzzzzz.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /fastlane/.env.deploy: -------------------------------------------------------------------------------- 1 | DEPLOY_BRANCH=master 2 | DEPLOY_PLIST_PATH=StoryboardKit/Info.plist 3 | DEPLOY_PODSPEC=StoryboardKit.podspec 4 | DEPLOY_REMOTE=origin 5 | 6 | DEPLOY_CHANGELOG_PATH=CHANGELOG.md 7 | DEPLOY_CHANGELOG_DELIMITER=--- 8 | 9 | # Used for CHANGELOG Generation and Github Release Management 10 | GITHUB_OWNER=Adorkable 11 | GITHUB_REPOSITORY=StoryboardKit 12 | # CI Should Provide GITHUB_API_TOKEN 13 | 14 | CARTHAGE_FRAMEWORK_NAME=StoryboardKit -------------------------------------------------------------------------------- /Cocoapod/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - StoryboardKit (0.5.9): 3 | - SWXMLHash 4 | - SWXMLHash (2.5.0) 5 | 6 | DEPENDENCIES: 7 | - StoryboardKit (from `../`) 8 | 9 | EXTERNAL SOURCES: 10 | StoryboardKit: 11 | :path: "../" 12 | 13 | SPEC CHECKSUMS: 14 | StoryboardKit: 84b859333d89d7e49beebfc3e2c8c13d9ebd4a90 15 | SWXMLHash: 04158642e0c90c52274db7914032edd41bed9a41 16 | 17 | PODFILE CHECKSUM: b29a5efaf365d56920c542e37990cb965160bc23 18 | 19 | COCOAPODS: 1.0.1 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .ruby-version 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Cocoapods 25 | # 26 | Pods/ 27 | 28 | # Carthage 29 | # 30 | Carthage/Checkouts 31 | Carthage/Build 32 | 33 | # Jazzy 34 | # 35 | docs/ 36 | 37 | # Fastlane 38 | fastlane/report.xml 39 | fastlane/test-output 40 | fastlane/test_output 41 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | ``` 5 | sudo gem install fastlane 6 | ``` 7 | # Available Actions 8 | ## Mac 9 | ### mac test_framework 10 | ``` 11 | fastlane mac test_framework 12 | ``` 13 | Runs all the tests 14 | 15 | ---- 16 | 17 | This README.md is auto-generated and will be re-generated every time to run [fastlane](https://fastlane.tools). 18 | More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools). 19 | The documentation of fastlane can be found on [GitHub](https://github.com/fastlane/fastlane/tree/master/fastlane). -------------------------------------------------------------------------------- /StoryboardKit/StoryboardKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardKit.h 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/3/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for StoryboardKit. 12 | FOUNDATION_EXPORT double StoryboardKitVersionNumber; 13 | 14 | //! Project version string for StoryboardKit. 15 | FOUNDATION_EXPORT const unsigned char StoryboardKitVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /StoryboardKit.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "StoryboardKit" 4 | s.version = "0.5.10" 5 | s.summary = "All you would want to know about yer Storyboards" 6 | 7 | s.homepage = "https://github.com/Adorkable/StoryboardKit.git" 8 | 9 | s.author = { "Ian G" => "yo.ian.g@gmail.com" } 10 | s.platform = :osx, "10.10" 11 | 12 | s.license = "MIT" 13 | 14 | s.source = { :git => "https://github.com/Adorkable/StoryboardKit.git", :tag => s.version.to_s } 15 | 16 | s.source_files = "StoryboardKit/**/*.swift" 17 | 18 | s.requires_arc = true 19 | 20 | s.dependency 'SWXMLHash' 21 | end 22 | -------------------------------------------------------------------------------- /StoryboardKit/Views/TableViewClassInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewClassInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian Grossberg on 8/26/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Table View Class that is used in your application and its storyboards 12 | public class TableViewClassInfo: ViewClassInfo { 13 | override public class var defaultClass : String { return "UITableView" } 14 | 15 | /** 16 | Default init 17 | 18 | - parameter className: name of the Class. If nil defaults to UITableView. 19 | 20 | - returns: A new TableViewClassInfo 21 | */ 22 | required public init(className : String?) { 23 | super.init(className: className) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /StoryboardKit/Views/CollectionViewClassInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewClassInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian Grossberg on 10/30/15. 6 | // Copyright © 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Collection View Class that is used in your application and its storyboards 12 | public class CollectionViewClassInfo: ViewClassInfo { 13 | override public class var defaultClass : String { return "UICollectionView" } 14 | 15 | /** 16 | Default init 17 | 18 | - parameter className: name of the Class. If nil defaults to UICollectionView. 19 | 20 | - returns: A new CollectionViewClassInfo 21 | */ 22 | required public init(className : String?) { 23 | super.init(className: className) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /StoryboardKitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 0.5.10 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /StoryboardKit/ViewControllers/TabBarControllerClassInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarControllerClassInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian Grossberg on 10/15/15. 6 | // Copyright © 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Tab Bar Controller Class that is used in your application and its storyboards 12 | public class TabBarControllerClassInfo: ViewControllerClassInfo { 13 | 14 | override public class var defaultClass : String { return "UITabBarController" } 15 | 16 | /** 17 | Default init 18 | 19 | - parameter className: Name of the Tab Bar Controller class. If nil defaults to UITabBarController 20 | 21 | - returns: A new instance. 22 | */ 23 | required public init(className: String?) { 24 | super.init(className: className) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /StoryboardKit/ViewControllers/NavigationControllerClassInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationControllerClassInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/8/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Navigation Controller class that is used in your application and its storyboards 12 | public class NavigationControllerClassInfo: ViewControllerClassInfo { 13 | override public class var defaultClass : String { return "UINavigationController" } 14 | 15 | /** 16 | Default init 17 | 18 | - parameter className: name of the Navigation Controller class. If nil defaults to UINavigationController. 19 | 20 | - returns: A new ViewClassInfo instance 21 | */ 22 | required public init(className: String?) { 23 | super.init(className: className) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /StoryboardKit/ViewControllerLayoutGuideInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewControllerLayoutGuideInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/1/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a View Controller Layout Guide Instance that is used in your application and its storyboards 12 | public class ViewControllerLayoutGuideInstanceInfo: NSObject, Idable { 13 | 14 | /// Storyboard Id 15 | public let id : String 16 | 17 | /// Type - TODO: Enum 18 | public let type : String 19 | 20 | /** 21 | Default init 22 | 23 | - parameter id: Storyboard Id 24 | - parameter type: Type 25 | 26 | - returns: A new instance. 27 | */ 28 | public init(id : String, type : String) { 29 | self.id = id 30 | self.type = type 31 | 32 | super.init() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /StoryboardKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.5.10 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | NSHumanReadableCopyright 24 | Copyright © 2015 Adorkable. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /StoryboardKit/ViewControllers/TabBarControllerInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarControllerInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian Grossberg on 10/15/15. 6 | // Copyright © 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Tab Bar Controller Instance that is used in your application and its storyboards 12 | public class TabBarControllerInstanceInfo: ViewControllerInstanceInfo { 13 | 14 | /** 15 | Default init 16 | 17 | - parameter classInfo: Class 18 | - parameter id: Storyboard Id 19 | - parameter storyboardIdentifier: Storyboard Identifier 20 | - parameter view: View 21 | 22 | - returns: A new instance. 23 | */ 24 | public override init(classInfo : ViewControllerClassInfo, id : String, storyboardIdentifier : String?, view : ViewInstanceInfo?) { 25 | 26 | super.init(classInfo: classInfo, id: id, storyboardIdentifier: storyboardIdentifier, view: view) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /StoryboardKit/NavigationItemInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationItemInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/1/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Navigation Item Instance that is used in your application and its storyboards 12 | public class NavigationItemInstanceInfo: NSObject, Idable { 13 | 14 | /// Storyboard Id 15 | public let id : String 16 | 17 | /// Navigation Item Key 18 | public let navigationItemKey : String 19 | 20 | /// Title 21 | public let title : String 22 | 23 | /** 24 | Default init 25 | 26 | - parameter id: Storyboard Id 27 | - parameter navigationItemKey: Navigation Item Key 28 | - parameter title: Title 29 | 30 | - returns: A new instance. 31 | */ 32 | init(id : String, navigationItemKey : String, title : String) { 33 | self.id = id 34 | self.navigationItemKey = navigationItemKey 35 | self.title = title 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /StoryboardKitTests/StoryboardInfoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardInfoTests.swift 3 | // StoryboardKitTests 4 | // 5 | // Created by Ian on 5/3/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | 12 | import StoryboardKit 13 | 14 | func storyboardPathBuilder() -> String? { 15 | if let absoluteString = NSBundle(forClass: StoryboardInfoTests.self).URLForResource("StoryboardKit", withExtension: ".storyboard")?.absoluteString { 16 | return (absoluteString as NSString).stringByReplacingOccurrencesOfString("file://", withString: "") 17 | } 18 | return nil 19 | } 20 | 21 | class StoryboardInfoTests: XCTestCase { 22 | func testStoryboardPathBuilder() { 23 | let storyboardPath = storyboardPathBuilder() 24 | XCTAssertNotNil(storyboardPath, "Storyboard Path is nil") 25 | 26 | if storyboardPath != nil 27 | { 28 | let fileExists = NSFileManager.defaultManager().fileExistsAtPath(storyboardPath!) 29 | XCTAssertTrue(fileExists, "Storyboard file does not exist at path \(storyboardPath!)") 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Ian Grossberg 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | 25 | -------------------------------------------------------------------------------- /StoryboardKit/SegueClassInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SegueClassInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/4/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Segue Class that is used in your application and its storyboards 12 | public class SegueClassInfo: ClassInfo { 13 | 14 | override public class var defaultClass : String { return "UIStoryboardSegue" } 15 | 16 | /// All instance of this class in the application 17 | public private(set) var instanceInfos = Array< StoryboardKit_WeakWrapper< SegueInstanceInfo> >() 18 | 19 | /** 20 | Default init 21 | 22 | - parameter className: Name of the Segue class. If nil defaults to UIStoryboardSegue 23 | 24 | - returns: A new instance. 25 | */ 26 | required public init(className : String?) { 27 | super.init(className: className) 28 | } 29 | 30 | /** 31 | Add a Segue Instance 32 | 33 | - parameter instanceInfo: Segue Instance to add 34 | */ 35 | func add(instanceInfo instanceInfo : SegueInstanceInfo) { 36 | self.instanceInfos.append( StoryboardKit_WeakWrapper(instanceInfo) ) 37 | } 38 | } -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # Customise this file, documentation can be found here: 2 | # https://github.com/fastlane/fastlane/tree/master/fastlane/docs 3 | # All available actions: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Actions.md 4 | # can also be listed using the `fastlane actions` command 5 | 6 | # Change the syntax highlighting to Ruby 7 | # All lines starting with a # are ignored when running `fastlane` 8 | 9 | # If you want to automatically update fastlane if a new version is available: 10 | # update_fastlane 11 | 12 | # This is the minimum version number required. 13 | # Update this, if you use features of a newer version 14 | fastlane_version "1.95.0" 15 | 16 | default_platform :mac 17 | 18 | platform :mac do 19 | 20 | desc "Runs all the tests" 21 | lane :test_framework do 22 | scan 23 | end 24 | 25 | end 26 | 27 | 28 | # More information about multiple platforms in fastlane: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md 29 | # All available actions: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Actions.md 30 | 31 | # fastlane reports which actions are used 32 | # No personal data is recorded. Learn more at https://github.com/fastlane/enhancer 33 | -------------------------------------------------------------------------------- /StoryboardKitTests/ClassInfoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClassInfoTests.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/29/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import StoryboardKit 12 | 13 | class ClassInfoTests: XCTestCase { 14 | 15 | func testInit() { 16 | let classInfo = ClassInfo(className: nil) 17 | 18 | XCTAssertEqual(classInfo.infoClassName, ClassInfo.defaultClass, "ClassInfo class name should be \"\(ClassInfo.defaultClass)\", was \"\(classInfo.infoClassName)\"") 19 | } 20 | 21 | func testInitCustomClassName() { 22 | let className = "Blah-dee Blah Blah" 23 | 24 | let classInfo = ClassInfo(className: className) 25 | 26 | XCTAssertEqual(classInfo.infoClassName, className, "ClassInfo class name should be \"\(className)\", was \"\(classInfo.infoClassName)\"") 27 | } 28 | 29 | func testDebugDescription() { 30 | let classInfo = ClassInfo(className: nil) 31 | 32 | let debugDescription = classInfo.debugDescription 33 | 34 | XCTAssertNotNil(debugDescription.rangeOfString("Class: "), "ClassInfo's debug description should include the class: \(debugDescription)") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.3 3 | env: 4 | global: 5 | - LC_CTYPE=en_US.UTF-8 6 | - LANG=en_US.UTF-8 7 | # - FASTLANE_LANE=test_framework 8 | # matrix: 9 | # - FASTLANE_ENV=gitsubmodules 10 | # - FASTLANE_ENV=cocoapod 11 | before_install: 12 | - gem install fastlane --no-rdoc --no-ri --no-document --quiet 13 | - gem install cocoapods --no-rdoc --no-ri --no-document --quiet 14 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet 15 | # - fastlane enable_crash_reporting 16 | - git submodule init 17 | script: 18 | - xcodebuild -scheme StoryboardKit -project StoryboardKit.xcodeproj -sdk 'macosx10.11' -destination 'platform=OS X' -enableCodeCoverage YES clean build test | xcpretty 19 | - cd Cocoapod && pod install && xcodebuild -scheme CocoapodTests -workspace StoryboardKit.xcworkspace -sdk 'macosx10.11' -destination 'platform=OS X' -enableCodeCoverage YES test | xcpretty && cd .. 20 | # - fastlane $FASTLANE_LANE configuration:Debug --env $FASTLANE_ENV 21 | # - fastlane $FASTLANE_LANE configuration:Release --env $FASTLANE_ENV 22 | # deploy: 23 | # provider: script 24 | # script: fastlane complete_framework_release --env deploy 25 | # on: 26 | # tags: true 27 | after_success: 28 | - bash <(curl -s https://codecov.io/bash) 29 | -------------------------------------------------------------------------------- /StoryboardKit/Views/ViewClassInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewClassInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/29/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | * Represents a View Class that is used in your application and its storyboards 13 | */ 14 | public class ViewClassInfo: ClassInfo { 15 | 16 | override public class var defaultClass : String { return "UIView" } 17 | 18 | /// All instances of this class in the application 19 | public private(set) var instanceInfos = [StoryboardKit_WeakWrapper]() 20 | 21 | /** 22 | Default init 23 | 24 | - parameter className: name of the View class. If nil defaults to UIView. 25 | 26 | - returns: A new ViewClassInfo instance 27 | */ 28 | required public init(className : String?) { 29 | super.init(className: className) 30 | } 31 | 32 | /** 33 | Add a View instance of this class 34 | 35 | - parameter instanceInfo: Instance Info of a View of this class 36 | */ 37 | func add(instanceInfo instanceInfo : ViewInstanceInfo) { 38 | // TODO: prevent duplicates 39 | self.instanceInfos.append( StoryboardKit_WeakWrapper(instanceInfo) ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /StoryboardKit/ClassInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClassInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/29/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Class 12 | public class ClassInfo: NSObject { 13 | public class var defaultClass : String { return "" } 14 | 15 | /// Name of the Class 16 | public let infoClassName : String 17 | 18 | /** 19 | Default init 20 | 21 | - parameter className: name of the Class. If nil defaults to ClassInfo.defaultClass. 22 | 23 | - returns: A new ClassInfo instance 24 | */ 25 | required public init(className : String?) { 26 | 27 | var useClassName : String 28 | if className != nil 29 | { 30 | useClassName = className! 31 | } else 32 | { 33 | useClassName = self.dynamicType.defaultClass 34 | } 35 | 36 | self.infoClassName = useClassName 37 | 38 | super.init() 39 | } 40 | } 41 | 42 | extension ClassInfo /*: CustomDebugStringConvertible*/ { 43 | 44 | /// Debug Description 45 | public override var debugDescription: String { 46 | var result = super.debugDescription 47 | 48 | result += "Class: \(self.infoClassName)" 49 | 50 | return result 51 | } 52 | } -------------------------------------------------------------------------------- /StoryboardKit/ViewControllers/NavigationControllerInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationControllerInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/1/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Navigation Controller Instance that is used in your application and its storyboards 12 | public class NavigationControllerInstanceInfo: ViewControllerInstanceInfo { 13 | /// Scene Member Id 14 | public let sceneMemberId : String? 15 | 16 | /// Segue to Root View 17 | public var root : SegueInstanceInfo? 18 | 19 | /** 20 | Default init 21 | 22 | - parameter classInfo: Class 23 | - parameter id: Storyboard Id 24 | - parameter storyboardIdentifier: Storyboard Identifier 25 | - parameter sceneMemberId: Scene Member Id 26 | - parameter root: Segue to Root View 27 | 28 | - returns: A new instance. 29 | */ 30 | init(classInfo : NavigationControllerClassInfo, id : String, storyboardIdentifier : String?, sceneMemberId : String?/*, root : SegueInstanceInfo?*/) { 31 | 32 | self.sceneMemberId = sceneMemberId 33 | // self.root = root 34 | 35 | super.init(classInfo: classInfo, id: id, storyboardIdentifier: storyboardIdentifier, view: nil) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /StoryboardKit/ViewControllers/ViewControllerClassInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewControllerClassInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/3/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | * Represents a View Controller Class that is used in your application and its storyboards 13 | */ 14 | public class ViewControllerClassInfo: ClassInfo { 15 | 16 | override public class var defaultClass : String { return "UIViewController" } 17 | 18 | /// All instances of this class in the application 19 | public private(set) var instanceInfos = Array< StoryboardKit_WeakWrapper >() 20 | 21 | /** 22 | Default init 23 | 24 | - parameter className: name of the View Controller class. If nil defaults to UIViewController 25 | 26 | - returns: A new ViewControllerClassInfo instance 27 | */ 28 | required public init(className : String?) { 29 | super.init(className: className) 30 | } 31 | 32 | /** 33 | Add a View Controller instance of this class 34 | 35 | - parameter instanceInfo: Instance Info of a View Controller of this class 36 | */ 37 | func add(instanceInfo instanceInfo : ViewControllerInstanceInfo) { 38 | // TODO: prevent duplicates 39 | self.instanceInfos.append( StoryboardKit_WeakWrapper(instanceInfo) ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /StoryboardKit.xcodeproj/project.xcworkspace/xcshareddata/StoryboardKit.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "E9C0A5D7FC17CA9A15245CD4B5C2BFCBC98368D6", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "EB2210CFD48672E403BED699D5D7F01B844069CF" : 0, 8 | "E9C0A5D7FC17CA9A15245CD4B5C2BFCBC98368D6" : 0 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "1D91BBBC-D400-40A8-B88C-AC3B2BCD0F6E", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "EB2210CFD48672E403BED699D5D7F01B844069CF" : "StoryboardKit\/Libraries\/SWXMLHash\/", 13 | "E9C0A5D7FC17CA9A15245CD4B5C2BFCBC98368D6" : "StoryboardKit\/" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "StoryboardKit", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "StoryboardKit.xcodeproj", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:Adorkable\/StoryboardKit.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E9C0A5D7FC17CA9A15245CD4B5C2BFCBC98368D6" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/drmohundro\/SWXMLHash.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "EB2210CFD48672E403BED699D5D7F01B844069CF" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /StoryboardKit/Utility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utility.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/3/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // TODO: organize appropriately, evaluate need of each 12 | 13 | // http://stackoverflow.com/a/24128121/96153 14 | /// Weak wrapper used for permanent container members that shouldn't be owners 15 | public class StoryboardKit_WeakWrapper { 16 | 17 | /// Wrapped value 18 | public weak var value : T? 19 | init (_ value: T) { 20 | self.value = value 21 | } 22 | } 23 | 24 | extension StoryboardKit_WeakWrapper : CustomDebugStringConvertible { 25 | 26 | /// Debug Description 27 | public var debugDescription: String { 28 | var result : String 29 | if let value = self.value 30 | { 31 | result = "Weak Wrapper<\(value.dynamicType)>" 32 | if let debugStringConvertable = value as? CustomDebugStringConvertible 33 | { 34 | result = result + " \(debugStringConvertable.debugDescription)" 35 | } else 36 | { 37 | result = result + " \(value)" 38 | } 39 | } else 40 | { 41 | result = "Weak Wrapper<\(self.value.dynamicType) nil" 42 | } 43 | return result 44 | } 45 | } 46 | 47 | func classWithClassName(className : String, objects : [T]) -> T? { 48 | return objects.filter( { $0.infoClassName == className } ).first 49 | } 50 | 51 | protocol Idable { 52 | var id : String { get } 53 | } 54 | 55 | func objectsWithId(id : String, objects : [T]) -> [T] { 56 | return objects.filter({ $0.id == id }) 57 | } 58 | 59 | func firstObject(objects : [T]) -> T? { 60 | var result : T? 61 | 62 | if objects.count > 0 { 63 | result = objects[0] 64 | } 65 | 66 | return result 67 | } 68 | 69 | func firstObjectWithId(id : String, objects : [T]) -> T? { 70 | return firstObject( objectsWithId(id, objects: objects) ) 71 | } 72 | 73 | -------------------------------------------------------------------------------- /Cocoapod/StoryboardKit.xcodeproj/xcshareddata/xcschemes/CocoapodTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 16 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 40 | 41 | 42 | 43 | 49 | 50 | 52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /StoryboardKit/StoryboardFileVersionedParsers/3.0/StoryboardFile3_0Parser_Scenes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardFile3_0Parser_Scenes.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/30/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import SWXMLHash 12 | 13 | internal extension StoryboardFile3_0Parser { 14 | // MARK: Scenes 15 | 16 | internal func parseScenes(scenes : XMLIndexer, storyboardInstanceParseInfo : StoryboardInstanceParseInfo) { 17 | for scene in scenes.children { 18 | 19 | self.parseIndividualScene(scene, storyboardInstanceParseInfo: storyboardInstanceParseInfo) 20 | } 21 | } 22 | 23 | internal func parseIndividualScene(scene : XMLIndexer, storyboardInstanceParseInfo : StoryboardInstanceParseInfo) { 24 | 25 | if let element = scene.element, let sceneId = element.allAttributes["sceneID"]?.text 26 | { 27 | let sceneInfo = StoryboardInstanceInfo.SceneInfo(sceneId: sceneId) 28 | storyboardInstanceParseInfo.add(scene: sceneInfo) 29 | 30 | let objects = scene["objects"] 31 | for object in objects.children { 32 | if let sceneObjectElement = object.element 33 | { 34 | if sceneObjectElement.name == "viewController" || sceneObjectElement.name == "tableViewController" || sceneObjectElement.name == "collectionViewController" 35 | { 36 | // TODO: seperate out VC, TVC, CVC? 37 | self.parseViewController(object, sceneInfo: sceneInfo) 38 | } else if sceneObjectElement.name == "navigationController" 39 | { 40 | self.parseNavigationController(object, sceneInfo: sceneInfo) 41 | } else if sceneObjectElement.name == "tabBarController" 42 | { 43 | self.parseTabBarController(object, sceneInfo: sceneInfo) 44 | } else 45 | { 46 | // TODO: placeholder 47 | self.Log("Skipping unsupported scene object \(sceneObjectElement.name)") 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /StoryboardKitTests/StoryboardFileParserTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardFileParserTests.swift 3 | // StoryboardKitTests 4 | // 5 | // Created by Ian on 5/31/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import StoryboardKit 12 | 13 | class StoryboardFileParserTests: XCTestCase { 14 | var applicationInfo : ApplicationInfo? 15 | 16 | override func setUp() { 17 | self.continueAfterFailure = false 18 | 19 | super.setUp() 20 | 21 | applicationInfo = ApplicationInfo() 22 | } 23 | 24 | func testCannotFindStoryboardFile() { 25 | do 26 | { 27 | try StoryboardFileParser.parse(applicationInfo!, pathFileName: "asldkfjlkajdslfkajsldfkjalsdfkjlaskdjflaskjdfa.alskdjflksjf") 28 | 29 | XCTAssert(false, "Expected parse to throw an error") 30 | } catch let error as NSError 31 | { 32 | XCTAssertNotNil(error, "Expected parse to throw an error: \(error)") 33 | } 34 | } 35 | 36 | func testParseFinishes() { 37 | do 38 | { 39 | try StoryboardFileParser.parse(applicationInfo!, pathFileName: storyboardPathBuilder()! ) 40 | } catch let error as NSError 41 | { 42 | XCTAssertNil(error, "Expected parse to not throw an error: \(error)") 43 | } 44 | 45 | XCTAssert(true, "Pass") 46 | } 47 | 48 | func testParseFinishedWithStoryboardInstanceInfo() { 49 | var result : StoryboardFileParser.ParseResult 50 | do 51 | { 52 | result = try StoryboardFileParser.parse(applicationInfo!, pathFileName: storyboardPathBuilder()! ) 53 | } catch let error as NSError 54 | { 55 | XCTAssertNil(error, "Expected parse to not throw an error: \(error)") 56 | } 57 | 58 | XCTAssertNotNil(result.0, "returned StoryboardInstanceInfo reference is nil") 59 | } 60 | 61 | func testParseFinishesWithoutErrors() { 62 | do 63 | { 64 | try StoryboardFileParser.parse(applicationInfo!, pathFileName: storyboardPathBuilder()! ) 65 | } catch let error as NSError 66 | { 67 | XCTAssertNil(error, "Expected parse to not throw an error: \(error)") 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /StoryboardKit/Views/TableViewInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian Grossberg on 8/26/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Table View Instance 12 | public class TableViewInstanceInfo: ViewInstanceInfo { 13 | 14 | /// Represents a Table View Cell Prototype 15 | public class TableViewCellPrototypeInfo { 16 | /// Storyboard Id 17 | public let id : String 18 | 19 | /// Reuse Identifier 20 | public let reuseIdentifier : String? 21 | 22 | /** 23 | Default init 24 | 25 | - parameter id: Storyboard Id 26 | - parameter reuseIdentifier: Reuse Identifier 27 | 28 | - returns: A new TableViewCellPrototypeInfo instance 29 | */ 30 | init(id : String, reuseIdentifier : String?) { 31 | self.id = id 32 | 33 | self.reuseIdentifier = reuseIdentifier 34 | } 35 | } 36 | 37 | /// All Cell Prototypes contained in this Table View Instance 38 | public let cellPrototypes : [TableViewCellPrototypeInfo]? 39 | 40 | /** 41 | Default init 42 | 43 | - parameter classInfo: Class 44 | - parameter id: Storyboard Id 45 | - parameter frame: Frame 46 | - parameter autoResizingMaskWidthSizable: Autoresizing Mask Width Sizable 47 | - parameter autoResizingMaskHeightSizable: Autoresizing Mask Height Sizable 48 | - parameter subviews: Subviews 49 | - parameter backgroundColor: Background Color 50 | - parameter cellPrototypes: Cell Prototypes 51 | 52 | - returns: A new CollectionViewInstanceInfo instance 53 | */ 54 | init(classInfo: ViewClassInfo, id: String, frame: CGRect?, autoResizingMaskWidthSizable: Bool, autoResizingMaskHeightSizable: Bool, subviews: [ViewInstanceInfo]?, backgroundColor: NSColor?, cellPrototypes : [TableViewCellPrototypeInfo]?) { 55 | 56 | self.cellPrototypes = cellPrototypes 57 | 58 | super.init(classInfo: classInfo, id: id, frame: frame, autoResizingMaskWidthSizable: autoResizingMaskWidthSizable, autoResizingMaskHeightSizable: autoResizingMaskHeightSizable, subviews: subviews, backgroundColor: backgroundColor) 59 | } 60 | } -------------------------------------------------------------------------------- /StoryboardKit/StoryboardInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/3/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import SWXMLHash 12 | 13 | /** 14 | * Represents a Storyboard file instance 15 | */ 16 | public class StoryboardInstanceInfo: NSObject { 17 | 18 | /// Use Autolayout 19 | public let useAutolayout : Bool 20 | 21 | /// Use Trait Collections 22 | public let useTraitCollections : Bool 23 | 24 | /// Initial View Controller Instance 25 | public let initialViewController : ViewControllerInstanceInfo? 26 | 27 | /// All scenes in the storyboard 28 | public private(set) var scenes = Array() 29 | 30 | /** 31 | Default init 32 | 33 | - parameter useAutolayout: Use Autolayout 34 | - parameter useTraitCollections: Use Trait Collections 35 | - parameter initialViewController: Initial View Controller Instance 36 | 37 | - returns: A new instance. 38 | */ 39 | public required init(useAutolayout : Bool, useTraitCollections : Bool, initialViewController : ViewControllerInstanceInfo?) { 40 | 41 | self.useAutolayout = useAutolayout 42 | self.useTraitCollections = useTraitCollections 43 | self.initialViewController = initialViewController 44 | 45 | super.init() 46 | } 47 | 48 | /** 49 | * Represents a Scene in the storyboard 50 | */ 51 | public class SceneInfo: NSObject { 52 | 53 | /// Scene Id 54 | let sceneId : String 55 | 56 | // TODO: not optional, use parsing placeholder 57 | /// View Controller 58 | public var controller : ViewControllerInstanceInfo? 59 | 60 | /// Placeholder object 61 | public var placeHolder : AnyObject? 62 | 63 | /** 64 | Default init 65 | 66 | - parameter sceneId: Scene Id 67 | 68 | - returns: A new instance. 69 | */ 70 | init(sceneId : String) { 71 | self.sceneId = sceneId 72 | 73 | super.init() 74 | } 75 | } 76 | 77 | /** 78 | Add a Scene to the storyboard 79 | 80 | - parameter scene: Scene to add 81 | */ 82 | func add(scene scene : SceneInfo) { 83 | // TODO: validate that it isn't a dup 84 | self.scenes.append(scene) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /StoryboardKit/Views/ViewInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/29/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a View Instance 12 | public class ViewInstanceInfo: NSObject { 13 | /// Class 14 | public let classInfo : ViewClassInfo 15 | 16 | /// Storyboard id 17 | public let id : String 18 | 19 | /// Frame 20 | public let frame : CGRect? // TODO: should this be non-optional? 21 | 22 | /// Autoresizing Mask Width Sizable 23 | public let autoResizingMaskWidthSizable : Bool 24 | 25 | /// Autoresizing Mask Height Sizable 26 | public let autoResizingMaskHeightSizable : Bool 27 | 28 | /// Subviews 29 | public let subviews : [ViewInstanceInfo]? 30 | 31 | /// Background Color 32 | public let backgroundColor : NSColor? 33 | 34 | /** 35 | Default init 36 | 37 | - parameter classInfo: Class 38 | - parameter id: Storyboard Id 39 | - parameter frame: frame 40 | - parameter autoResizingMaskWidthSizable: Autoresizing Mask Width Sizable 41 | - parameter autoResizingMaskHeightSizable: Autoresizing Mask Height Sizable 42 | - parameter subviews: Subviews 43 | - parameter backgroundColor: Background Color 44 | 45 | - returns: A new instance. 46 | */ 47 | init(classInfo : ViewClassInfo, id : String, frame : CGRect?, autoResizingMaskWidthSizable : Bool, autoResizingMaskHeightSizable : Bool, subviews : [ViewInstanceInfo]?, backgroundColor : NSColor?) { 48 | self.classInfo = classInfo 49 | self.id = id 50 | 51 | self.frame = frame 52 | 53 | self.autoResizingMaskWidthSizable = autoResizingMaskWidthSizable 54 | self.autoResizingMaskHeightSizable = autoResizingMaskHeightSizable 55 | 56 | self.subviews = subviews 57 | 58 | self.backgroundColor = backgroundColor 59 | 60 | super.init() 61 | 62 | self.classInfo.add(instanceInfo: self) 63 | } 64 | } 65 | 66 | 67 | extension ViewInstanceInfo /*: CustomDebugStringConvertible*/ { 68 | 69 | /// Debug Description 70 | override public var debugDescription : String { 71 | return super.debugDescription + "Id: \(self.id)\(self.classInfo.debugDescription)" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /StoryboardKit/ViewControllers/ViewControllerInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewControllerInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/3/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a View Controller Instance that is used in your application and its storyboards 12 | public class ViewControllerInstanceInfo: NSObject, Idable { 13 | /// Class 14 | public let classInfo : ViewControllerClassInfo 15 | 16 | /// Storyboard Id 17 | public let id : String 18 | 19 | /// Storyboard Identifier 20 | public let storyboardIdentifier : String? 21 | 22 | /// Segues 23 | public private(set) var segues = Array< SegueInstanceInfo >() 24 | 25 | /// Layout Guides 26 | public private(set) var layoutGuides = Array< ViewControllerLayoutGuideInstanceInfo >() 27 | 28 | /// Navigation Items 29 | public private(set) var navigationItems = Array< NavigationItemInstanceInfo >() 30 | 31 | /// View 32 | public let view : ViewInstanceInfo? 33 | 34 | /** 35 | Default Init 36 | 37 | - parameter classInfo: Class 38 | - parameter id: Storyboard Id 39 | - parameter storyboardIdentifier: Storyboard Identifier 40 | - parameter view: View 41 | 42 | - returns: A new instance. 43 | */ 44 | init(classInfo : ViewControllerClassInfo, id : String, storyboardIdentifier : String?, view : ViewInstanceInfo?) { 45 | self.classInfo = classInfo 46 | self.id = id 47 | 48 | self.storyboardIdentifier = storyboardIdentifier 49 | 50 | self.view = view 51 | 52 | super.init() 53 | 54 | self.classInfo.add(instanceInfo: self) 55 | } 56 | 57 | func add(segue segue : SegueInstanceInfo) { 58 | self.segues.append(segue) 59 | } 60 | 61 | func add(layoutGuide layoutGuide : ViewControllerLayoutGuideInstanceInfo) { 62 | self.layoutGuides.append(layoutGuide) 63 | } 64 | 65 | func add(navigationItem navigationItem : NavigationItemInstanceInfo) { 66 | self.navigationItems.append(navigationItem) 67 | } 68 | } 69 | 70 | extension ViewControllerInstanceInfo /*: CustomDebugStringConvertible*/ { 71 | /// Debug Description 72 | override public var debugDescription : String { 73 | return super.debugDescription + "Id: \(self.id)\(self.classInfo.debugDescription)" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /StoryboardKit/Views/CollectionViewInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian Grossberg on 10/30/15. 6 | // Copyright © 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Represents a Collection View Instance 12 | public class CollectionViewInstanceInfo: ViewInstanceInfo { 13 | 14 | /// Represents a Collection View Cell Prototype 15 | public class CollectionViewCellPrototypeInfo { 16 | /// Storyboard Id 17 | public let id : String 18 | 19 | /// Reuse Identifier 20 | public let reuseIdentifier : String? 21 | 22 | /** 23 | Default init 24 | 25 | - parameter id: Storyboard Id 26 | - parameter reuseIdentifier: Reuse Identifier 27 | 28 | - returns: A new CollectionViewCellPrototypeInfo instance 29 | */ 30 | init(id : String, reuseIdentifier : String?) { 31 | self.id = id 32 | 33 | self.reuseIdentifier = reuseIdentifier 34 | } 35 | } 36 | 37 | /// All Cell Prototypes contained in this Collection View Instance 38 | public let cellPrototypes : [CollectionViewCellPrototypeInfo]? 39 | 40 | /** 41 | Default init 42 | 43 | - parameter classInfo: Class 44 | - parameter id: Storyboard Id 45 | - parameter frame: Frame 46 | - parameter autoResizingMaskWidthSizable: Autoresizing Mask Width Sizable 47 | - parameter autoResizingMaskHeightSizable: Autoresizing Mask Height Sizable 48 | - parameter subviews: Subviews 49 | - parameter backgroundColor: Background Color 50 | - parameter cellPrototypes: Cell Prototypes 51 | 52 | - returns: A new CollectionViewInstanceInfo instance 53 | */ 54 | init(classInfo: ViewClassInfo, id: String, frame: CGRect?, autoResizingMaskWidthSizable: Bool, autoResizingMaskHeightSizable: Bool, subviews: [ViewInstanceInfo]?, backgroundColor: NSColor?, cellPrototypes : [CollectionViewCellPrototypeInfo]?) { 55 | 56 | self.cellPrototypes = cellPrototypes 57 | 58 | super.init(classInfo: classInfo, id: id, frame: frame, autoResizingMaskWidthSizable: autoResizingMaskWidthSizable, autoResizingMaskHeightSizable: autoResizingMaskHeightSizable, subviews: subviews, backgroundColor: backgroundColor) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /StoryboardKit/StoryboardFileVersionedParsers/3.0/StoryboardFile3_0Parser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardFile3_0Parser.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/31/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import SWXMLHash 12 | 13 | /// Parser for Storyboard File Format 3.0 14 | internal class StoryboardFile3_0Parser: NSObject, StoryboardFileVersionedParser { 15 | 16 | static func supports(root: XMLIndexer) -> Bool { 17 | var result : Bool 18 | 19 | if let version = root["document"].element?.allAttributes["version"]?.text 20 | { 21 | if version == "3.0" 22 | { 23 | result = true 24 | } else 25 | { 26 | result = false 27 | } 28 | } else 29 | { 30 | result = false 31 | } 32 | 33 | return result 34 | } 35 | 36 | static func parse(indexer: XMLIndexer, applicationInfo: ApplicationInfo) throws -> StoryboardFileParser.ParseResult { 37 | let parser = StoryboardFile3_0Parser(applicationInfo: applicationInfo) 38 | return try parser.parse(indexer) 39 | } 40 | 41 | internal let applicationInfo : ApplicationInfo 42 | 43 | internal var storyboardInstanceParseInfo : StoryboardInstanceParseInfo? 44 | 45 | internal var parsedSegues = [SegueInstanceParseInfo]() 46 | 47 | internal var logs : [String]? 48 | internal func Log(message : String) { 49 | if logs == nil 50 | { 51 | logs = [String]() 52 | } 53 | logs?.append(message) 54 | } 55 | 56 | required init(applicationInfo : ApplicationInfo) 57 | { 58 | self.applicationInfo = applicationInfo 59 | 60 | super.init() 61 | } 62 | 63 | func parse(indexer : XMLIndexer) throws -> StoryboardFileParser.ParseResult { 64 | var result : StoryboardFileParser.ParseResult 65 | 66 | self.storyboardInstanceParseInfo = self.createStoryboardInstance(indexer) 67 | 68 | if let storyboardInstanceParseInfo = self.storyboardInstanceParseInfo 69 | { 70 | self.parseScenes(indexer["document"]["scenes"], storyboardInstanceParseInfo: storyboardInstanceParseInfo) 71 | 72 | self.createSegueInstanceInfosFromParsed() 73 | 74 | result = try self.createStoryboardInstanceInfoFromParsed() 75 | } 76 | 77 | return result 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Tools/matchVersiontoBranch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $3 == '1' ] 4 | then 5 | branch=$(git rev-parse --abbrev-ref HEAD) 6 | if [[ $branch == release/* ]] 7 | then 8 | if [[ $branch =~ release/([0-9.]*)$ ]] 9 | then 10 | version=${BASH_REMATCH[1]} 11 | else 12 | exit 0 13 | fi 14 | else 15 | exit 0 16 | fi 17 | else 18 | exit 0 19 | fi 20 | 21 | root='./' 22 | 23 | plistFiles=("StoryboardKit/Info.plist") 24 | podspecFile="StoryboardKit.podspec" 25 | 26 | echo "Set Version to $version, using root $root" 27 | 28 | function updatePlist() 29 | { 30 | if [ -z "$1" ] 31 | then 32 | echo "updatePlist() expects a version for first parameter" >&2 33 | return -1 34 | fi 35 | version=$1 36 | 37 | if [ -z "$2" ] 38 | then 39 | echo "updatePlist() expects a file name for second parameter" >&2 40 | return -1 41 | fi 42 | plistFile=$2 43 | 44 | # TODO: support both PlistBuddy and plutil, test for which available 45 | #PlistBuddy "$2" Set "CFBundleShortVersionString" "$1" 46 | if plutil -replace "CFBundleShortVersionString" -string "$version" "$root/$plistFile" 47 | then 48 | echo "Updated plist file $plistFile to version $version" 49 | else 50 | return -1 51 | fi 52 | 53 | return 0 54 | } 55 | 56 | function updatePlists() { 57 | if [ -z "$1" ] 58 | then 59 | echo "updatePlists() expects a version for first parameter" 60 | return -1 61 | fi 62 | version=$1 63 | 64 | if [ -z ="$2" ] 65 | then 66 | echo "updatePlists() expects a list of PList files for second parameter" 67 | return -1 68 | fi 69 | plistFiles=$2 70 | 71 | for plistFile in ${plistFiles[@]} 72 | do 73 | if !(updatePlist "$version" "$plistFile") 74 | then 75 | echo "Error while updatingPlist($version, $plistFile)" >&2 76 | exit -1 77 | fi 78 | done 79 | } 80 | 81 | function updatePodspec() 82 | { 83 | if [ -z "$1" ] 84 | then 85 | echo "updatePodspec() expects a version for first parameter" 86 | return -1 87 | fi 88 | version=$1 89 | 90 | if [ -z ="$2" ] 91 | then 92 | echo "updatePodspec() expects a Podspec file for second parameter" 93 | return -1 94 | fi 95 | podspecFile=$2 96 | 97 | search="s/s.version = \"[0-9.]*\"/s.version = \"$version\"/g" 98 | if sed -i -e "$search" "$root/$podspecFile" 99 | then 100 | echo "Updated podspec file $podspecFile to version $version" 101 | else 102 | return -1 103 | fi 104 | return 0 105 | } 106 | 107 | if !(updatePlists "$version" "$plistFiles") 108 | then 109 | echo "Error while updatePlists($version, $plistFiles)" >&2 110 | exit -1 111 | fi 112 | 113 | if !(updatePodspec "$version" "$podspecFile") 114 | then 115 | echo "Error while updatePodspec($version, $podspecFile)" >&2 116 | exit -1 117 | fi 118 | -------------------------------------------------------------------------------- /StoryboardKitTests/TableViewInstanceInfoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian Grossberg on 8/26/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import StoryboardKit 12 | 13 | class TableViewInstanceInfoTests: XCTestCase { 14 | 15 | var applicationInfo : ApplicationInfo? 16 | 17 | var tableViewInstanceInfoId = "BYg-eK-ujo" 18 | var tableViewInstanceInfo : TableViewInstanceInfo? 19 | 20 | override func setUp() { 21 | self.continueAfterFailure = false 22 | 23 | super.setUp() 24 | 25 | applicationInfo = ApplicationInfo() 26 | 27 | do 28 | { 29 | try StoryboardFileParser.parse(applicationInfo!, pathFileName: storyboardPathBuilder()! ) 30 | } catch let error as NSError 31 | { 32 | XCTAssertNil(error, "Expected parse to not throw an error: \(error)") 33 | } 34 | 35 | if let tableViewInstanceInfo = applicationInfo?.viewInstanceWithId(self.tableViewInstanceInfoId) as? TableViewInstanceInfo 36 | { 37 | self.tableViewInstanceInfo = tableViewInstanceInfo 38 | } 39 | } 40 | 41 | func testClassInfo() { 42 | let className = "UITableView" 43 | let classInfo = self.applicationInfo?.viewClassWithClassName(className) 44 | 45 | XCTAssertNotNil(self.tableViewInstanceInfo, "Unable to retrieve ViewInstanceInfo with id \(self.tableViewInstanceInfoId)") 46 | 47 | XCTAssertNotNil(classInfo, "\(self.tableViewInstanceInfo!)'s classInfo should not be nil") 48 | 49 | XCTAssertEqual(self.tableViewInstanceInfo!.classInfo, classInfo!, "\(self.tableViewInstanceInfo!)'s classInfo should be equal to \(classInfo!)") 50 | } 51 | 52 | func testCellPrototypes() { 53 | XCTAssertNotNil(self.tableViewInstanceInfo, "Unable to retrieve ViewInstanceInfo with id \(self.tableViewInstanceInfoId)") 54 | 55 | XCTAssertNotNil(self.tableViewInstanceInfo!.cellPrototypes, "\(self.tableViewInstanceInfo!) should contain cell prototypes") 56 | XCTAssertGreaterThan(self.tableViewInstanceInfo!.cellPrototypes!.count, 0, "\(self.tableViewInstanceInfo!)'s cell prototypes count should be greater than 0") 57 | XCTAssertNotNil(self.tableViewInstanceInfo!.cellPrototypes![0].reuseIdentifier, "\(self.tableViewInstanceInfo!.cellPrototypes![0])'s reuseIdentifier should not be nil") 58 | 59 | let reuseIdentifier = "I'm a Table Cell" 60 | XCTAssertEqual(self.tableViewInstanceInfo!.cellPrototypes![0].reuseIdentifier!, reuseIdentifier, "\(self.tableViewInstanceInfo!.cellPrototypes![0])'s reuseIdentifier should be \"\(reuseIdentifier)\"") 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /StoryboardKit/StoryboardFileParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardFileParser.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/3/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import SWXMLHash 12 | 13 | /** 14 | * Interface for implementing a Version Specific Storyboard File Parser 15 | */ 16 | protocol StoryboardFileVersionedParser { 17 | static func supports(root : XMLIndexer) -> Bool 18 | static func parse(indexer : XMLIndexer, applicationInfo : ApplicationInfo) throws -> StoryboardFileParser.ParseResult 19 | } 20 | 21 | /** 22 | * Parses storyboard files 23 | */ 24 | public class StoryboardFileParser: NSObject { 25 | /** 26 | * Result value after parsing a Storyboard file 27 | */ 28 | public typealias ParseResult = (StoryboardInstanceInfo?, [String]?) 29 | 30 | /** 31 | Main parsing function 32 | 33 | - parameter applicationInfo: The applicationInfo instance you wish to fill 34 | - parameter pathFileName: The path to the Storyboard file 35 | 36 | - returns: A StoryboardInstanceInfo that represents the parsed Storyboard file and/or an error, and/or any verbose feedback, any of which non-nil or nil depending on the parsing results 37 | */ 38 | public class func parse(applicationInfo : ApplicationInfo, pathFileName : String) throws -> ParseResult { 39 | var result : ParseResult 40 | 41 | if NSFileManager.defaultManager().fileExistsAtPath(pathFileName) { 42 | 43 | if let data = NSData(contentsOfFile: pathFileName) 44 | { 45 | let indexer = SWXMLHash.parse(data) 46 | result = try self.parseXML(indexer, applicationInfo: applicationInfo) 47 | } else 48 | { 49 | throw NSError(domain: "Unable to open Storyboard file \(pathFileName)", code: 0, userInfo: nil) 50 | } 51 | } else 52 | { 53 | throw NSError(domain: "Unable to find Storyboard file \(pathFileName)", code: 0, userInfo: nil) 54 | } 55 | 56 | return result 57 | } 58 | 59 | internal static let versionedParserClasses : [StoryboardFileVersionedParser.Type] = [StoryboardFile3_0Parser.self] 60 | 61 | internal class func parseXML(indexer : XMLIndexer, applicationInfo : ApplicationInfo) throws -> ParseResult { 62 | var result : ParseResult 63 | 64 | // TODO: fix to use versionsedParserClasses 65 | if StoryboardFile3_0Parser.supports(indexer) 66 | { 67 | result = try StoryboardFile3_0Parser.parse(indexer, applicationInfo: applicationInfo) 68 | } else 69 | { 70 | throw NSError(domain: "Unsupported Storyboard file format version: \(version)", code: 0, userInfo: nil) 71 | } 72 | 73 | return result 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | # Use this file to configure the Overcommit hooks you wish to use. This will 2 | # extend the default configuration defined in: 3 | # https://github.com/brigade/overcommit/blob/master/config/default.yml 4 | # 5 | # At the topmost level of this YAML file is a key representing type of hook 6 | # being run (e.g. pre-commit, commit-msg, etc.). Within each type you can 7 | # customize each hook, such as whether to only run it on certain files (via 8 | # `include`), whether to only display output if it fails (via `quiet`), etc. 9 | # 10 | # For a complete list of hooks, see: 11 | # https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook 12 | # 13 | # For a complete list of options that you can use to customize hooks, see: 14 | # https://github.com/brigade/overcommit#configuration 15 | # 16 | # Uncomment the following lines to make the configuration take effect. 17 | 18 | #PreCommit: 19 | # RuboCop: 20 | # enabled: true 21 | # on_warn: fail # Treat all warnings as failures 22 | # 23 | # TrailingWhitespace: 24 | # exclude: 25 | # - '**/db/structure.sql' # Ignore trailing whitespace in generated files 26 | # 27 | #PostCheckout: 28 | # ALL: # Special hook name that customizes all hooks of this type 29 | # quiet: true # Change all post-checkout hooks to only display output on failure 30 | # 31 | # IndexTags: 32 | # enabled: true # Generate a tags file with `ctags` each time HEAD changes 33 | 34 | CommitMsg: 35 | ALL: 36 | enabled: false 37 | EmptyMessage: 38 | enabled: true 39 | HardTabs: 40 | enabled: true 41 | 42 | PostCheckout: 43 | ALL: 44 | enabled: false 45 | BundleInstall: 46 | enabled: true 47 | SubmoduleStatus: 48 | enabled: true 49 | MatchVersionToBranch: 50 | enabled: true 51 | required_executable: './Tools/matchVersiontoBranch.sh' 52 | 53 | PostCommit: 54 | ALL: 55 | enabled: false 56 | BundleInstall: 57 | enabled: true 58 | SubmoduleStatus: 59 | enabled: true 60 | GitGuilt: 61 | enabled: true 62 | 63 | PostMerge: 64 | ALL: 65 | enabled: false 66 | BundleInstall: 67 | enabled: true 68 | SubmoduleStatus: 69 | enabled: true 70 | 71 | PostRewrite: 72 | ALL: 73 | enabled: false 74 | BundleInstall: 75 | enabled: true 76 | SubmoduleStatus: 77 | enabled: true 78 | 79 | PreCommit: 80 | ALL: 81 | enabled: false 82 | AuthorEmail: 83 | enabled: true 84 | BrokenSymlinks: 85 | enabled: true 86 | # BundleCheck: 87 | # enabled: true 88 | CaseConflicts: 89 | enabled: true 90 | GoLint: 91 | enabled: true 92 | GoVet: 93 | enabled: true 94 | JsonSyntax: 95 | enabled: true 96 | LocalPathsInGemfile: 97 | enabled: true 98 | MergeConflicts: 99 | enabled: true 100 | TravisLint: 101 | enabled: true 102 | 103 | PrePush: 104 | ALL: 105 | enabled: false 106 | 107 | PreRebase: 108 | ALL: 109 | enabled: false 110 | 111 | -------------------------------------------------------------------------------- /StoryboardKit/SegueInstanceInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SegueInstanceInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/3/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // TODO: should we keep it weak? should we be using the weak attribute? 12 | public typealias SegueConnection = StoryboardKit_WeakWrapper 13 | 14 | /// Represents a Segue Instance used in your application and its storyboards 15 | public class SegueInstanceInfo: NSObject, Idable { 16 | 17 | /// Class 18 | public let classInfo : SegueClassInfo 19 | 20 | /// Storyboard Id 21 | public let id : String 22 | 23 | /// Source 24 | public let source : SegueConnection 25 | 26 | /// Destination 27 | public let destination : SegueConnection 28 | 29 | /// Kind of Segue 30 | public let kind : String? 31 | 32 | /// Identifier 33 | public let identifier : String? 34 | 35 | /** 36 | Default init 37 | 38 | - parameter classInfo: Class 39 | - parameter id: Storyboard Id 40 | - parameter source: Source 41 | - parameter destination: Destination 42 | - parameter kind: Kind of Segue 43 | - parameter identifier: Identifier 44 | 45 | - returns: A new instance. 46 | */ 47 | public init(classInfo : SegueClassInfo, id : String, source : SegueConnection, destination : SegueConnection, kind : String?, identifier : String?) { 48 | self.classInfo = classInfo 49 | self.id = id 50 | self.source = source 51 | self.destination = destination 52 | self.kind = kind 53 | self.identifier = identifier 54 | 55 | super.init() 56 | 57 | self.classInfo.add(instanceInfo: self) 58 | } 59 | 60 | /** 61 | Convenience init: Destination as a View Controller Instance 62 | 63 | - parameter classInfo: Class 64 | - parameter id: Storyboard Id 65 | - parameter source: Source as SegueConnection 66 | - parameter destination: Destination as View Controller Instance 67 | - parameter kind: Kind of Segue 68 | - parameter identifier: Identifier 69 | 70 | - returns: A new instance. 71 | */ 72 | public convenience init(classInfo : SegueClassInfo, id : String, source : SegueConnection, destination : ViewControllerInstanceInfo, kind : String?, identifier : String?) { 73 | self.init(classInfo: classInfo, id: id, source: source, destination: StoryboardKit_WeakWrapper(destination), kind: kind, identifier: identifier) 74 | } 75 | } 76 | 77 | extension SegueInstanceInfo /*: CustomDebugStringConvertible*/ { 78 | 79 | /// Debug Description 80 | override public var debugDescription : String { 81 | get { 82 | var result = super.debugDescription 83 | 84 | result += "\n\(self.classInfo)" 85 | result += "\nId: \(self.id)" 86 | result += "\nSource: \(self.source.value)" 87 | result += "\nDestination: \(self.destination.value)" 88 | result += "\nKind: \(self.kind)" 89 | result += "\nIdentifier: \(self.identifier)" 90 | 91 | return result 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /StoryboardKitTests/ApplicationInfoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationInfoTests.swift 3 | // StoryboardKitTests 4 | // 5 | // Created by Ian on 5/31/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import StoryboardKit 12 | 13 | class ApplicationInfoTests: XCTestCase { 14 | var applicationInfo : ApplicationInfo? 15 | 16 | override func setUp() { 17 | super.setUp() 18 | 19 | applicationInfo = ApplicationInfo() 20 | 21 | do 22 | { 23 | try StoryboardFileParser.parse(applicationInfo!, pathFileName: storyboardPathBuilder()! ) 24 | } catch let error as NSError 25 | { 26 | XCTAssertNil(error, "Expected parse to not throw an error: \(error)") 27 | } 28 | } 29 | 30 | func testViewControllerClassWithClassName() { 31 | let className = "UIViewController" 32 | let classInfo = applicationInfo!.viewControllerClassWithClassName(className) 33 | XCTAssertNotNil(classInfo, "Expected a classInfo for '\(className)' class") 34 | } 35 | 36 | func testViewControllerInstanceWithInstanceId() { 37 | let id = "yrP-vr-uHE" 38 | let instanceInfo = applicationInfo?.viewControllerInstanceWithId(id) 39 | XCTAssertNotNil(instanceInfo, "Expected an instanceInfo for id '\(id)'") 40 | } 41 | 42 | func testViewControllerInstanceWithStoryboardIdentifier() { 43 | let storyboardIdentifier = "FirstInstance" 44 | let instanceInfo = applicationInfo?.viewControllerInstanceWithStoryboardIdentifier(storyboardIdentifier) 45 | XCTAssertNotNil(instanceInfo, "Expected an instanceInfo for storyboard identifier '\(storyboardIdentifier)'") 46 | } 47 | 48 | func testNavigationControllerInstanceWithId() { 49 | let id = "eGU-XO-Tph" 50 | let instanceInfo = applicationInfo?.navigationControllerInstanceWithId(id) 51 | XCTAssertNotNil(instanceInfo, "Expected an instanceInfo for id '\(id)'") 52 | } 53 | 54 | func testNavigationControllerInstanceWithStoryboardIdentifier() { 55 | let storyboardIdentifier = "Navigation" 56 | let instanceInfo = applicationInfo?.navigationControllerInstanceWithStoryboardIdentifier(storyboardIdentifier) 57 | XCTAssertNotNil(instanceInfo, "Expected an instanceInfo for storyboard identifier '\(storyboardIdentifier)'") 58 | } 59 | 60 | func testSegueClassWithClassName() { 61 | let className = "UIStoryboardSegue" 62 | let classInfo = applicationInfo?.segueClassWithClassName(className) 63 | XCTAssertNotNil(classInfo, "Expected a classInfo for '\(className)' class") 64 | } 65 | 66 | func testViewClassWithClassName() { 67 | let className = "UIView" 68 | let classInfo = applicationInfo?.viewClassWithClassName(className) 69 | XCTAssertNotNil(classInfo, "Expected a classInfo for '\(className)' class") 70 | } 71 | 72 | func testViewClassWithCustomClassName() { 73 | let className = "CustomButton" 74 | let classInfo = applicationInfo?.viewClassWithClassName(className) 75 | XCTAssertNotNil(classInfo, "Expected a classInfo for '\(className)' class") 76 | } 77 | 78 | func testViewInstanceWithId() { 79 | let id = "IKn-pG-61R" 80 | let instanceInfo = applicationInfo?.viewInstanceWithId(id) 81 | XCTAssertNotNil(instanceInfo, "Expected an instanceInfo for id '\(id)'") 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /StoryboardKitTests/StoryboardInstanceInfoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardInstanceInfoTests.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/29/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import StoryboardKit 12 | 13 | class StoryboardInstanceInfoTests: XCTestCase { 14 | var applicationInfo : ApplicationInfo? 15 | 16 | override func setUp() { 17 | self.continueAfterFailure = false 18 | 19 | super.setUp() 20 | 21 | applicationInfo = ApplicationInfo() 22 | } 23 | 24 | // TODO: test that false is being store 25 | func testUseAutolayout() { 26 | var result : StoryboardFileParser.ParseResult 27 | 28 | do 29 | { 30 | result = try StoryboardFileParser.parse(applicationInfo!, pathFileName: storyboardPathBuilder()! ) 31 | } catch let error as NSError 32 | { 33 | XCTAssertNil(error, "Expected parse to not throw an error: \(error)") 34 | } 35 | 36 | XCTAssertNotNil(result.0, "returned StoryboardInstanceInfo reference is nil") 37 | 38 | let storyboardInstanceInfo = result.0! 39 | 40 | XCTAssertTrue(storyboardInstanceInfo.useAutolayout, "Expected useAutolayout in \(storyboardInstanceInfo) to be true") 41 | } 42 | 43 | // TODO: test that false is being store 44 | func testUseTraitCollections() { 45 | var result : StoryboardFileParser.ParseResult 46 | 47 | do 48 | { 49 | result = try StoryboardFileParser.parse(applicationInfo!, pathFileName: storyboardPathBuilder()! ) 50 | } catch let error as NSError 51 | { 52 | XCTAssertNil(error, "Expected parse to not throw an error: \(error)") 53 | } 54 | XCTAssertNotNil(result.0, "returned StoryboardInstanceInfo reference is nil") 55 | 56 | let storyboardInstanceInfo = result.0! 57 | 58 | XCTAssertTrue(storyboardInstanceInfo.useTraitCollections, "Expected useTraitCollections in \(storyboardInstanceInfo) to be true") 59 | } 60 | 61 | func testInitialViewController() { 62 | var result : StoryboardFileParser.ParseResult 63 | 64 | do 65 | { 66 | result = try StoryboardFileParser.parse(applicationInfo!, pathFileName: storyboardPathBuilder()! ) 67 | } catch let error as NSError 68 | { 69 | XCTAssertNil(error, "Expected parse to not throw an error: \(error)") 70 | } 71 | 72 | XCTAssertNotNil(result.0, "returned StoryboardInstanceInfo reference is nil") 73 | 74 | let storyboardInstanceInfo = result.0! 75 | 76 | XCTAssertNotNil(storyboardInstanceInfo.initialViewController, "Expected StoryboardInstanceInfo to contain an initialViewController") 77 | 78 | let firstInstanceStoryboardIdentifier = "FirstInstance" 79 | let firstInstance = applicationInfo?.viewControllerInstanceWithStoryboardIdentifier(firstInstanceStoryboardIdentifier) 80 | XCTAssertNotNil(firstInstance, "Test Failure: Expected ApplicationInfo to contain a View Controller with Storyboard Identifier \(firstInstanceStoryboardIdentifier)") 81 | XCTAssertEqual(storyboardInstanceInfo.initialViewController!, firstInstance!, "Expected StoryboardInstanceInfo's initialViewController to be \(firstInstance!), instead it was \(storyboardInstanceInfo.initialViewController!)") 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [0.5.7](https://github.com/Adorkable/StoryboardKit/tree/0.5.7) (2016-08-09) 4 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.5.6...0.5.7) 5 | 6 | ## [0.5.6](https://github.com/Adorkable/StoryboardKit/tree/0.5.6) (2015-12-15) 7 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.5.5...0.5.6) 8 | 9 | **Implemented enhancements:** 10 | 11 | - Github Releases through Travis [\#15](https://github.com/Adorkable/StoryboardKit/issues/15) 12 | 13 | ## [0.5.5](https://github.com/Adorkable/StoryboardKit/tree/0.5.5) (2015-11-14) 14 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.5.4...0.5.5) 15 | 16 | **Implemented enhancements:** 17 | 18 | - Changelogs! [\#14](https://github.com/Adorkable/StoryboardKit/issues/14) 19 | 20 | ## [0.5.4](https://github.com/Adorkable/StoryboardKit/tree/0.5.4) (2015-11-14) 21 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.5.3...0.5.4) 22 | 23 | **Implemented enhancements:** 24 | 25 | - Parse View tree [\#12](https://github.com/Adorkable/StoryboardKit/issues/12) 26 | - appledocs/jazzy docs [\#7](https://github.com/Adorkable/StoryboardKit/issues/7) 27 | - Hook up to Travis CI [\#6](https://github.com/Adorkable/StoryboardKit/issues/6) 28 | - Do not require a StoryboardInstanceInfo to parse a storyboard file [\#4](https://github.com/Adorkable/StoryboardKit/issues/4) 29 | 30 | **Fixed bugs:** 31 | 32 | - fix Travis CI Test Failure [\#13](https://github.com/Adorkable/StoryboardKit/issues/13) 33 | 34 | ## [0.5.3](https://github.com/Adorkable/StoryboardKit/tree/0.5.3) (2015-11-03) 35 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.5.2...0.5.3) 36 | 37 | ## [0.5.2](https://github.com/Adorkable/StoryboardKit/tree/0.5.2) (2015-11-02) 38 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.5.1...0.5.2) 39 | 40 | ## [0.5.1](https://github.com/Adorkable/StoryboardKit/tree/0.5.1) (2015-11-02) 41 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.5.0...0.5.1) 42 | 43 | ## [0.5.0](https://github.com/Adorkable/StoryboardKit/tree/0.5.0) (2015-10-31) 44 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.4.1...0.5.0) 45 | 46 | ## [0.4.1](https://github.com/Adorkable/StoryboardKit/tree/0.4.1) (2015-10-15) 47 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.4.0...0.4.1) 48 | 49 | ## [0.4.0](https://github.com/Adorkable/StoryboardKit/tree/0.4.0) (2015-09-19) 50 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.3.2...0.4.0) 51 | 52 | ## [0.3.2](https://github.com/Adorkable/StoryboardKit/tree/0.3.2) (2015-09-11) 53 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.3.1...0.3.2) 54 | 55 | ## [0.3.1](https://github.com/Adorkable/StoryboardKit/tree/0.3.1) (2015-09-11) 56 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.3.0...0.3.1) 57 | 58 | ## [0.3.0](https://github.com/Adorkable/StoryboardKit/tree/0.3.0) (2015-08-26) 59 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.2.0...0.3.0) 60 | 61 | ## [0.2.0](https://github.com/Adorkable/StoryboardKit/tree/0.2.0) (2015-08-22) 62 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.1.1...0.2.0) 63 | 64 | ## [0.1.1](https://github.com/Adorkable/StoryboardKit/tree/0.1.1) (2015-06-24) 65 | [Full Changelog](https://github.com/Adorkable/StoryboardKit/compare/0.1.0...0.1.1) 66 | 67 | ## [0.1.0](https://github.com/Adorkable/StoryboardKit/tree/0.1.0) (2015-06-24) 68 | 69 | 70 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | StoryboardKit 2 | === 3 | [![Build Status](http://img.shields.io/travis/Adorkable/StoryboardKit.svg?branch=master&style=flat)](https://travis-ci.org/Adorkable/StoryboardKit) 4 | [![codecov.io](https://img.shields.io/codecov/c/github/Adorkable/StoryboardKit.svg)](http://codecov.io/github/Adorkable/StoryboardKit?branch=master) 5 | [![Pod Platform](http://img.shields.io/cocoapods/p/StoryboardKit.svg?style=flat)](http://cocoadocs.org/docsets/StoryboardKit/) 6 | [![Pod License](http://img.shields.io/cocoapods/l/StoryboardKit.svg?style=flat)](http://cocoadocs.org/docsets/StoryboardKit/) 7 | [![Pod Version](http://img.shields.io/cocoapods/v/StoryboardKit.svg?style=flat)](http://cocoadocs.org/docsets/StoryboardKit/) 8 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FAdorkable%2FStoryboardKit.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FAdorkable%2FStoryboardKit?ref=badge_shield) 9 | 10 | **StoryboardKit** a simple OSX library that tells you all you would want to know about storyboard files. 11 | 12 | Currently it supports iOS storyboards only. OSX storyboard support forthcoming! 13 | 14 | Installation 15 | --- 16 | --- 17 | **StoryboardKit** is available through **[cocoapods](http://cocoapods.org)**, to install simple add the following line to your `PodFile`: 18 | 19 | ``` ruby 20 | pod "StoryboardKit" 21 | ``` 22 | 23 | Alternatively you can add the **[github repo](https://github.com/Adorkable/StoryboardKit)** as a submodule and use **StoryboardKit** as a framework. 24 | 25 | Carthage support soon! 26 | 27 | Setup and Usage 28 | --- 29 | --- 30 | The library uses two "root" level objects to provide the tree of information you'll need: 31 | 32 | * `ApplicationInfo` - contains all information global to your application such as class information as well as instance information 33 | * `StoryboardInfo` - contains all information specific to a particular Storyboard file 34 | 35 | To parse a Storyboard file: 36 | 37 | ``` swift 38 | var applicationInfo = ApplicationInfo() 39 | var storyboardInfo = StoryboardFileParser.parse(applicationInfo!, pathFileName: "Main.storyboard") 40 | ``` 41 | 42 | From here you can access things like the list of all **ViewControllerClassInfo**s or **ViewControllerInstanceInfo**s in the app through your **ApplicationInfo** instance 43 | 44 | ``` swift 45 | for viewControllerClass in application.viewControllerClasses { 46 | ... 47 | } 48 | ``` 49 | 50 | Or perhaps you'll traverse through your Storyboard graph via the **StoryboardInstanceInfo**'s **initialViewController** or **scenes** list. 51 | 52 | ``` swift 53 | guard let initialViewController = storyboardInfo.initialViewController else { ... } 54 | guard let initialView = initialViewController.view else { ... } 55 | 56 | guard let subviews = initialView.subviews else { ... } 57 | 58 | for subview in subviews { 59 | ... 60 | } 61 | ``` 62 | 63 | To learn more about the information **StoryboardKit** currently parses please read the docs here: [cocoadocs.org](http://cocoadocs.org/docsets/StoryboardKit/) 64 | 65 | To see an example of **StoryboardKit** in use check out the _seguecode_ repo here: [seguecode](https://github.com/Adorkable/seguecode) 66 | 67 | Contributing 68 | --- 69 | If you have any ideas, suggestions or bugs to report please [create an issue](https://github.com/Adorkable/StoryboardKit/issues/new) labeled *feature* or *bug* (check to see if the issue exists first please!). 70 | 71 | Or submit a pull request, there is a lot more work to be done! 72 | 73 | 74 | ## License 75 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FAdorkable%2FStoryboardKit.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FAdorkable%2FStoryboardKit?ref=badge_large) -------------------------------------------------------------------------------- /StoryboardKitTests/ViewInstanceInfoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewInstanceInfoTests.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/30/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import StoryboardKit 12 | 13 | class ViewInstanceInfoTests: XCTestCase { 14 | var applicationInfo : ApplicationInfo? 15 | 16 | var viewInstanceInfoId = "IKn-pG-61R" 17 | var viewInstanceInfo : ViewInstanceInfo? 18 | 19 | override func setUp() { 20 | self.continueAfterFailure = false 21 | 22 | super.setUp() 23 | 24 | applicationInfo = ApplicationInfo() 25 | 26 | do 27 | { 28 | try StoryboardFileParser.parse(applicationInfo!, pathFileName: storyboardPathBuilder()! ) 29 | } catch let error as NSError 30 | { 31 | XCTAssertNil(error, "Expected parse to not throw an error: \(error)") 32 | } 33 | 34 | self.viewInstanceInfo = applicationInfo?.viewInstanceWithId(self.viewInstanceInfoId) 35 | } 36 | 37 | func testClassInfo() { 38 | let className = "UIView" 39 | let classInfo = self.applicationInfo?.viewClassWithClassName(className) 40 | 41 | XCTAssertNotNil(self.viewInstanceInfo, "Unable to retrieve ViewInstanceInfo with id \(self.viewInstanceInfoId)") 42 | 43 | XCTAssertNotNil(classInfo, "\(self.viewInstanceInfo!)'s classInfo should not be nil") 44 | 45 | XCTAssertEqual(self.viewInstanceInfo!.classInfo, classInfo!, "\(self.viewInstanceInfo!)'s classInfo should be equal to \(classInfo!)") 46 | } 47 | 48 | func testId() { 49 | XCTAssertNotNil(self.viewInstanceInfo, "Unable to retrieve ViewInstanceInfo with id \(self.viewInstanceInfoId)") 50 | 51 | XCTAssertEqual(self.viewInstanceInfo!.id, self.viewInstanceInfoId, "\(self.viewInstanceInfo!)'s id should be equal to \(self.viewInstanceInfo)") 52 | } 53 | 54 | func testFrame() { 55 | let equalTo = CGRect(x: 0, y: 0, width: 600, height: 600) 56 | 57 | XCTAssertNotNil(self.viewInstanceInfo, "Unable to retrieve ViewInstanceInfo with id \(self.viewInstanceInfoId)") 58 | // XCTAssertNotNil(self.viewInstanceInfo!.frame, "\(self.viewInstanceInfo)'s frame should not be nil") 59 | 60 | XCTAssertEqual(self.viewInstanceInfo!.frame!, equalTo, "\(self.viewInstanceInfo!)'s frame should be equal to \(equalTo)") 61 | } 62 | 63 | func testAutoResizingMaskWidthSizable() { 64 | let equalTo = true 65 | 66 | XCTAssertNotNil(self.viewInstanceInfo, "Unable to retrieve ViewInstanceInfo with id \(self.viewInstanceInfoId)") 67 | 68 | XCTAssertEqual(self.viewInstanceInfo!.autoResizingMaskWidthSizable, equalTo, "\(self.viewInstanceInfo!)'s autoResizingMaskWidthSizable should be equal to \(equalTo)") 69 | } 70 | 71 | func testAutoResizingMaskHeightSizable() { 72 | let equalTo = true 73 | 74 | XCTAssertNotNil(self.viewInstanceInfo, "Unable to retrieve ViewInstanceInfo with id \(self.viewInstanceInfoId)") 75 | 76 | XCTAssertEqual(self.viewInstanceInfo!.autoResizingMaskHeightSizable, equalTo, "\(self.viewInstanceInfo!)'s autoResizingMaskHeightSizable should be equal to \(equalTo)") 77 | } 78 | 79 | func testSubviews() { 80 | let equalTo = 4 81 | 82 | XCTAssertNotNil(self.viewInstanceInfo, "Unable to retrieve ViewInstanceInfo with id \(self.viewInstanceInfoId)") 83 | 84 | XCTAssertNotNil(self.viewInstanceInfo!.subviews, "\(self.viewInstanceInfo!)'s subviews should not be nil") 85 | XCTAssertEqual(self.viewInstanceInfo!.subviews!.count, equalTo, "\(self.viewInstanceInfo!)'s subview count should be equal to \(equalTo)") 86 | } 87 | 88 | func testBackgroundColor() { 89 | let equalTo = NSColor(calibratedWhite: 0.66666666666666663, alpha: 1.0) 90 | 91 | XCTAssertNotNil(self.viewInstanceInfo, "Unable to retrieve ViewInstanceInfo with id \(self.viewInstanceInfoId)") 92 | 93 | XCTAssertNotNil(self.viewInstanceInfo!.backgroundColor, "\(self.viewInstanceInfo!)'s background color should not be nil") 94 | XCTAssertEqual(self.viewInstanceInfo!.backgroundColor!, equalTo, "\(self.viewInstanceInfo!)'s background color count should be equal to \(equalTo)") 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /StoryboardKit/StoryboardFileVersionedParsers/3.0/StoryboardFile3_0Parser_Storyboard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardFile3_0Parser_Storyboard.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/30/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import SWXMLHash 12 | 13 | internal extension StoryboardFile3_0Parser { 14 | 15 | 16 | // MARK: Storyboard 17 | 18 | internal class StoryboardInstanceParseInfo : NSObject { 19 | // internal var fileType : String 20 | // internal var fileVersion : String 21 | // internal var toolsVersion : String 22 | // internal var systemVersion : String 23 | // internal var targetRuntime : String 24 | // internal var propertyAccessControl : String 25 | 26 | internal var useAutolayout : Bool 27 | internal var useTraitCollections : Bool 28 | internal var initialViewControllerId : String? 29 | 30 | init(useAutolayout : Bool, useTraitCollections : Bool, initialViewControllerId : String?) { 31 | self.useAutolayout = useAutolayout 32 | self.useTraitCollections = useTraitCollections 33 | self.initialViewControllerId = initialViewControllerId 34 | 35 | super.init() 36 | } 37 | 38 | var scenes : [StoryboardInstanceInfo.SceneInfo] = Array() 39 | 40 | func add(scene scene : StoryboardInstanceInfo.SceneInfo) { 41 | // TODO: validate that it isn't a dup 42 | self.scenes.append(scene) 43 | } 44 | } 45 | 46 | internal func createStoryboardInstance(root : XMLIndexer) -> StoryboardInstanceParseInfo? { 47 | var result : StoryboardInstanceParseInfo? 48 | 49 | if let document = root["document"].element 50 | { 51 | let useAutolayout = document.allAttributes["useAutolayout"]?.text == "YES" 52 | let useTraitCollections = document.allAttributes["useTraitCollections"]?.text == "YES" 53 | 54 | let initialViewControllerId = document.allAttributes["initialViewController"]?.text 55 | 56 | 57 | let storyboardInstance = StoryboardInstanceParseInfo(useAutolayout: useAutolayout, useTraitCollections: useTraitCollections, initialViewControllerId: initialViewControllerId) 58 | 59 | // TODO: StoryboardFileInfo 60 | // storyboardInstance.fileType = document.attributes["type"] 61 | // storyboardInstance.fileVersion = document.attributes["version"] 62 | // storyboardInstance.toolsVersion = document.attributes["toolsVersion"] 63 | // storyboardInstance.systemVersion = document.attributes["systemVersion"] 64 | // storyboardInstance.targetRuntime = document.attributes["targetRuntime"] 65 | // storyboardInstance.propertyAccessControl = document.attributes["propertyAccessControl"] 66 | 67 | result = storyboardInstance 68 | } 69 | 70 | return result 71 | } 72 | 73 | internal func createStoryboardInstanceInfoFromParsed() throws -> StoryboardFileParser.ParseResult { 74 | var result : StoryboardFileParser.ParseResult 75 | 76 | if let storyboardInstanceParseInfo = self.storyboardInstanceParseInfo 77 | { 78 | var initialViewController : ViewControllerInstanceInfo? 79 | 80 | if let initialViewControllerId = storyboardInstanceParseInfo.initialViewControllerId 81 | { 82 | initialViewController = self.applicationInfo.viewControllerInstanceWithId(initialViewControllerId) 83 | } 84 | 85 | let storyboardInstanceInfo = StoryboardInstanceInfo(useAutolayout: storyboardInstanceParseInfo.useAutolayout, useTraitCollections: storyboardInstanceParseInfo.useTraitCollections, initialViewController: initialViewController) 86 | 87 | for sceneInfo in storyboardInstanceParseInfo.scenes 88 | { 89 | storyboardInstanceInfo.add(scene: sceneInfo) 90 | } 91 | 92 | result = (storyboardInstanceInfo, self.logs) 93 | } else 94 | { 95 | throw NSError(domain: "Unable to find StoryboardInstanceParseInfo, likely cause was we were unable to parse root of Storyboard file", code: 0, userInfo: nil) 96 | } 97 | 98 | return result 99 | } 100 | } -------------------------------------------------------------------------------- /StoryboardKit.xcodeproj/xcshareddata/xcschemes/StoryboardKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /StoryboardKit/StoryboardFileVersionedParsers/3.0/StoryboardFile3_0Parser_Segues.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardFile3_0Parser_Segues.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/30/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import SWXMLHash 12 | 13 | internal extension StoryboardFile3_0Parser { 14 | // MARK: Segues 15 | 16 | internal class SegueInstanceParseInfo : NSObject/*, CustomDebugStringConvertible*/ { 17 | internal let classInfo : SegueClassInfo 18 | internal let id : String 19 | internal var source : SegueConnection 20 | internal let kind : String? 21 | internal let identifier : String? 22 | internal let destinationId : String 23 | internal var destination : SegueConnection? 24 | 25 | init(classInfo : SegueClassInfo, id : String, source : SegueConnection, destinationId : String, kind : String?, identifier : String?) { 26 | self.classInfo = classInfo 27 | self.id = id 28 | self.source = source 29 | self.destinationId = destinationId 30 | self.kind = kind 31 | self.identifier = identifier 32 | 33 | super.init() 34 | } 35 | 36 | override var debugDescription : String { 37 | var result : String = super.debugDescription 38 | 39 | if let identifier = self.identifier 40 | { 41 | result += identifier 42 | } 43 | 44 | return result 45 | } 46 | } 47 | 48 | internal func createConnectionParseInfo(connection : XMLIndexer, source : ViewControllerInstanceInfo) -> SegueInstanceParseInfo? { 49 | var result : SegueInstanceParseInfo? 50 | 51 | if let element = connection.element, 52 | let id = element.allAttributes["id"]?.text, 53 | let destinationId = element.allAttributes["destination"]?.text 54 | { 55 | var useClass : String 56 | if let customClass = element.allAttributes["customClass"]?.text 57 | { 58 | useClass = customClass 59 | } else 60 | { 61 | useClass = SegueClassInfo.defaultClass 62 | } 63 | 64 | var segueClass = self.applicationInfo.segueClassWithClassName(useClass) 65 | if segueClass == nil 66 | { 67 | segueClass = SegueClassInfo(className: useClass) 68 | self.applicationInfo.add(segueClass: segueClass!) 69 | } 70 | 71 | let kind = element.allAttributes["kind"]?.text 72 | let identifier = element.allAttributes["identifier"]?.text 73 | 74 | result = SegueInstanceParseInfo(classInfo: segueClass!, id: id, source: StoryboardKit_WeakWrapper(source), destinationId: destinationId, kind: kind, identifier: identifier) 75 | } 76 | 77 | return result 78 | } 79 | 80 | internal func createSegueInstanceInfosFromParsed() { 81 | while self.parsedSegues.count > 0 82 | { 83 | let segueParsedInfo = self.parsedSegues.removeLast() 84 | 85 | var segueInfo : SegueInstanceInfo? 86 | 87 | if let destination = self.applicationInfo.viewControllerInstanceWithId(segueParsedInfo.destinationId) 88 | { 89 | segueInfo = SegueInstanceInfo(classInfo: segueParsedInfo.classInfo, id: segueParsedInfo.id, source: segueParsedInfo.source, destination: destination, kind: segueParsedInfo.kind, identifier: segueParsedInfo.identifier) 90 | } else if let destination = self.applicationInfo.navigationControllerInstanceWithId(segueParsedInfo.destinationId) 91 | { 92 | segueInfo = SegueInstanceInfo(classInfo: segueParsedInfo.classInfo, id: segueParsedInfo.id, source: segueParsedInfo.source, destination: destination, kind: segueParsedInfo.kind, identifier: segueParsedInfo.identifier) 93 | } else if let destination = self.applicationInfo.tabBarControllerInstanceWithId(segueParsedInfo.destinationId) 94 | { 95 | segueInfo = SegueInstanceInfo(classInfo: segueParsedInfo.classInfo, id: segueParsedInfo.id, source: segueParsedInfo.source, destination: destination, kind: segueParsedInfo.kind, identifier: segueParsedInfo.identifier) 96 | } else 97 | { 98 | self.Log("Error linking pending segues, unable to find destination with id \(segueParsedInfo.destinationId)") 99 | } 100 | 101 | if segueInfo != nil { 102 | if segueInfo!.kind == "relationship" && segueInfo!.source.value is NavigationControllerInstanceInfo { 103 | (segueInfo!.source.value as! NavigationControllerInstanceInfo).root = segueInfo 104 | } else { 105 | segueInfo!.source.value!.add(segue: segueInfo!) 106 | } 107 | } 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.1) 5 | activesupport (4.2.11.1) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | addressable (2.8.0) 11 | public_suffix (>= 2.0.2, < 5.0) 12 | atomos (0.1.3) 13 | babosa (1.0.2) 14 | claide (1.0.3) 15 | cocoapods (1.7.5) 16 | activesupport (>= 4.0.2, < 5) 17 | claide (>= 1.0.2, < 2.0) 18 | cocoapods-core (= 1.7.5) 19 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 20 | cocoapods-downloader (>= 1.2.2, < 2.0) 21 | cocoapods-plugins (>= 1.0.0, < 2.0) 22 | cocoapods-search (>= 1.0.0, < 2.0) 23 | cocoapods-stats (>= 1.0.0, < 2.0) 24 | cocoapods-trunk (>= 1.3.1, < 2.0) 25 | cocoapods-try (>= 1.1.0, < 2.0) 26 | colored2 (~> 3.1) 27 | escape (~> 0.0.4) 28 | fourflusher (>= 2.3.0, < 3.0) 29 | gh_inspector (~> 1.0) 30 | molinillo (~> 0.6.6) 31 | nap (~> 1.0) 32 | ruby-macho (~> 1.4) 33 | xcodeproj (>= 1.10.0, < 2.0) 34 | cocoapods-core (1.7.5) 35 | activesupport (>= 4.0.2, < 6) 36 | fuzzy_match (~> 2.0.4) 37 | nap (~> 1.0) 38 | cocoapods-deintegrate (1.0.4) 39 | cocoapods-downloader (1.6.3) 40 | cocoapods-plugins (1.0.0) 41 | nap 42 | cocoapods-search (1.0.0) 43 | cocoapods-stats (1.1.0) 44 | cocoapods-trunk (1.4.0) 45 | nap (>= 0.8, < 2.0) 46 | netrc (~> 0.11) 47 | cocoapods-try (1.1.0) 48 | colored (1.2) 49 | colored2 (3.1.2) 50 | commander-fastlane (4.4.6) 51 | highline (~> 1.7.2) 52 | concurrent-ruby (1.1.5) 53 | declarative (0.0.10) 54 | declarative-option (0.1.0) 55 | digest-crc (0.4.1) 56 | domain_name (0.5.20190701) 57 | unf (>= 0.0.5, < 1.0.0) 58 | dotenv (2.7.5) 59 | emoji_regex (1.0.1) 60 | escape (0.0.4) 61 | excon (0.71.0) 62 | faraday (0.15.4) 63 | multipart-post (>= 1.2, < 3) 64 | faraday-cookie_jar (0.0.6) 65 | faraday (>= 0.7.4) 66 | http-cookie (~> 1.0.0) 67 | faraday-http-cache (2.0.0) 68 | faraday (~> 0.8) 69 | faraday_middleware (0.13.1) 70 | faraday (>= 0.7.4, < 1.0) 71 | fastimage (2.1.5) 72 | fastlane (2.129.0) 73 | CFPropertyList (>= 2.3, < 4.0.0) 74 | addressable (>= 2.3, < 3.0.0) 75 | babosa (>= 1.0.2, < 2.0.0) 76 | bundler (>= 1.12.0, < 3.0.0) 77 | colored 78 | commander-fastlane (>= 4.4.6, < 5.0.0) 79 | dotenv (>= 2.1.1, < 3.0.0) 80 | emoji_regex (>= 0.1, < 2.0) 81 | excon (>= 0.45.0, < 1.0.0) 82 | faraday (~> 0.9) 83 | faraday-cookie_jar (~> 0.0.6) 84 | faraday_middleware (~> 0.9) 85 | fastimage (>= 2.1.0, < 3.0.0) 86 | gh_inspector (>= 1.1.2, < 2.0.0) 87 | google-api-client (>= 0.21.2, < 0.24.0) 88 | google-cloud-storage (>= 1.15.0, < 2.0.0) 89 | highline (>= 1.7.2, < 2.0.0) 90 | json (< 3.0.0) 91 | jwt (~> 2.1.0) 92 | mini_magick (>= 4.9.4, < 5.0.0) 93 | multi_xml (~> 0.5) 94 | multipart-post (~> 2.0.0) 95 | plist (>= 3.1.0, < 4.0.0) 96 | public_suffix (~> 2.0.0) 97 | rubyzip (>= 1.2.2, < 2.0.0) 98 | security (= 0.1.3) 99 | simctl (~> 1.6.3) 100 | slack-notifier (>= 2.0.0, < 3.0.0) 101 | terminal-notifier (>= 2.0.0, < 3.0.0) 102 | terminal-table (>= 1.4.5, < 2.0.0) 103 | tty-screen (>= 0.6.3, < 1.0.0) 104 | tty-spinner (>= 0.8.0, < 1.0.0) 105 | word_wrap (~> 1.0.0) 106 | xcodeproj (>= 1.8.1, < 2.0.0) 107 | xcpretty (~> 0.3.0) 108 | xcpretty-travis-formatter (>= 0.0.3) 109 | fourflusher (2.3.1) 110 | fuzzy_match (2.0.4) 111 | gh_inspector (1.1.3) 112 | github_changelog_generator (1.14.3) 113 | activesupport 114 | faraday-http-cache 115 | multi_json 116 | octokit (~> 4.6) 117 | rainbow (>= 2.1) 118 | rake (>= 10.0) 119 | retriable (~> 2.1) 120 | google-api-client (0.23.9) 121 | addressable (~> 2.5, >= 2.5.1) 122 | googleauth (>= 0.5, < 0.7.0) 123 | httpclient (>= 2.8.1, < 3.0) 124 | mime-types (~> 3.0) 125 | representable (~> 3.0) 126 | retriable (>= 2.0, < 4.0) 127 | signet (~> 0.9) 128 | google-cloud-core (1.3.0) 129 | google-cloud-env (~> 1.0) 130 | google-cloud-env (1.2.0) 131 | faraday (~> 0.11) 132 | google-cloud-storage (1.16.0) 133 | digest-crc (~> 0.4) 134 | google-api-client (~> 0.23) 135 | google-cloud-core (~> 1.2) 136 | googleauth (>= 0.6.2, < 0.10.0) 137 | googleauth (0.6.7) 138 | faraday (~> 0.12) 139 | jwt (>= 1.4, < 3.0) 140 | memoist (~> 0.16) 141 | multi_json (~> 1.11) 142 | os (>= 0.9, < 2.0) 143 | signet (~> 0.7) 144 | highline (1.7.10) 145 | http-cookie (1.0.3) 146 | domain_name (~> 0.5) 147 | httpclient (2.8.3) 148 | i18n (0.9.5) 149 | concurrent-ruby (~> 1.0) 150 | json (2.3.1) 151 | jwt (2.1.0) 152 | memoist (0.16.0) 153 | mime-types (3.2.2) 154 | mime-types-data (~> 3.2015) 155 | mime-types-data (3.2019.0331) 156 | mini_magick (4.9.5) 157 | minitest (5.11.3) 158 | molinillo (0.6.6) 159 | multi_json (1.13.1) 160 | multi_xml (0.6.0) 161 | multipart-post (2.0.0) 162 | nanaimo (0.2.6) 163 | nap (1.1.0) 164 | naturally (2.2.0) 165 | netrc (0.11.0) 166 | octokit (4.14.0) 167 | sawyer (~> 0.8.0, >= 0.5.3) 168 | os (1.0.1) 169 | plist (3.5.0) 170 | public_suffix (2.0.5) 171 | rainbow (3.0.0) 172 | rake (12.3.3) 173 | representable (3.0.4) 174 | declarative (< 0.1.0) 175 | declarative-option (< 0.2.0) 176 | uber (< 0.2.0) 177 | retriable (2.1.0) 178 | rouge (2.0.7) 179 | ruby-macho (1.4.0) 180 | rubyzip (1.3.0) 181 | sawyer (0.8.2) 182 | addressable (>= 2.3.5) 183 | faraday (> 0.8, < 2.0) 184 | security (0.1.3) 185 | signet (0.11.0) 186 | addressable (~> 2.3) 187 | faraday (~> 0.9) 188 | jwt (>= 1.5, < 3.0) 189 | multi_json (~> 1.10) 190 | simctl (1.6.5) 191 | CFPropertyList 192 | naturally 193 | slack-notifier (2.3.2) 194 | terminal-notifier (2.0.0) 195 | terminal-table (1.8.0) 196 | unicode-display_width (~> 1.1, >= 1.1.1) 197 | thread_safe (0.3.6) 198 | tty-cursor (0.7.0) 199 | tty-screen (0.7.0) 200 | tty-spinner (0.9.1) 201 | tty-cursor (~> 0.7) 202 | tzinfo (1.2.5) 203 | thread_safe (~> 0.1) 204 | uber (0.1.0) 205 | unf (0.1.4) 206 | unf_ext 207 | unf_ext (0.0.7.6) 208 | unicode-display_width (1.6.0) 209 | word_wrap (1.0.0) 210 | xcodeproj (1.12.0) 211 | CFPropertyList (>= 2.3.3, < 4.0) 212 | atomos (~> 0.1.3) 213 | claide (>= 1.0.2, < 2.0) 214 | colored2 (~> 3.1) 215 | nanaimo (~> 0.2.6) 216 | xcpretty (0.3.0) 217 | rouge (~> 2.0.7) 218 | xcpretty-travis-formatter (1.0.0) 219 | xcpretty (~> 0.2, >= 0.0.7) 220 | 221 | PLATFORMS 222 | ruby 223 | 224 | DEPENDENCIES 225 | cocoapods 226 | fastlane 227 | github_changelog_generator 228 | 229 | BUNDLED WITH 230 | 1.16.6 231 | -------------------------------------------------------------------------------- /StoryboardKit/ApplicationInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationInfo.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 5/3/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | * Contains information global to your application 13 | */ 14 | public class ApplicationInfo: NSObject { 15 | /// All View Controller Class Infos in your application 16 | public private(set) var viewControllerClasses = [ViewControllerClassInfo]() 17 | 18 | /** 19 | Add a View Controller Class Info 20 | 21 | - parameter viewControllerClass: View Controller Class Info to add 22 | */ 23 | func add(viewControllerClass viewControllerClass : ViewControllerClassInfo) { 24 | // TODO: validates that this isn't a dup 25 | self.viewControllerClasses.append(viewControllerClass) 26 | } 27 | 28 | /** 29 | Retrieve a View Controller Class Info by class name 30 | 31 | - parameter className: Name of the class you wish to retrieve 32 | 33 | - returns: If found a reference to the View Controller Class Info you wished to retrieve, otherwise nil 34 | */ 35 | public func viewControllerClassWithClassName(className : String) -> ViewControllerClassInfo? { 36 | return classWithClassName(className, objects: self.viewControllerClasses) 37 | } 38 | 39 | /// All View Controller Instance Infos in your application 40 | public private(set) var viewControllerInstances = [ViewControllerInstanceInfo]() 41 | 42 | /** 43 | Add a View Controller Instance Info 44 | 45 | - parameter viewControllerInstance: View Controller Instance Info to add 46 | */ 47 | func add(viewControllerInstance viewControllerInstance : ViewControllerInstanceInfo) { 48 | // TODO: validates that this isn't a dup 49 | self.viewControllerInstances.append(viewControllerInstance) 50 | } 51 | 52 | /** 53 | Retrieve a View Controller Instance Info by Storyboard id 54 | 55 | - parameter id: Storyboard id of the instance you wish to retrieve (not to be confused with IB assigned identifier) 56 | 57 | - returns: If found a reference to the View Controller Instance Info you wished to retrieve, otherwise nil 58 | */ 59 | public func viewControllerInstanceWithId(id : String) -> ViewControllerInstanceInfo? { 60 | return firstObjectWithId(id, objects: self.viewControllerInstances) 61 | } 62 | 63 | /** 64 | Retrieve a View Controller Instance Info by Storyboard Identifier 65 | 66 | - parameter id: Storyboard Identifier of the instance you wish to retrieve 67 | 68 | - returns: If found a reference to the View Controller Instance Info you wished to retrieve, otherwise nil 69 | */ 70 | public func viewControllerInstanceWithStoryboardIdentifier(identifier : String) -> ViewControllerInstanceInfo? { 71 | return self.viewControllerInstances.filter( { $0.storyboardIdentifier == identifier} ).first 72 | } 73 | 74 | /// All Navigation Controller Instance Infos in your application 75 | public private(set) var navigationControllerInstances = [NavigationControllerInstanceInfo]() 76 | 77 | /** 78 | Add a Navigation Controller Instance Info to your application 79 | 80 | - parameter navigationControllerInstance: Navigation Controller Instance Info to add 81 | */ 82 | func add(navigationControllerInstance navigationControllerInstance : NavigationControllerInstanceInfo) { 83 | // TODO: validates that this isn't a dup 84 | self.navigationControllerInstances.append(navigationControllerInstance) 85 | } 86 | 87 | /** 88 | Retrieve a Navigation Controller Instance Info by Storyboard id 89 | 90 | - parameter id: Storyboard id of the instance you wish to retrieve (not to be confused with IB assigned identifier) 91 | 92 | - returns: If found a reference to the Navigation Controller Instance Info you wished to retrieve, otherwise nil 93 | */ 94 | public func navigationControllerInstanceWithId(id : String) -> NavigationControllerInstanceInfo? { 95 | return firstObjectWithId(id, objects: self.navigationControllerInstances) 96 | } 97 | 98 | /** 99 | Retrieve a Navigation Controller Instance Info by Storyboard Identifier 100 | 101 | - parameter id: Storyboard Identifier of the instance you wish to retrieve 102 | 103 | - returns: If found a reference to the Navigation Controller Instance Info you wished to retrieve, otherwise nil 104 | */ 105 | public func navigationControllerInstanceWithStoryboardIdentifier(identifier : String) -> NavigationControllerInstanceInfo? { 106 | return self.navigationControllerInstances.filter( { $0.storyboardIdentifier == identifier} ).first 107 | } 108 | 109 | /// All Tab Bar Controller Instance Infos in your application 110 | public private(set) var tabBarControllerInstances = [TabBarControllerInstanceInfo]() 111 | 112 | /** 113 | Add a Tab Bar Controller Instance Info to your application 114 | 115 | - parameter tabBarControllerInstance: Tab Bar Controller Instance Info to add 116 | */ 117 | func add(tabBarControllerInstance tabBarControllerInstance : TabBarControllerInstanceInfo) { 118 | // TODO: validates that this isn't a dup 119 | self.tabBarControllerInstances.append(tabBarControllerInstance) 120 | } 121 | 122 | /** 123 | Retrieve a Tab Bar Controller Instance Info by Storyboard id 124 | 125 | - parameter id: Storyboard id of the instance you wish to retrieve (not to be confused with IB assigned identifier) 126 | 127 | - returns: If found a reference to the Tab Bar Controller Instance Info you wished to retrieve, otherwise nil 128 | */ 129 | public func tabBarControllerInstanceWithId(id : String) -> TabBarControllerInstanceInfo? { 130 | return firstObjectWithId(id, objects: self.tabBarControllerInstances) 131 | } 132 | 133 | /** 134 | Retrieve a Tab Bar Controller Instance Info by Storyboard Identifier 135 | 136 | - parameter id: Storyboard Identifier of the instance you wish to retrieve 137 | 138 | - returns: If found a reference to the Tab Bar Controller Instance Info you wished to retrieve, otherwise nil 139 | */ 140 | public func tabBarControllerInstanceWithStoryboardIdentifier(identifier : String) -> TabBarControllerInstanceInfo? { 141 | return self.tabBarControllerInstances.filter( { $0.storyboardIdentifier == identifier} ).first 142 | } 143 | 144 | /// All Segue Class Infos in your application 145 | public private(set) var segueClasses = [SegueClassInfo]() 146 | 147 | /** 148 | Add a Segue Class Info to your application 149 | 150 | - parameter segueClass: Segue Class Info to add 151 | */ 152 | func add(segueClass segueClass : SegueClassInfo) { 153 | // TODO: validates that this isn't a dup 154 | self.segueClasses.append(segueClass) 155 | } 156 | 157 | /** 158 | Retrieve a Segue Class Info by class name 159 | 160 | - parameter className: Name of the class you wish to retrieve 161 | 162 | - returns: If found a reference to the Segue Class Info you wished to retrieve, otherwise nil 163 | */ 164 | public func segueClassWithClassName(className : String) -> SegueClassInfo? { 165 | return classWithClassName(className, objects: self.segueClasses) 166 | } 167 | 168 | // TODO: store SegueInstances 169 | 170 | /// All View Class Infos in your application 171 | public private(set) var viewClasses = [ViewClassInfo]() 172 | 173 | /** 174 | Add a View Class Info to your application 175 | 176 | - parameter viewClass: View Class Info to add 177 | */ 178 | func add(viewClass viewClass : ViewClassInfo) { 179 | // TODO: validates that this isn't a dup 180 | self.viewClasses.append(viewClass) 181 | } 182 | 183 | /** 184 | Retrieve a View Class Info by class name 185 | 186 | - parameter className: Name of the class you wish to retrieve 187 | 188 | - returns: If found a reference to the View Class Info you wished to retrieve, otherwise nil 189 | */ 190 | public func viewClassWithClassName(className : String) -> ViewClassInfo? { 191 | return classWithClassName(className, objects: self.viewClasses) 192 | } 193 | 194 | /** 195 | Retrieve a View Instance Info by id 196 | 197 | - parameter id: id of the instance you wish to retrieve 198 | 199 | - returns: If found a reference to the View Instance Info you wished to retrieve, otherwise nil 200 | */ 201 | public func viewInstanceWithId(id : String) -> ViewInstanceInfo? { 202 | var result : ViewInstanceInfo? 203 | 204 | for viewClass in self.viewClasses 205 | { 206 | for viewInstanceWeakWrapper in viewClass.instanceInfos 207 | { 208 | if let viewInstance = viewInstanceWeakWrapper.value 209 | { 210 | if viewInstance.id == id 211 | { 212 | result = viewInstance 213 | break 214 | } 215 | } 216 | } 217 | 218 | if result != nil 219 | { 220 | break 221 | } 222 | } 223 | 224 | return result 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /StoryboardKit/StoryboardFileVersionedParsers/3.0/StoryboardFile3_0Parser_ViewControllers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardFile3_0Parser_ViewControllers.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/30/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import SWXMLHash 12 | 13 | internal extension StoryboardFile3_0Parser { 14 | // MARK: View Controllers 15 | 16 | internal class func useClass(element : XMLElement, classInfo : ViewControllerClassInfo.Type) -> String 17 | { 18 | var result : String 19 | if let customClass = element.allAttributes["customClass"]?.text 20 | { 21 | result = customClass 22 | } else 23 | { 24 | result = classInfo.defaultClass 25 | } 26 | return result 27 | } 28 | 29 | internal func findOrCreateViewControllerClassInfo(useClass : String, classInfo : ViewControllerClassInfo.Type) -> ViewControllerClassInfo 30 | { 31 | let result : ViewControllerClassInfo 32 | 33 | if let controllerClassInfo = self.applicationInfo.viewControllerClassWithClassName(useClass) // TODO: as? classInfo 34 | { 35 | result = controllerClassInfo 36 | } else 37 | { 38 | result = classInfo.init(className: useClass) 39 | self.applicationInfo.add(viewControllerClass: result) 40 | } 41 | 42 | return result 43 | } 44 | 45 | internal func parseViewController(viewController : XMLIndexer, sceneInfo : StoryboardInstanceInfo.SceneInfo) { 46 | if let element = viewController.element, 47 | let id = element.allAttributes["id"]?.text 48 | { 49 | let useClass = StoryboardFile3_0Parser.useClass(element, classInfo: ViewControllerClassInfo.self) 50 | 51 | var viewControllerClassInfo = self.applicationInfo.viewControllerClassWithClassName(useClass) 52 | if viewControllerClassInfo == nil 53 | { 54 | viewControllerClassInfo = ViewControllerClassInfo(className: useClass) 55 | self.applicationInfo.add(viewControllerClass: viewControllerClassInfo!) 56 | } 57 | 58 | let storyboardIdentifier = element.allAttributes["storyboardIdentifier"]?.text 59 | 60 | var view : ViewInstanceInfo? // Should be using view.key attribute? 61 | 62 | // This is garbage, find a cleaner way 63 | if let possibleView = self.createView(viewController["view"]) 64 | { 65 | view = possibleView 66 | } else if let possibleTableView = self.createTableView(viewController["tableView"]) 67 | { 68 | view = possibleTableView 69 | } else if let possibleCollectionView = self.createCollectionView(viewController["collectionView"]) 70 | { 71 | view = possibleCollectionView 72 | } 73 | 74 | let viewControllerInstanceInfo = ViewControllerInstanceInfo(classInfo: viewControllerClassInfo!, id: id, storyboardIdentifier: storyboardIdentifier, view: view) 75 | 76 | sceneInfo.controller = viewControllerInstanceInfo 77 | self.applicationInfo.add(viewControllerInstance: viewControllerInstanceInfo) 78 | 79 | self.parseLayoutGuides(viewController["layoutGuides"], source: viewControllerInstanceInfo) 80 | 81 | let navigationItem = viewController["navigationItem"] 82 | if navigationItem.element != nil 83 | { 84 | self.parseNavigationItem(navigationItem, source: viewControllerInstanceInfo) 85 | } 86 | 87 | self.parseConnections(viewController["connections"], source: viewControllerInstanceInfo) 88 | } 89 | } 90 | 91 | // MARK: Navigation Controller 92 | 93 | internal func parseNavigationController(navigationController : XMLIndexer, sceneInfo : StoryboardInstanceInfo.SceneInfo) { 94 | if let element = navigationController.element, let id = element.allAttributes["id"]?.text 95 | { 96 | let useClass = StoryboardFile3_0Parser.useClass(element, classInfo: NavigationControllerClassInfo.self) 97 | 98 | // TODO: restrict to NavControllerClasses 99 | var navigationControllerClassInfo = self.applicationInfo.viewControllerClassWithClassName(useClass) as? NavigationControllerClassInfo 100 | if navigationControllerClassInfo == nil 101 | { 102 | navigationControllerClassInfo = NavigationControllerClassInfo(className: useClass) 103 | self.applicationInfo.add(viewControllerClass: navigationControllerClassInfo!) 104 | } 105 | 106 | let storyboardIdentifier = element.allAttributes["storyboardIdentifier"]?.text 107 | let sceneMemberId = element.allAttributes["sceneMemberID"]?.text 108 | 109 | let navigationControllerInstanceInfo = NavigationControllerInstanceInfo(classInfo: navigationControllerClassInfo!, id: id, storyboardIdentifier: storyboardIdentifier, sceneMemberId: sceneMemberId) 110 | 111 | sceneInfo.controller = navigationControllerInstanceInfo 112 | self.applicationInfo.add(navigationControllerInstance: navigationControllerInstanceInfo) 113 | 114 | /* var navigationBar = viewController["navigationBar"] //TODO: can this be optional? 115 | if navigationBar.element != nil 116 | { 117 | self.parseNavigationBar(navigationBar, source: navigationControllerInstanceInfo) 118 | } 119 | */ 120 | self.parseConnections(navigationController["connections"], source: navigationControllerInstanceInfo) 121 | } 122 | } 123 | 124 | // MARK: Tab Bar Controller 125 | 126 | internal func parseTabBarController(viewController : XMLIndexer, sceneInfo : StoryboardInstanceInfo.SceneInfo) { 127 | if let element = viewController.element, 128 | let id = element.allAttributes["id"]?.text 129 | { 130 | let useClass = StoryboardFile3_0Parser.useClass(element, classInfo: TabBarControllerClassInfo.self) 131 | 132 | let classInfo = self.findOrCreateViewControllerClassInfo(useClass, classInfo: TabBarControllerClassInfo.self) 133 | 134 | let storyboardIdentifier = element.allAttributes["storyboardIdentifier"]?.text 135 | 136 | let view = self.createView(viewController["view"]) // Should be using view.key attribute? 137 | 138 | let instanceInfo = TabBarControllerInstanceInfo(classInfo: classInfo, id: id, storyboardIdentifier: storyboardIdentifier, view: view) 139 | 140 | sceneInfo.controller = instanceInfo 141 | self.applicationInfo.add(tabBarControllerInstance: instanceInfo) 142 | 143 | self.parseLayoutGuides(viewController["layoutGuides"], source: instanceInfo) 144 | 145 | let navigationItem = viewController["navigationItem"] 146 | if navigationItem.element != nil 147 | { 148 | self.parseNavigationItem(navigationItem, source: instanceInfo) 149 | } 150 | 151 | self.parseConnections(viewController["connections"], source: instanceInfo) 152 | } 153 | } 154 | 155 | // MARK: Layout Guides 156 | 157 | internal func parseLayoutGuides(layoutGuides : XMLIndexer, source : ViewControllerInstanceInfo) { 158 | for layoutGuide in layoutGuides.children 159 | { 160 | self.parseLayoutGuide(layoutGuide, source: source) 161 | } 162 | } 163 | 164 | internal func parseLayoutGuide(layoutGuide : XMLIndexer, source : ViewControllerInstanceInfo) { 165 | 166 | if let element = layoutGuide.element, 167 | let id = element.allAttributes["id"]?.text, 168 | let type = element.allAttributes["type"]?.text 169 | { 170 | let layoutGuide = ViewControllerLayoutGuideInstanceInfo(id: id, type: type ) 171 | source.add(layoutGuide: layoutGuide) 172 | } else 173 | { 174 | self.Log("Unable to create View Controller Layout Guide Instance Info from \(layoutGuide)") 175 | } 176 | } 177 | 178 | // MARK: Navigation Item 179 | 180 | internal func parseNavigationItem(navigationItem : XMLIndexer, source : ViewControllerInstanceInfo) { 181 | 182 | switch navigationItem 183 | { 184 | case .Element(let element): 185 | if let id = element.allAttributes["id"]?.text, 186 | let navigationItemKey = element.allAttributes["key"]?.text, 187 | let title = element.allAttributes["title"]?.text 188 | { 189 | let navigationItemInstance = NavigationItemInstanceInfo(id: id, navigationItemKey: navigationItemKey, title: title) 190 | source.add(navigationItem: navigationItemInstance) 191 | } else 192 | { 193 | self.Log("Unable to create Navigation Item Instance Info from \(navigationItem)") 194 | } 195 | break 196 | case .XMLError(let error): 197 | self.Log("Unable to create Navigation Item Instance Info from \(navigationItem): \(error)") 198 | break 199 | default: 200 | self.Log("Unable to create Navigation Item Instance Info from \(navigationItem), unhandled element \(navigationItem.element)") 201 | } 202 | } 203 | 204 | // MARK: Connections 205 | 206 | internal func parseConnection(connection : XMLIndexer, source : ViewControllerInstanceInfo) { 207 | if let element = connection.element 208 | { 209 | if element.name == "segue" 210 | { 211 | if let segueParseInfo = self.createConnectionParseInfo(connection, source: source ) 212 | { 213 | self.parsedSegues.append(segueParseInfo) 214 | } 215 | } else 216 | { 217 | self.Log("Skipping unsupported connection type \(element.name)") 218 | } 219 | } 220 | } 221 | 222 | internal func parseConnections(connections : XMLIndexer, source : ViewControllerInstanceInfo) { 223 | for connection in connections.children 224 | { 225 | self.parseConnection(connection, source: source) 226 | } 227 | } 228 | } -------------------------------------------------------------------------------- /Cocoapod/StoryboardKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3C1245831C209F5400525217 /* ApplicationInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C12455C1C209E7E00525217 /* ApplicationInfoTests.swift */; }; 11 | 3C1245841C209F5400525217 /* ClassInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C12455D1C209E7E00525217 /* ClassInfoTests.swift */; }; 12 | 3C1245861C209F5400525217 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3C12455F1C209E7E00525217 /* Images.xcassets */; }; 13 | 3C1245881C209F5400525217 /* StoryboardFileParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1245611C209E7E00525217 /* StoryboardFileParserTests.swift */; }; 14 | 3C1245891C209F5400525217 /* StoryboardInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1245621C209E7E00525217 /* StoryboardInfoTests.swift */; }; 15 | 3C12458A1C209F5400525217 /* StoryboardInstanceInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1245631C209E7E00525217 /* StoryboardInstanceInfoTests.swift */; }; 16 | 3C12458C1C209F5400525217 /* TableViewInstanceInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1245651C209E7E00525217 /* TableViewInstanceInfoTests.swift */; }; 17 | 3C12458D1C209F5400525217 /* ViewInstanceInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1245661C209E7E00525217 /* ViewInstanceInfoTests.swift */; }; 18 | 3C12458F1C20A07700525217 /* StoryboardKit.storyboard in Copy iOS Storyboaard */ = {isa = PBXBuildFile; fileRef = 3C1245641C209E7E00525217 /* StoryboardKit.storyboard */; }; 19 | E40A379BD928D05C4C108DB2 /* Pods_CocoapodTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D5557C7BDAE283B154FD653 /* Pods_CocoapodTests.framework */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXCopyFilesBuildPhase section */ 23 | 3C12458E1C20A06200525217 /* Copy iOS Storyboaard */ = { 24 | isa = PBXCopyFilesBuildPhase; 25 | buildActionMask = 2147483647; 26 | dstPath = ""; 27 | dstSubfolderSpec = 7; 28 | files = ( 29 | 3C12458F1C20A07700525217 /* StoryboardKit.storyboard in Copy iOS Storyboaard */, 30 | ); 31 | name = "Copy iOS Storyboaard"; 32 | runOnlyForDeploymentPostprocessing = 0; 33 | }; 34 | /* End PBXCopyFilesBuildPhase section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 3C12455C1C209E7E00525217 /* ApplicationInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationInfoTests.swift; sourceTree = ""; }; 38 | 3C12455D1C209E7E00525217 /* ClassInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClassInfoTests.swift; sourceTree = ""; }; 39 | 3C12455F1C209E7E00525217 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 40 | 3C1245601C209E7E00525217 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 3C1245611C209E7E00525217 /* StoryboardFileParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardFileParserTests.swift; sourceTree = ""; }; 42 | 3C1245621C209E7E00525217 /* StoryboardInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardInfoTests.swift; sourceTree = ""; }; 43 | 3C1245631C209E7E00525217 /* StoryboardInstanceInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardInstanceInfoTests.swift; sourceTree = ""; }; 44 | 3C1245641C209E7E00525217 /* StoryboardKit.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = StoryboardKit.storyboard; sourceTree = ""; }; 45 | 3C1245651C209E7E00525217 /* TableViewInstanceInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewInstanceInfoTests.swift; sourceTree = ""; }; 46 | 3C1245661C209E7E00525217 /* ViewInstanceInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewInstanceInfoTests.swift; sourceTree = ""; }; 47 | 3C1245731C209E9D00525217 /* StoryboardKit.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = StoryboardKit.podspec; path = ../StoryboardKit.podspec; sourceTree = SOURCE_ROOT; }; 48 | 3C12457A1C209F1F00525217 /* CocoapodTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CocoapodTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 7360C8C399AA4BCDA1485EA6 /* Pods-CocoapodTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CocoapodTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CocoapodTests/Pods-CocoapodTests.release.xcconfig"; sourceTree = ""; }; 50 | 7D5557C7BDAE283B154FD653 /* Pods_CocoapodTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CocoapodTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | C57DC03E4498CE1BFA77FA1E /* Pods-CocoapodTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CocoapodTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CocoapodTests/Pods-CocoapodTests.debug.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 3C1245771C209F1F00525217 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | E40A379BD928D05C4C108DB2 /* Pods_CocoapodTests.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 09EAA0F2DD47C0B231DA86BB /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 7D5557C7BDAE283B154FD653 /* Pods_CocoapodTests.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 3C12453A1C209E1E00525217 = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3C12455B1C209E7E00525217 /* StoryboardKitTests */, 78 | 3C1245821C209F4600525217 /* Supporting Files */, 79 | 3C1245451C209E1E00525217 /* Products */, 80 | F09A357FF4D3F33BE53EAE35 /* Pods */, 81 | 09EAA0F2DD47C0B231DA86BB /* Frameworks */, 82 | ); 83 | sourceTree = ""; 84 | }; 85 | 3C1245451C209E1E00525217 /* Products */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 3C12457A1C209F1F00525217 /* CocoapodTests.xctest */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 3C12455B1C209E7E00525217 /* StoryboardKitTests */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 3C12455C1C209E7E00525217 /* ApplicationInfoTests.swift */, 97 | 3C12455D1C209E7E00525217 /* ClassInfoTests.swift */, 98 | 3C12455F1C209E7E00525217 /* Images.xcassets */, 99 | 3C1245601C209E7E00525217 /* Info.plist */, 100 | 3C1245611C209E7E00525217 /* StoryboardFileParserTests.swift */, 101 | 3C1245621C209E7E00525217 /* StoryboardInfoTests.swift */, 102 | 3C1245631C209E7E00525217 /* StoryboardInstanceInfoTests.swift */, 103 | 3C1245641C209E7E00525217 /* StoryboardKit.storyboard */, 104 | 3C1245651C209E7E00525217 /* TableViewInstanceInfoTests.swift */, 105 | 3C1245661C209E7E00525217 /* ViewInstanceInfoTests.swift */, 106 | ); 107 | name = StoryboardKitTests; 108 | path = ../StoryboardKitTests; 109 | sourceTree = ""; 110 | }; 111 | 3C1245821C209F4600525217 /* Supporting Files */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 3C1245731C209E9D00525217 /* StoryboardKit.podspec */, 115 | ); 116 | name = "Supporting Files"; 117 | sourceTree = ""; 118 | }; 119 | F09A357FF4D3F33BE53EAE35 /* Pods */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | C57DC03E4498CE1BFA77FA1E /* Pods-CocoapodTests.debug.xcconfig */, 123 | 7360C8C399AA4BCDA1485EA6 /* Pods-CocoapodTests.release.xcconfig */, 124 | ); 125 | name = Pods; 126 | sourceTree = ""; 127 | }; 128 | /* End PBXGroup section */ 129 | 130 | /* Begin PBXNativeTarget section */ 131 | 3C1245791C209F1F00525217 /* CocoapodTests */ = { 132 | isa = PBXNativeTarget; 133 | buildConfigurationList = 3C12457F1C209F1F00525217 /* Build configuration list for PBXNativeTarget "CocoapodTests" */; 134 | buildPhases = ( 135 | 1B05B9F136D1834A82215664 /* [CP] Check Pods Manifest.lock */, 136 | 3C1245761C209F1F00525217 /* Sources */, 137 | 3C1245771C209F1F00525217 /* Frameworks */, 138 | 3C1245781C209F1F00525217 /* Resources */, 139 | 3C12458E1C20A06200525217 /* Copy iOS Storyboaard */, 140 | 94CEB39D5BE692C2A16290F5 /* [CP] Embed Pods Frameworks */, 141 | 5F4B0CC39C79F9287BE27420 /* [CP] Copy Pods Resources */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = CocoapodTests; 148 | productName = CocoapodTests; 149 | productReference = 3C12457A1C209F1F00525217 /* CocoapodTests.xctest */; 150 | productType = "com.apple.product-type.bundle.unit-test"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 3C12453B1C209E1E00525217 /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastSwiftUpdateCheck = 0720; 159 | LastUpgradeCheck = 0730; 160 | ORGANIZATIONNAME = Adorkable; 161 | TargetAttributes = { 162 | 3C1245791C209F1F00525217 = { 163 | CreatedOnToolsVersion = 7.2; 164 | }; 165 | }; 166 | }; 167 | buildConfigurationList = 3C12453E1C209E1E00525217 /* Build configuration list for PBXProject "StoryboardKit" */; 168 | compatibilityVersion = "Xcode 3.2"; 169 | developmentRegion = English; 170 | hasScannedForEncodings = 0; 171 | knownRegions = ( 172 | en, 173 | ); 174 | mainGroup = 3C12453A1C209E1E00525217; 175 | productRefGroup = 3C1245451C209E1E00525217 /* Products */; 176 | projectDirPath = ""; 177 | projectRoot = ""; 178 | targets = ( 179 | 3C1245791C209F1F00525217 /* CocoapodTests */, 180 | ); 181 | }; 182 | /* End PBXProject section */ 183 | 184 | /* Begin PBXResourcesBuildPhase section */ 185 | 3C1245781C209F1F00525217 /* Resources */ = { 186 | isa = PBXResourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 3C1245861C209F5400525217 /* Images.xcassets in Resources */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXResourcesBuildPhase section */ 194 | 195 | /* Begin PBXShellScriptBuildPhase section */ 196 | 1B05B9F136D1834A82215664 /* [CP] Check Pods Manifest.lock */ = { 197 | isa = PBXShellScriptBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | ); 201 | inputPaths = ( 202 | ); 203 | name = "[CP] Check Pods Manifest.lock"; 204 | outputPaths = ( 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | shellPath = /bin/sh; 208 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 209 | showEnvVarsInLog = 0; 210 | }; 211 | 5F4B0CC39C79F9287BE27420 /* [CP] Copy Pods Resources */ = { 212 | isa = PBXShellScriptBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | ); 216 | inputPaths = ( 217 | ); 218 | name = "[CP] Copy Pods Resources"; 219 | outputPaths = ( 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | shellPath = /bin/sh; 223 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CocoapodTests/Pods-CocoapodTests-resources.sh\"\n"; 224 | showEnvVarsInLog = 0; 225 | }; 226 | 94CEB39D5BE692C2A16290F5 /* [CP] Embed Pods Frameworks */ = { 227 | isa = PBXShellScriptBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | ); 231 | inputPaths = ( 232 | ); 233 | name = "[CP] Embed Pods Frameworks"; 234 | outputPaths = ( 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | shellPath = /bin/sh; 238 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CocoapodTests/Pods-CocoapodTests-frameworks.sh\"\n"; 239 | showEnvVarsInLog = 0; 240 | }; 241 | /* End PBXShellScriptBuildPhase section */ 242 | 243 | /* Begin PBXSourcesBuildPhase section */ 244 | 3C1245761C209F1F00525217 /* Sources */ = { 245 | isa = PBXSourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 3C1245841C209F5400525217 /* ClassInfoTests.swift in Sources */, 249 | 3C1245831C209F5400525217 /* ApplicationInfoTests.swift in Sources */, 250 | 3C1245881C209F5400525217 /* StoryboardFileParserTests.swift in Sources */, 251 | 3C1245891C209F5400525217 /* StoryboardInfoTests.swift in Sources */, 252 | 3C12458D1C209F5400525217 /* ViewInstanceInfoTests.swift in Sources */, 253 | 3C12458C1C209F5400525217 /* TableViewInstanceInfoTests.swift in Sources */, 254 | 3C12458A1C209F5400525217 /* StoryboardInstanceInfoTests.swift in Sources */, 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | /* End PBXSourcesBuildPhase section */ 259 | 260 | /* Begin XCBuildConfiguration section */ 261 | 3C12454A1C209E1E00525217 /* Debug */ = { 262 | isa = XCBuildConfiguration; 263 | buildSettings = { 264 | ALWAYS_SEARCH_USER_PATHS = NO; 265 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 266 | CLANG_CXX_LIBRARY = "libc++"; 267 | CLANG_ENABLE_MODULES = YES; 268 | CLANG_ENABLE_OBJC_ARC = YES; 269 | CLANG_WARN_BOOL_CONVERSION = YES; 270 | CLANG_WARN_CONSTANT_CONVERSION = YES; 271 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 272 | CLANG_WARN_EMPTY_BODY = YES; 273 | CLANG_WARN_ENUM_CONVERSION = YES; 274 | CLANG_WARN_INT_CONVERSION = YES; 275 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 276 | CLANG_WARN_UNREACHABLE_CODE = YES; 277 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 278 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 279 | COPY_PHASE_STRIP = NO; 280 | CURRENT_PROJECT_VERSION = 1; 281 | DEBUG_INFORMATION_FORMAT = dwarf; 282 | ENABLE_STRICT_OBJC_MSGSEND = YES; 283 | ENABLE_TESTABILITY = YES; 284 | GCC_C_LANGUAGE_STANDARD = gnu99; 285 | GCC_DYNAMIC_NO_PIC = NO; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_OPTIMIZATION_LEVEL = 0; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "DEBUG=1", 290 | "$(inherited)", 291 | ); 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 299 | MTL_ENABLE_DEBUG_INFO = YES; 300 | ONLY_ACTIVE_ARCH = YES; 301 | SDKROOT = iphoneos; 302 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 303 | TARGETED_DEVICE_FAMILY = "1,2"; 304 | VERSIONING_SYSTEM = "apple-generic"; 305 | VERSION_INFO_PREFIX = ""; 306 | }; 307 | name = Debug; 308 | }; 309 | 3C12454B1C209E1E00525217 /* Release */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 314 | CLANG_CXX_LIBRARY = "libc++"; 315 | CLANG_ENABLE_MODULES = YES; 316 | CLANG_ENABLE_OBJC_ARC = YES; 317 | CLANG_WARN_BOOL_CONVERSION = YES; 318 | CLANG_WARN_CONSTANT_CONVERSION = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_EMPTY_BODY = YES; 321 | CLANG_WARN_ENUM_CONVERSION = YES; 322 | CLANG_WARN_INT_CONVERSION = YES; 323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 324 | CLANG_WARN_UNREACHABLE_CODE = YES; 325 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 326 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 327 | COPY_PHASE_STRIP = NO; 328 | CURRENT_PROJECT_VERSION = 1; 329 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 330 | ENABLE_NS_ASSERTIONS = NO; 331 | ENABLE_STRICT_OBJC_MSGSEND = YES; 332 | GCC_C_LANGUAGE_STANDARD = gnu99; 333 | GCC_NO_COMMON_BLOCKS = YES; 334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 336 | GCC_WARN_UNDECLARED_SELECTOR = YES; 337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 338 | GCC_WARN_UNUSED_FUNCTION = YES; 339 | GCC_WARN_UNUSED_VARIABLE = YES; 340 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 341 | MTL_ENABLE_DEBUG_INFO = NO; 342 | SDKROOT = iphoneos; 343 | TARGETED_DEVICE_FAMILY = "1,2"; 344 | VALIDATE_PRODUCT = YES; 345 | VERSIONING_SYSTEM = "apple-generic"; 346 | VERSION_INFO_PREFIX = ""; 347 | }; 348 | name = Release; 349 | }; 350 | 3C1245801C209F1F00525217 /* Debug */ = { 351 | isa = XCBuildConfiguration; 352 | baseConfigurationReference = C57DC03E4498CE1BFA77FA1E /* Pods-CocoapodTests.debug.xcconfig */; 353 | buildSettings = { 354 | CODE_SIGN_IDENTITY = "-"; 355 | COMBINE_HIDPI_IMAGES = YES; 356 | INFOPLIST_FILE = ../StoryboardKitTests/Info.plist; 357 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 358 | MACOSX_DEPLOYMENT_TARGET = 10.11; 359 | PRODUCT_BUNDLE_IDENTIFIER = cc.adorkable.StoryboardKit.CocoapodTests; 360 | PRODUCT_NAME = "$(TARGET_NAME)"; 361 | SDKROOT = macosx; 362 | }; 363 | name = Debug; 364 | }; 365 | 3C1245811C209F1F00525217 /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | baseConfigurationReference = 7360C8C399AA4BCDA1485EA6 /* Pods-CocoapodTests.release.xcconfig */; 368 | buildSettings = { 369 | CODE_SIGN_IDENTITY = "-"; 370 | COMBINE_HIDPI_IMAGES = YES; 371 | INFOPLIST_FILE = ../StoryboardKitTests/Info.plist; 372 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 373 | MACOSX_DEPLOYMENT_TARGET = 10.11; 374 | PRODUCT_BUNDLE_IDENTIFIER = cc.adorkable.StoryboardKit.CocoapodTests; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | SDKROOT = macosx; 377 | }; 378 | name = Release; 379 | }; 380 | /* End XCBuildConfiguration section */ 381 | 382 | /* Begin XCConfigurationList section */ 383 | 3C12453E1C209E1E00525217 /* Build configuration list for PBXProject "StoryboardKit" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | 3C12454A1C209E1E00525217 /* Debug */, 387 | 3C12454B1C209E1E00525217 /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | 3C12457F1C209F1F00525217 /* Build configuration list for PBXNativeTarget "CocoapodTests" */ = { 393 | isa = XCConfigurationList; 394 | buildConfigurations = ( 395 | 3C1245801C209F1F00525217 /* Debug */, 396 | 3C1245811C209F1F00525217 /* Release */, 397 | ); 398 | defaultConfigurationIsVisible = 0; 399 | defaultConfigurationName = Release; 400 | }; 401 | /* End XCConfigurationList section */ 402 | }; 403 | rootObject = 3C12453B1C209E1E00525217 /* Project object */; 404 | } 405 | -------------------------------------------------------------------------------- /StoryboardKit/StoryboardFileVersionedParsers/3.0/StoryboardFile3_0Parser_Views.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StoryboardFile3_0Parser_Views.swift 3 | // StoryboardKit 4 | // 5 | // Created by Ian on 6/30/15. 6 | // Copyright (c) 2015 Adorkable. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import SWXMLHash 12 | 13 | // TODO: move to keypath based parsing 14 | internal extension StoryboardFile3_0Parser { 15 | // MARK: Views 16 | 17 | class ViewInstanceParseInfo { 18 | let id : String 19 | let useClass : String 20 | let viewClass : ViewClassInfo 21 | let frame : CGRect? 22 | let autoResizingMaskWidthSizable : Bool 23 | let autoResizingMaskHeightSizable : Bool 24 | let subviews : [ViewInstanceInfo]? 25 | var backgroundColor : NSColor? 26 | 27 | init(id : String, frame : CGRect?, useClass : String, viewClass : ViewClassInfo, autoResizingMaskWidthSizable : Bool, autoResizingMaskHeightSizable : Bool, subviews : [ViewInstanceInfo]?, backgroundColor : NSColor?) { 28 | self.id = id 29 | 30 | self.frame = frame 31 | 32 | self.useClass = useClass 33 | self.viewClass = viewClass 34 | 35 | self.autoResizingMaskWidthSizable = autoResizingMaskWidthSizable 36 | self.autoResizingMaskHeightSizable = autoResizingMaskHeightSizable 37 | 38 | self.subviews = subviews 39 | 40 | self.backgroundColor = backgroundColor 41 | } 42 | } 43 | 44 | internal func parseSubviews(subnode : XMLIndexer) -> [ViewInstanceInfo]? { 45 | var result : [ViewInstanceInfo]? 46 | 47 | var subviews = [ViewInstanceInfo]() 48 | for subviewNode in subnode.children 49 | { 50 | if let subviewElement = subviewNode.element 51 | { 52 | if subviewElement.name == "tableView" 53 | { 54 | if let subview = self.createTableView(subviewNode) 55 | { 56 | subviews.append(subview) 57 | } else 58 | { 59 | // TODO: 60 | } 61 | } else if subviewElement.name == "collectionView" 62 | { 63 | if let subview = self.createCollectionView(subviewNode) 64 | { 65 | subviews.append(subview) 66 | } else 67 | { 68 | // TODO: 69 | } 70 | } else 71 | { 72 | if subviewElement.name != "view" 73 | { 74 | self.Log("Unhandled subview type \(subviewElement.name), defaulting to basic view") 75 | } 76 | if let subview = self.createView(subviewNode) 77 | { 78 | subviews.append(subview) 79 | } else 80 | { 81 | // TODO: 82 | } 83 | } 84 | } 85 | } 86 | if subviews.count > 0 87 | { 88 | result = subviews 89 | } 90 | 91 | return result 92 | } 93 | 94 | internal func parseView(view : XMLIndexer, viewClassInfoClass : ViewClassInfo.Type) -> ViewInstanceParseInfo? { 95 | var result : ViewInstanceParseInfo? 96 | 97 | if let element = view.element, 98 | let id = element.allAttributes["id"]?.text 99 | { 100 | // TODO: support all View classes 101 | var useClass : String 102 | if let customClass = element.allAttributes["customClass"]?.text 103 | { 104 | useClass = customClass 105 | } else 106 | { 107 | useClass = viewClassInfoClass.defaultClass 108 | } 109 | 110 | var viewClass : ViewClassInfo 111 | if let foundViewClass = self.applicationInfo.viewClassWithClassName(useClass) 112 | { 113 | viewClass = foundViewClass 114 | } else 115 | { 116 | viewClass = viewClassInfoClass.init(className: useClass) 117 | self.applicationInfo.add(viewClass: viewClass) 118 | } 119 | 120 | var frame : CGRect? 121 | var autoResizingMaskWidthSizable : Bool = false 122 | var autoResizingMaskHeightSizable : Bool = false 123 | var subviews : [ViewInstanceInfo]? 124 | var backgroundColor : NSColor? 125 | 126 | // TODO: support subclass parser handling of subnode rather than traversing multiple times 127 | for subnode in view.children 128 | { 129 | if let subelement = subnode.element 130 | { 131 | if subelement.name == "rect" && subelement.allAttributes["key"]?.text == "frame" 132 | { 133 | 134 | frame = self.createRect(subnode) 135 | 136 | } else if subelement.name == "autoresizingMask" && subelement.allAttributes["key"]?.text == "autoresizingMask" 137 | { 138 | 139 | self.getAutoresizingMaskValues(subnode, widthSizable: &autoResizingMaskWidthSizable, heightSizable: &autoResizingMaskHeightSizable) 140 | 141 | } else if subelement.name == "subviews" 142 | { 143 | 144 | subviews = self.parseSubviews(subnode) 145 | } else if subelement.name == "color" 146 | { 147 | let color = self.createColor(subnode) 148 | if subelement.allAttributes["key"]?.text == "backgroundColor" 149 | { 150 | backgroundColor = color 151 | } 152 | } else if subelement.name == "constraints" 153 | { 154 | // TODO: 155 | } 156 | } 157 | } 158 | // var backgroundColor : NSColor? // TODO: Efff, why is there a UIColor? Make our own color object? 159 | // var constraints : [NSLayoutConstraint]? // TODO: these definitely need to be our own objects 160 | result = ViewInstanceParseInfo(id: id, frame: frame, useClass: useClass, viewClass: viewClass, autoResizingMaskWidthSizable: autoResizingMaskWidthSizable, autoResizingMaskHeightSizable: autoResizingMaskHeightSizable, subviews: subviews, backgroundColor: backgroundColor) 161 | } 162 | 163 | return result 164 | } 165 | 166 | internal func createView(view : XMLIndexer, viewClassInfoClass : ViewClassInfo.Type = ViewClassInfo.self) -> ViewInstanceInfo? { 167 | var result : ViewInstanceInfo? 168 | if let parseInfo = self.parseView(view, viewClassInfoClass: viewClassInfoClass) 169 | { 170 | let view = ViewInstanceInfo(classInfo: parseInfo.viewClass, id: parseInfo.id, frame: parseInfo.frame, autoResizingMaskWidthSizable: parseInfo.autoResizingMaskWidthSizable, autoResizingMaskHeightSizable: parseInfo.autoResizingMaskHeightSizable, subviews: parseInfo.subviews, backgroundColor: parseInfo.backgroundColor) 171 | result = view 172 | } 173 | 174 | return result 175 | } 176 | 177 | internal func createRect(rect : XMLIndexer) -> CGRect? { 178 | var result : CGRect? 179 | 180 | if let element = rect.element, 181 | let x = (element.allAttributes["x"]?.text as NSString?)?.doubleValue, 182 | let y = (element.allAttributes["y"]?.text as NSString?)?.doubleValue, 183 | let width = (element.allAttributes["width"]?.text as NSString?)?.doubleValue, 184 | let height = (element.allAttributes["height"]?.text as NSString?)?.doubleValue 185 | { 186 | result = CGRect(x: CGFloat(x), y: CGFloat(y), width: CGFloat(width), height: CGFloat(height)) 187 | } 188 | 189 | return result 190 | } 191 | 192 | internal func getAutoresizingMaskValues(autoresizingMask : XMLIndexer, inout widthSizable : Bool, inout heightSizable : Bool) { 193 | 194 | if let element = autoresizingMask.element 195 | { 196 | widthSizable = element.allAttributes["widthSizable"]?.text == "YES" 197 | heightSizable = element.allAttributes["heightSizable"]?.text == "YES" 198 | } 199 | } 200 | 201 | internal func createColor(color : XMLIndexer) -> NSColor? { 202 | var result : NSColor? 203 | 204 | if let element = color.element 205 | { 206 | if let colorSpace = element.allAttributes["colorSpace"]?.text 207 | { 208 | if colorSpace == "calibratedWhite" 209 | { 210 | if let white = (element.allAttributes["white"]?.text as NSString?)?.doubleValue, 211 | let alpha = (element.allAttributes["alpha"]?.text as NSString?)?.doubleValue 212 | { 213 | result = NSColor(calibratedWhite: CGFloat(white), alpha: CGFloat(alpha)) 214 | } else 215 | { 216 | self.Log("Error: Unable to find expected members of colorspace \(colorSpace)") 217 | } 218 | } else if colorSpace == "calibratedRGB" 219 | { 220 | if let red = (element.allAttributes["red"]?.text as NSString?)?.doubleValue, 221 | let green = (element.allAttributes["green"]?.text as NSString?)?.doubleValue, 222 | let blue = (element.allAttributes["green"]?.text as NSString?)?.doubleValue, 223 | let alpha = (element.allAttributes["alpha"]?.text as NSString?)?.doubleValue 224 | { 225 | result = NSColor(calibratedRed: CGFloat(red), green: CGFloat(green), blue: CGFloat(blue), alpha: CGFloat(alpha) ) 226 | } else 227 | { 228 | self.Log("Error: Unable to find expected members of colorspace \(colorSpace)") 229 | } 230 | } else if colorSpace == "custom", 231 | let customColorSpace = element.allAttributes["customColorSpace"]?.text 232 | { 233 | if customColorSpace == "genericCMYKColorSpace", 234 | let cyan = (element.allAttributes["cyan"]?.text as NSString?)?.doubleValue, 235 | let magenta = (element.allAttributes["magenta"]?.text as NSString?)?.doubleValue, 236 | let yellow = (element.allAttributes["yellow"]?.text as NSString?)?.doubleValue, 237 | let black = (element.allAttributes["black"]?.text as NSString?)?.doubleValue, 238 | let alpha = (element.allAttributes["alpha"]?.text as NSString?)?.doubleValue 239 | { 240 | // TODO: what's "device" NSColor's difference? 241 | result = NSColor(deviceCyan: CGFloat(cyan), magenta: CGFloat(magenta), yellow: CGFloat(yellow), black: CGFloat(black), alpha: CGFloat(alpha) ) 242 | } else 243 | { 244 | self.Log("Error: Unknown custom colorspace \(customColorSpace)") 245 | } 246 | 247 | } else 248 | { 249 | self.Log("Error: Unknown colorspace \(colorSpace)") 250 | } 251 | } else if let _ = element.allAttributes["cocoaTouchSystemColor"]?.text 252 | { 253 | // cocoaTouchSystemColor="darkTextColor" 254 | } else 255 | { 256 | self.Log("Unsupported color format: \(element)") 257 | } 258 | } 259 | 260 | return result 261 | } 262 | 263 | class TableViewInstanceParseInfo { 264 | // TODO: feels like a hack to contain the superclass's parse info 265 | let viewInstanceParseInfo : ViewInstanceParseInfo 266 | let cellPrototypes : [TableViewInstanceInfo.TableViewCellPrototypeInfo]? 267 | 268 | init(viewInstanceParseInfo : ViewInstanceParseInfo, cellPrototypes : [TableViewInstanceInfo.TableViewCellPrototypeInfo]?) { 269 | self.viewInstanceParseInfo = viewInstanceParseInfo 270 | self.cellPrototypes = cellPrototypes 271 | } 272 | } 273 | 274 | internal func parseTableView(tableView : XMLIndexer) -> TableViewInstanceParseInfo? { 275 | var result : TableViewInstanceParseInfo? 276 | 277 | if let parseInfo = self.parseView(tableView, viewClassInfoClass: TableViewClassInfo.self) 278 | { 279 | var cellPrototypes : [TableViewInstanceInfo.TableViewCellPrototypeInfo]? 280 | 281 | for subnode in tableView.children 282 | { 283 | if let subelement = subnode.element 284 | { 285 | if subelement.name == "prototypes" 286 | { 287 | cellPrototypes = self.createTableViewCellPrototypes(subnode) 288 | } 289 | } 290 | } 291 | 292 | result = TableViewInstanceParseInfo(viewInstanceParseInfo: parseInfo, cellPrototypes: cellPrototypes) 293 | } 294 | 295 | return result 296 | } 297 | 298 | internal func createTableViewCellPrototypes(prototypes : XMLIndexer) -> [TableViewInstanceInfo.TableViewCellPrototypeInfo]? { 299 | var result : [TableViewInstanceInfo.TableViewCellPrototypeInfo]? 300 | 301 | for subnode in prototypes.children 302 | { 303 | if let subelement = subnode.element 304 | { 305 | if subelement.name == "tableViewCell" 306 | { 307 | if let cellPrototype = self.createTableViewCellPrototype(subnode) 308 | { 309 | if result == nil 310 | { 311 | result = Array() 312 | } 313 | 314 | result!.append(cellPrototype) 315 | } 316 | } else 317 | { 318 | self.Log("Error: Unknown prototype type \(subelement.name)") 319 | } 320 | } 321 | } 322 | 323 | return result 324 | } 325 | 326 | internal func createTableViewCellPrototype(tableViewCell : XMLIndexer) -> TableViewInstanceInfo.TableViewCellPrototypeInfo? { 327 | var result : TableViewInstanceInfo.TableViewCellPrototypeInfo? 328 | 329 | if let element = tableViewCell.element, 330 | let id = element.allAttributes["id"]?.text 331 | { 332 | let reuseIdentifier = element.allAttributes["reuseIdentifier"]?.text 333 | result = TableViewInstanceInfo.TableViewCellPrototypeInfo(id: id, reuseIdentifier: reuseIdentifier) 334 | } 335 | 336 | return result 337 | } 338 | 339 | internal func createTableView(tableView : XMLIndexer) -> TableViewInstanceInfo? { 340 | var result : TableViewInstanceInfo? 341 | if let parseInfo = self.parseTableView(tableView) 342 | { 343 | let viewInstanceParseInfo = parseInfo.viewInstanceParseInfo 344 | 345 | let tableView = TableViewInstanceInfo(classInfo: viewInstanceParseInfo.viewClass, id: viewInstanceParseInfo.id, frame: viewInstanceParseInfo.frame, autoResizingMaskWidthSizable: viewInstanceParseInfo.autoResizingMaskWidthSizable, autoResizingMaskHeightSizable: viewInstanceParseInfo.autoResizingMaskHeightSizable, subviews: viewInstanceParseInfo.subviews, backgroundColor: viewInstanceParseInfo.backgroundColor, cellPrototypes: parseInfo.cellPrototypes) 346 | result = tableView 347 | } 348 | 349 | return result 350 | } 351 | 352 | class CollectionViewInstanceParseInfo { 353 | // TODO: feels like a hack to contain the superclass's parse info 354 | let viewInstanceParseInfo : ViewInstanceParseInfo 355 | let cellPrototypes : [CollectionViewInstanceInfo.CollectionViewCellPrototypeInfo]? 356 | 357 | init(viewInstanceParseInfo : ViewInstanceParseInfo, cellPrototypes : [CollectionViewInstanceInfo.CollectionViewCellPrototypeInfo]?) { 358 | self.viewInstanceParseInfo = viewInstanceParseInfo 359 | self.cellPrototypes = cellPrototypes 360 | } 361 | } 362 | 363 | internal func parseCollectionView(collectionView : XMLIndexer) -> CollectionViewInstanceParseInfo? { 364 | var result : CollectionViewInstanceParseInfo? 365 | 366 | if let parseInfo = self.parseView(collectionView, viewClassInfoClass: CollectionViewClassInfo.self) 367 | { 368 | var cellPrototypes : [CollectionViewInstanceInfo.CollectionViewCellPrototypeInfo]? 369 | 370 | for subnode in collectionView.children 371 | { 372 | if let subelement = subnode.element 373 | { 374 | if subelement.name == "cells" 375 | { 376 | cellPrototypes = self.createCollectionViewCellPrototypes(subnode) 377 | } 378 | } 379 | } 380 | 381 | result = CollectionViewInstanceParseInfo(viewInstanceParseInfo: parseInfo, cellPrototypes: cellPrototypes) 382 | } 383 | 384 | return result 385 | } 386 | 387 | internal func createCollectionViewCellPrototypes(prototypes : XMLIndexer) -> [CollectionViewInstanceInfo.CollectionViewCellPrototypeInfo]? { 388 | var result : [CollectionViewInstanceInfo.CollectionViewCellPrototypeInfo]? 389 | 390 | for subnode in prototypes.children 391 | { 392 | if let subelement = subnode.element 393 | { 394 | if subelement.name == "collectionViewCell" 395 | { 396 | if let cellPrototype = self.createCollectionViewCellPrototype(subnode) 397 | { 398 | if result == nil 399 | { 400 | result = Array() 401 | } 402 | 403 | result!.append(cellPrototype) 404 | } 405 | } else 406 | { 407 | self.Log("Error: Unknown prototype type \(subelement.name)") 408 | } 409 | } 410 | } 411 | 412 | return result 413 | } 414 | 415 | internal func createCollectionViewCellPrototype(collectionViewCell : XMLIndexer) -> CollectionViewInstanceInfo.CollectionViewCellPrototypeInfo? { 416 | var result : CollectionViewInstanceInfo.CollectionViewCellPrototypeInfo? 417 | 418 | if let element = collectionViewCell.element, 419 | let id = element.allAttributes["id"]?.text 420 | { 421 | let reuseIdentifier = element.allAttributes["reuseIdentifier"]?.text 422 | result = CollectionViewInstanceInfo.CollectionViewCellPrototypeInfo(id: id, reuseIdentifier: reuseIdentifier) 423 | } 424 | 425 | return result 426 | } 427 | 428 | internal func createCollectionView(collectionView : XMLIndexer) -> CollectionViewInstanceInfo? { 429 | var result : CollectionViewInstanceInfo? 430 | if let parseInfo = self.parseCollectionView(collectionView) 431 | { 432 | let viewInstanceParseInfo = parseInfo.viewInstanceParseInfo 433 | 434 | let collectionView = CollectionViewInstanceInfo(classInfo: viewInstanceParseInfo.viewClass, id: viewInstanceParseInfo.id, frame: viewInstanceParseInfo.frame, autoResizingMaskWidthSizable: viewInstanceParseInfo.autoResizingMaskWidthSizable, autoResizingMaskHeightSizable: viewInstanceParseInfo.autoResizingMaskHeightSizable, subviews: viewInstanceParseInfo.subviews, backgroundColor: viewInstanceParseInfo.backgroundColor, cellPrototypes: parseInfo.cellPrototypes) 435 | result = collectionView 436 | } 437 | 438 | return result 439 | } 440 | } -------------------------------------------------------------------------------- /StoryboardKitTests/StoryboardKit.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | --------------------------------------------------------------------------------