├── .gitignore ├── .travis.yml ├── CastingApp ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Classy.cast ├── Info.plist └── ViewController.swift ├── CastingAppTests ├── CastingAppTests.swift └── Info.plist ├── LICENSE ├── README.md ├── cast ├── cast-script ├── Cast.h ├── Cast.swift ├── Info.plist └── main.swift ├── cast.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── Cast (OSX).xcscheme │ ├── Cast (iOS).xcscheme │ ├── Cast (tvOS).xcscheme │ ├── CastingApp.xcscheme │ └── cast.xcscheme └── images ├── 1.png └── 2.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | xcuserdata 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | xcode_project: cast.xcodeproj 4 | xcode_scheme: CastingApp 5 | osx_image: xcode8.2 6 | xcode_sdk: iphonesimulator10.1 7 | 8 | script: 9 | - xcodebuild clean build test -project cast.xcodeproj -scheme CastingApp -destination 'platform=iOS Simulator,name=iPhone 7,OS=10.1' -------------------------------------------------------------------------------- /CastingApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CastingApp 4 | // 5 | // Created by Guy on 21/05/2016. 6 | // Copyright © 2016 Houzz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Cast 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | extension Classy { 48 | static func parseCust(from dict: JSONDictionary) -> Int? { 49 | return 0 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /CastingApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /CastingApp/Base.lproj/LaunchScreen.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 | 27 | 28 | -------------------------------------------------------------------------------- /CastingApp/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /CastingApp/Classy.cast: -------------------------------------------------------------------------------- 1 | // 2 | // Classy.swift 3 | // delme 4 | // 5 | // Created by Guy on 10/05/2016. 6 | // Copyright © 2016 Houzz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreGraphics 11 | 12 | public enum What: String { 13 | case yes = "yes" 14 | case no = "no" 15 | case maybe = "maybe" 16 | } 17 | 18 | @objc public class Classy: NSObject, DictionaryConvertible, NSCoding, NSCopying { 19 | lazy var skip: Int = 8 //! skip json 20 | var powerMode: Int 21 | var cust: Int //! custom 22 | let x: Int! = nil 23 | let y: String? = "as" //! "Arg ?? y" 24 | let why: What? 25 | let u: URL? 26 | let a: [Int]? 27 | let cx: Classx? 28 | let nested: CGFloat? //! "outer/inner ?? x/y" 29 | private(set) var d2: [String: Any]? 30 | var derived: String = "" //! ignore 31 | var second: String? //! "second[1]" 32 | var lowercasedProperty: What = .yes //! v"will" 33 | //! awake 34 | 35 | var xx:Int { //! ignore 36 | get { 37 | return x 38 | } 39 | } 40 | 41 | //! init 42 | } 43 | 44 | public class Classx: NSObject, DictionaryConvertible, NSCoding { 45 | let name: String = "" 46 | let age: Int = 0 47 | var opt: Int? 48 | //! init 49 | } 50 | 51 | public class Classz: Classy { 52 | var another: String! = nil 53 | //! nscoding 54 | } 55 | 56 | public class SubClass: Classx { 57 | let yaw: Int 58 | //! nscoding 59 | //! super "classx" 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /CastingApp/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /CastingApp/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // CastingApp 4 | // 5 | // Created by Guy on 21/05/2016. 6 | // Copyright © 2016 Houzz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /CastingAppTests/CastingAppTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CastingAppTests.swift 3 | // CastingAppTests 4 | // 5 | // Created by Guy on 21/05/2016. 6 | // Copyright © 2016 Houzz. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import CastingApp 11 | 12 | class CastingAppTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testEncode() { 25 | let an = Classy(dictionary: ["X": 3]) 26 | XCTAssert(an == nil) 27 | 28 | let a = Classy(dictionary: ["X": 3, "PowerMode": "0"])! 29 | 30 | XCTAssert(a.x == 3) 31 | XCTAssert(a.why == nil) 32 | XCTAssert(a.y == "as") 33 | XCTAssert(a.powerMode == 0) 34 | XCTAssert(a.u == nil) 35 | let dict = a.dictionaryRepresentation() 36 | XCTAssert(dict["X"] as! Int == 3) 37 | 38 | let b = Classz(dictionary: [ 39 | "X": "3", 40 | "Arg": "word", 41 | "PowerMode": 3, 42 | "Why": "maybe", 43 | "U": "http://houzz.com", 44 | "Outer": ["Inner": 4], 45 | "A": ["1", 2, 3], 46 | "Cx": ["Name": "Joe", "Age": 15], 47 | "Another": "another", 48 | "Second": ["first","second","third"], 49 | "D2": ["K": "v", "K2": 3]])! 50 | XCTAssert(b.x == 3) 51 | XCTAssert(b.a! == [1, 2, 3]) 52 | XCTAssert(b.cx?.age == 15) 53 | XCTAssert(b.why! == .maybe) 54 | XCTAssert(b.y! == "word") 55 | XCTAssert(b.powerMode == 3) 56 | XCTAssert(b.u!.absoluteString == "http://houzz.com") 57 | XCTAssert(b.nested! == 4) 58 | XCTAssert(b.another == "another") 59 | XCTAssert(b.d2!["K2"] as! Int == 3) 60 | let dict2 = b.dictionaryRepresentation() 61 | XCTAssert(dict2["X"] as! Int == 3) 62 | XCTAssert((dict2["U"] as! String) == "http://houzz.com") 63 | XCTAssert(b.second == "second") 64 | 65 | let d = b.copy() as! Classz 66 | XCTAssert(b.x == d.x) 67 | XCTAssert(b.u == d.u) 68 | 69 | let e = Classz(dictionary: dict2)! 70 | XCTAssert(b.x == e.x) 71 | XCTAssert(b.u == e.u) 72 | XCTAssert(e.nested! == 4) 73 | } 74 | 75 | func testJson() { 76 | let a = Classx(json: "{ \"Name\": \"Donald\", \"Age\": 60 }")! 77 | XCTAssert(a.age == 60) 78 | } 79 | 80 | func testSuperTag() { 81 | let a = SubClass(json: "{ \"Yaw\": 3, \"classx\": { \"Name\": \"Donald\", \"Age\": 60 } }")! 82 | XCTAssert(a.age == 60) 83 | XCTAssert(a.yaw == 3) 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /CastingAppTests/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 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Houzz Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSON Cast 2 | ![build status](https://travis-ci.org/Houzz/JSONCast.svg?branch=master) 3 | 4 | We explain a bit more about JSON Cast in our [blog post]( http://blog.houzz.com/post/148054770808/a-json-parsing-class-generator). 5 | 6 | Easily create classes from parsed JSON and conform to `NSCopying` and `NSEncoding`. In a nutshell, using JSON Cast you create a `.cast` file that is basically a Swift definition of your class properties, and Cast will create a `.swift` file from it that has the necessary `init` methods to init from JSON data or string, init from a dictionary as well as optionally `NSCoding`. Added as an Xcode build rule, so Swift files are automatically generated whenever the cast file is updated. 7 | 8 | - Supports Swift classes and structs (`NSCoding` / `NSCopying` only supported with classes) 9 | - Supports let and var properties 10 | - Supports any enum type - enums backed by other types are automatically supported while any other Swift enums can be easily supported 11 | - Automatically map JSON keys to properties based on property names, or easily add custom key mappings. 12 | - Supports required and optional JSON keys 13 | 14 | ## Swift 3 15 | 16 | JSON cast is ported to Swift 3, both the code it generates is Swift 3 and it is expecting the Xcode 8 runtime to be present. If you are still using Swift 2, there is a Swift 2 branch, though beware that this branch is not maintained, new developments will only go in the master branch. 17 | 18 | ## Basic Usage 19 | 20 | Create a `.cast` file with your properties as follows: 21 | 22 | ```swift 23 | class A: DictionaryConvertible { 24 | let b: Int 25 | let c: String? 26 | let d: URL //! "url" 27 | } 28 | ``` 29 | 30 | This will create a class that conforms to the `DictionaryConvertible` protocol that is mapped from a dictionary that has a key `"b"` that holds an `Int`, which can either be a number or a string that is converted to a number. The key `"c"` is optional and may not appear in the dictionary, it holds a string value, and a key `"url"` that is mapped to the property `d` in code. 31 | 32 | You never need to look at the generated file; it's not important. Add your methods in a different file as an extension to class `A`. 33 | 34 | ## Install 35 | 36 | 1. clone this repository. 37 | 1. Add the Cast project to your workspace. 38 | 1. For iOS projects, link with the Cast.framework, tvOS projects need to link with CastTV.framework and OS X projects with CastX.framework 39 | 1. Add a build rule to convert *.cast files as follows: 40 | 41 | ![build rule](images/1.png "build rule") 42 | 43 | Do source files matching *.cast, use script and type the following: 44 | 45 | ``` 46 | ${SRCROOT}/cast -c -n "$INPUT_FILE_PATH" "$DERIVED_SOURCES_DIR/$INPUT_FILE_BASE.swift" 47 | ``` 48 | 49 | In the output files, put `${DERIVED_SOURCES_DIR}/${INPUT_FILE_BASE}.swift` as the output file. 50 | 51 | 52 | Now create the cast script, in terminal `cd` to your project `dir` and type: 53 | 54 | ```bash 55 | ln -s path/to/cast-script/main.swift cast 56 | ``` 57 | 58 | Now try adding a `.cast` file to your project and it should compile. 59 | 60 | ### Command line options: 61 | 62 | The cast scripts accepts the following command line options: 63 | 64 | __-c__ or __-uppercase__ 65 | Capitalize the key names, so a property named "age" will use the key "Age". Without the -c key names are the same as property names, that is the property "age" will use the key "age." 66 | 67 | 68 | __-m__ or __-noimpoert__ 69 | Don't add an import Cast statement to the generated files. Useful if you chose to integrate by adding the mapper.swift file to your project instead of usig the cast framework 70 | 71 | ## Advanced Usage 72 | 73 | ### The Cast File 74 | 75 | The Cast file is a Swift class file that contains only class or struct declarations with their properties. When defining a class it should declare itself conforming to the `DictionaryConvertible` protocol. If it does not, the script assumes it inherits from a `DictionaryConvertible` conforming class and will call `super` on its methods. 76 | 77 | The `DictionaryConvertible` protocol defines two methods: `init?(dictionary: JSONDictionary)` is an initializer from a dictionary, `JSONDictionary` is a protocol that is defined in the cast framework and that `[String: Any]` conforms to, it is failable and will return nil if the dictionary does not contain all the required keys. Any property that is defined as non-optional with no default value is assumed to be required. 78 | It also defines a `dictionaryRepresentation()` method that returns a dictionary representing the object. Two additional convenience initialzers are defined in `DictionaryConvertible`: `init?(json: String)` and `init?(json: NSData)` that will use `NSJSONSerialization` to convert the JSON to a dictionary and call `init?(dictionary: [String: AnyObject])`. 79 | 80 | ### Properties 81 | 82 | Each property can be defined with a `let` or `var` and must include a type. The script isn't as smart as the compiler in inferring types. You can add a default value with `= value` If you want to define a special key mapping, add the key in a comment, such as `//! "Key"` or `//! "Level1/Level2/Key"` to define a key path into the dictionary, that is the key `Level1` is assumed to contain a dictionary that has the key `Level2` which is a dicionary which has the key `Key` which contains the value. 83 | 84 | Each property can have a default value which is assigned if the corresponding key is not found in the dictionary. Default values are provided as an initial value in Swift, e.g. `let x: String = "default"` 85 | 86 | Properties that are defined as `optional` or properties that are not optional but have a default value are also treated as optional, meaning if the corresponding key is not found in the dictionary, the default value is used. Required properties will cause the init to fail and return `nil` if their corresponding key is missing from the dictionary. 87 | 88 | If you annotate a property declaration with a `//! ignore` comment, it will be ignored by the Cast script and will not be included in the `NSCoding` encoding nor `init?(dictionary: JSONDictionary)`. Since extensions can't add stored properties to a class, you can use this to add stored properties which are derived from other properties, so are not in the dictionary. 89 | 90 | Here is a summary of the different property declarations: 91 | 92 | ```swift 93 | class AnObject: DictionaryConvertible { 94 | let a: Int 95 | let b: String? 96 | var c: Int = 0 97 | let d: [Int] //! "Other" 98 | var e: URL? //! ignore 99 | } 100 | ``` 101 | 102 | In this definition we have a readonly property `a` that will be initialized from the key `"a"` in the dictionary, and is required. If the dictionary does not have a key `"a"`, the init will fail. The property `b` is a `String` and is `optional`: if the dictionary does not have a key `"b"`, `b` will be `nil`. The read-write property `c` is an `Int`: if the dictionary does not have a key `"c"`, `c` will be assiged the default value of `0`. The property `d` will be initialized from the key `Other` in the dictionary that should contain an array of integers, and the property `e` is a derived property that will not be included in the initializer. 103 | 104 | ### Supporting Enums 105 | 106 | RawRepresentable enums are supported by default. To support pure swift enums one needs to have the enum adopt the protocol JSONValue which has methods to convert to/from a dictionary value and in order to support NSCoding one needs to also exten the NSCoding class and add an encode/decode functions for the enum type. 107 | 108 | ### JSONValue 109 | 110 | To enable cast to cast from JSON/ a new type, for example lets use Date (any `DictionaryConvertible` class is automatically supported) you need to extend that class to support the `JSONValue` protocol with two methods. For example, lets extend `Date` so we can use Date type properties, this implementation assumes dates are sent as integers representing the time since 1970. 111 | 112 | ```swift 113 | extension Date: JSONValue { 114 | public static func value(from object: Any) -> Date? { 115 | switch object { 116 | case let x as String: 117 | guard let y = Int(x) else { return nil } 118 | return NSDate(timeIntervalSince1970: NSTimeInterval(y)) 119 | 120 | case let x as Int: 121 | return NSDate(timeIntervalSince1970: NSTimeInterval(x)) 122 | 123 | case let x as NSDate: 124 | return x 125 | 126 | default: 127 | return nil 128 | } 129 | } 130 | 131 | public var jsonValue: Any? { 132 | return timeIntervalSince1970 133 | } 134 | } 135 | ``` 136 | 137 | The `value(from:)` method is used to convert from JSON to a value. Note the implementation handles both the case the JSON contains an Int and the case it contains a string with an Int, i.e. both `"date": 2` and `"date": "2"`. JSON cast works this way for all basic values, like Int, Float or Bool. 138 | 139 | ### Custom initialization 140 | 141 | You can write a custom parser to initialize a specific property, say you have a property `let powerMode: Int`, by adding a `//! custom` comment, cast will try to call a custom function that will get the dictionary as a parameter and should return an optional of the same type as the property, in this example the following will be called: `class func parsePowerMode(from dict: JSONDictionary) -> Int?`, the custom parser should return the value if the parsing was successful, or nil if not. 142 | 143 | ### Default init[^t] 144 | 145 | JSON Cast can generate a default init for classes much like Swift generates a default init for structs. The default init will get as parameters all the class properties, any optional property will be optional in the init. To enable use the `-init` command line option which will generate a default init for all classes, or annotate the class with `//! init` which will generate a default init just for the annotated class. 146 | 147 | [^t]: Thanks to [n8han](https://github.com/n8han) for contributing the default init. 148 | 149 | ### Derived Classes and Special JSON Tags for super classes 150 | 151 | You can derive from a `DictionaryConvertible` class, simply mark in the cast file the class inherits from another class. If JSON Cast does not see the `DictionaryConvertible` protocol conformance in the class decleration it assumes this is because the class inhertis from another class that is `DictionaryConvertible`. The generated init will read the current class from the dictionary and call `super.init(dicgtionary: dict)` to initialize the super class. If your JSON contains the super class tags as a sub-dictionary, you can annotate the class with `//! super "key"` which will cause JSON Cast to call super with the value of `dict[key]`. 152 | 153 | ======= 154 | ### Awake 155 | 156 | The init will call an `awake(with dictionary: JSONDictionary) -> Bool` functionary passing it the dictionary that was used to initialize the object. If the function returns true the init will succeed, if it returns false the init will fail. You can use the awakeFromDictionary method to perform last value validations after the dictionary is parsed as well as compute any derived value you need or do any post processing after the dictionary is read. 157 | 158 | ### Adopting `NSCoding` and `NSCopying` 159 | 160 | To adopt `NSCoding`, your oject has to be an Objective-C compatible object (a limitation of the `NSEncoding` protocol), so it must inherit from `NSObject`. To adopt `NSCoding`, simply add it to the list of protocols your class conforms to and the appropriate `init` and `encodeWithCoder` functions will be generated. 161 | 162 | To adopt `NSCopying`, add it to the list of protocols your class conforms to. A `copyWithZone` function will be generated. The `NSCopying` code relies on the `NSCoding` adoption, so you must adopt `NSCoding` to support `NSCopying`. 163 | 164 | ### Inheritance 165 | 166 | To inherit from a class that supports `DictionaryConvertible`, use the parent class name in the inheritance declaration, don't add a `DictionaryConvertible` declaration to the protocol list. By not adding a `DictionaryConvertible` declaration, the script knows that a parent class declares it and will use the approprate calls to `super` in the `init` and `dictionaryRepresentation` functions. 167 | 168 | If you are also adopting `NSCoding` in inherited classes, don't add the `NSCoding` protocol to the protocol list, instead add a `//! nscoding` comment to the class declaration. This will cause the script to call `super` in the `NSCoding` methods. 169 | 170 | No special action is need to support `NSCopying` in inherited classes. `NSCopying` "just" works on inherited classes, as it uses the `NSCoding` functions. 171 | 172 | ### Cast File Editing 173 | 174 | By default, Xcode treats the `.cast` file as plain text and will not highlight its syntax. You can change that by setting it to `Swift source` in the file inspector. 175 | 176 | ![file inspector](images/2.png) 177 | 178 | ## Swift 3 179 | 180 | JSON Cast is Swift 3 ready! If you are working with Xcode 8 beta and Swift 3 there is a Swift3 branch you can use which is compatible. 181 | 182 | ## Accessing Cast Classes from Objective C 183 | 184 | Since the `DictionaryConvertible` defines an init method that is not visible from Objective C since it relies on the `JSONDictionary` protocol that is not an Objective C protocol, if the cast script sees a class that was defined as `@objc` it will add a convenience `init?(dictionary: [String: Any])` initializer so the class can be initialized from Objective C 185 | 186 | ## Acknolwedgment 187 | 188 | Thanks to the great article posted [here](http://jasonlarsen.me/2015/10/16/no-magic-json-pt3.html) we've refactored JSON Cast with protocols instead of type casting which improved run time performance. 189 | 190 | 191 | -------------------------------------------------------------------------------- /cast: -------------------------------------------------------------------------------- 1 | cast-script/main.swift -------------------------------------------------------------------------------- /cast-script/Cast.h: -------------------------------------------------------------------------------- 1 | // 2 | // Cast.h 3 | // Cast 4 | // 5 | // Created by Guy on 21/05/2016. 6 | // Copyright © 2016 Houzz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Cast. 12 | FOUNDATION_EXPORT double CastVersionNumber; 13 | 14 | //! Project version string for Cast. 15 | FOUNDATION_EXPORT const unsigned char CastVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /cast-script/Cast.swift: -------------------------------------------------------------------------------- 1 | // 2 | // cast.swift 3 | // 4 | // Created by Guy on 28/10/2016. 5 | // Copyright © 2016 Houzz. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import CoreGraphics 10 | import SceneKit 11 | 12 | public protocol JSONValue { 13 | associatedtype Value = Self 14 | /** value from a json representation, typically deals with both the vaue represented as a string in the json 15 | * and with the value itself stored in the json, e.g. both "2" and 2 will reqult in a correct Int value 16 | */ 17 | static func value(from object: Any) -> Value? 18 | /** convert object to a value to store in a dictionary to convert to JSON 19 | * returning nil will not encode this property 20 | */ 21 | var jsonValue: Any? { get } 22 | } 23 | 24 | public protocol JSONKey { 25 | var value: String { get } 26 | func contains(_ str: String) -> Bool 27 | } 28 | 29 | extension String: JSONKey { 30 | public var value: String { 31 | return self 32 | } 33 | } 34 | 35 | public protocol JSONDictionary { 36 | func any(forKeyPath path: JSONKey) -> Any? 37 | func any(for key: String) -> Any? 38 | var isEmpty: Bool { get } 39 | } 40 | 41 | public extension JSONDictionary { 42 | public func any(forKeyPath path: JSONKey) -> Any? { 43 | let pathComponents = path.value.components(separatedBy: "/") 44 | var accumulator: Any = self 45 | 46 | for component in pathComponents { 47 | if let openIndex = component.range(of: "["), let closeIndex = component.range(of: "]") { 48 | let path = component[.. index { 54 | accumulator = value[index] 55 | continue 56 | } 57 | } else if let componentData = accumulator as? Self, let value = componentData.any(for: component) { 58 | accumulator = value 59 | continue 60 | } 61 | return nil 62 | } 63 | 64 | return accumulator 65 | } 66 | 67 | public func value(for key: JSONKey) -> A? { 68 | if let any = any(forKeyPath: key) { 69 | return A.value(from: any) as? A 70 | } 71 | return nil 72 | } 73 | 74 | public func value(for key: JSONKey) -> [A]? { 75 | if let any = any(forKeyPath: key) { 76 | return Array.value(from: any) 77 | } 78 | return nil 79 | } 80 | 81 | public func value(for key: JSONKey) -> A? where A.RawValue: JSONValue { 82 | if let raw: A.RawValue = value(for: key) { 83 | return A(rawValue: raw) 84 | } 85 | return nil 86 | } 87 | 88 | public func value(for key: JSONKey) -> [A]? where A.RawValue: JSONValue { 89 | if let rawArray: [A.RawValue] = value(for: key) { 90 | return rawArray.flatMap { A(rawValue: $0) } 91 | } 92 | return nil 93 | } 94 | } 95 | 96 | extension RawRepresentable { 97 | public var jsonValue: Any? { 98 | return self.rawValue 99 | } 100 | } 101 | 102 | extension RawRepresentable where RawValue: Castable { 103 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Self? { 104 | guard let i = RawValue.decode(with: decoder, fromKey: key) else { 105 | return nil 106 | } 107 | return Self(rawValue: i) 108 | } 109 | 110 | public func encode(with coder: NSCoder, forKey key: String) { 111 | self.rawValue.encode(with: coder, forKey: key) 112 | } 113 | } 114 | 115 | extension Dictionary: JSONDictionary, JSONValue { 116 | public func any(for key: String) -> Any? { 117 | guard let aKey = key as? Key else { return nil } 118 | return self[aKey] 119 | } 120 | 121 | public func encode(with coder: NSCoder, forKey key: String) { 122 | coder.encode(self, forKey: key) 123 | } 124 | 125 | public static func decode(with decoder: NSCoder, fromKey key: String) -> [Key: Value]? { 126 | return decoder.decodeObject(forKey: key) as? [Key: Value] 127 | } 128 | } 129 | 130 | extension Dictionary where Key: JSONKey { 131 | public func value(from object: Any) -> Value? { 132 | return object as? Value 133 | } 134 | } 135 | 136 | extension NSDictionary: JSONDictionary, JSONValue { 137 | public var isEmpty: Bool { 138 | return self.count == 0 139 | } 140 | 141 | public func any(for key: String) -> Any? { 142 | return self.object(forKey: key) 143 | } 144 | 145 | public func any(forKeyPath path: JSONKey) -> Any? { 146 | let pathComponents = path.value.components(separatedBy: "/") 147 | var accumulator: Any = self 148 | 149 | for component in pathComponents { 150 | if let openIndex = component.range(of: "["), let closeIndex = component.range(of: "]") { 151 | let path = String(component[.. index { 157 | accumulator = value[index] 158 | continue 159 | } 160 | } else if let componentData = accumulator as? JSONDictionary, let value = componentData.any(for: component) { 161 | accumulator = value 162 | continue 163 | } 164 | return nil 165 | } 166 | 167 | return accumulator 168 | } 169 | } 170 | 171 | extension JSONValue { 172 | public static func value(from object: Any) -> Value? { 173 | return object as? Value 174 | } 175 | 176 | public var jsonValue: Any? { 177 | return self 178 | } 179 | } 180 | 181 | public protocol Castable { 182 | func encode(with coder: NSCoder, forKey key: String) 183 | static func decode(with decoder: NSCoder, fromKey key: String) -> Self? 184 | } 185 | 186 | extension String: JSONValue, Castable { 187 | public func encode(with coder: NSCoder, forKey key: String) { 188 | coder.encode(self, forKey: key) 189 | } 190 | 191 | public static func decode(with decoder: NSCoder, fromKey key: String) -> String? { 192 | guard let i = decoder.decodeObject(forKey: key) as? String else { 193 | return nil 194 | } 195 | return i 196 | } 197 | } 198 | 199 | extension Int: JSONValue, Castable { 200 | public static func value(from object: Any) -> Int? { 201 | switch object { 202 | case let x as String: 203 | return Int(x) 204 | 205 | case let x as Int: 206 | return x 207 | 208 | default: 209 | return nil 210 | } 211 | } 212 | 213 | public func encode(with coder: NSCoder, forKey key: String) { 214 | coder.encode(self, forKey: key) 215 | } 216 | 217 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Int? { 218 | if decoder.containsValue(forKey: key) { 219 | return Int(decoder.decodeInt64(forKey: key)) 220 | } 221 | return nil 222 | } 223 | } 224 | 225 | extension Int8: JSONValue, Castable { 226 | public static func value(from object: Any) -> Int8? { 227 | switch object { 228 | case let x as String: 229 | return Int8(x) 230 | 231 | case let x as Int8: 232 | return x 233 | 234 | default: 235 | return nil 236 | } 237 | } 238 | 239 | public func encode(with coder: NSCoder, forKey key: String) { 240 | coder.encode(Int32(self), forKey: key) 241 | } 242 | 243 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Int8? { 244 | if decoder.containsValue(forKey: key) { 245 | return Int8(decoder.decodeInt32(forKey: key)) 246 | } 247 | return nil 248 | } 249 | } 250 | 251 | extension Int16: JSONValue, Castable { 252 | public static func value(from object: Any) -> Int16? { 253 | switch object { 254 | case let x as String: 255 | return Int16(x) 256 | 257 | case let x as Int16: 258 | return x 259 | 260 | default: 261 | return nil 262 | } 263 | } 264 | 265 | public func encode(with coder: NSCoder, forKey key: String) { 266 | coder.encode(self, forKey: key) 267 | } 268 | 269 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Int16? { 270 | guard decoder.containsValue(forKey: key) else { 271 | return nil 272 | } 273 | return Int16(decoder.decodeInt32(forKey: key)) 274 | } 275 | } 276 | 277 | extension Int32: JSONValue, Castable { 278 | public static func value(from object: Any) -> Int32? { 279 | switch object { 280 | case let x as String: 281 | return Int32(x) 282 | 283 | case let x as Int32: 284 | return x 285 | 286 | default: 287 | return nil 288 | } 289 | } 290 | 291 | public func encode(with coder: NSCoder, forKey key: String) { 292 | coder.encode(self, forKey: key) 293 | } 294 | 295 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Int32? { 296 | guard decoder.containsValue(forKey: key) else { 297 | return nil 298 | } 299 | return decoder.decodeInt32(forKey: key) 300 | } 301 | } 302 | 303 | extension Int64: JSONValue, Castable { 304 | public static func value(from object: Any) -> Int64? { 305 | switch object { 306 | case let x as String: 307 | return Int64(x) 308 | 309 | case let x as Int64: 310 | return x 311 | 312 | default: 313 | return nil 314 | } 315 | } 316 | 317 | public func encode(with coder: NSCoder, forKey key: String) { 318 | coder.encode(self, forKey: key) 319 | } 320 | 321 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Int64? { 322 | guard decoder.containsValue(forKey: key) else { 323 | return nil 324 | } 325 | 326 | return decoder.decodeInt64(forKey: key) 327 | } 328 | } 329 | 330 | extension Array where Element: JSONValue { 331 | public static func value(from object: Any) -> [Element]? { 332 | if let anyArray = object as? [Any] { 333 | return anyArray.flatMap { Element.value(from: $0) as? Element } 334 | } 335 | return nil 336 | } 337 | 338 | public var jsonValue: Any? { 339 | return self.map { $0.jsonValue } 340 | } 341 | } 342 | 343 | extension Array { 344 | public func encode(with coder: NSCoder, forKey key: String) { 345 | coder.encode(self, forKey: key) 346 | } 347 | 348 | public static func decode(with decoder: NSCoder, fromKey key: String) -> [Element]? { 349 | return decoder.decodeObject(forKey: key) as? [Element] 350 | } 351 | } 352 | 353 | extension Array where Element: RawRepresentable, Element.RawValue: JSONValue { 354 | public var jsonValue: Any? { 355 | return self.map { $0.jsonValue } 356 | } 357 | } 358 | 359 | extension UInt: JSONValue, Castable { 360 | public static func value(from object: Any) -> UInt? { 361 | switch object { 362 | case let x as String: 363 | return UInt(x) 364 | 365 | case let x as UInt: 366 | return x 367 | 368 | default: 369 | return nil 370 | } 371 | } 372 | 373 | public func encode(with coder: NSCoder, forKey key: String) { 374 | coder.encode(Int32(self), forKey: key) 375 | } 376 | 377 | public static func decode(with decoder: NSCoder, fromKey key: String) -> UInt? { 378 | guard decoder.containsValue(forKey: key) else { 379 | return nil 380 | } 381 | 382 | return UInt(decoder.decodeInt32(forKey: key)) 383 | } 384 | } 385 | 386 | extension UInt8: JSONValue, Castable { 387 | public static func value(from object: Any) -> UInt8? { 388 | switch object { 389 | case let x as String: 390 | return UInt8(x) 391 | 392 | case let x as UInt8: 393 | return x 394 | 395 | default: 396 | return nil 397 | } 398 | } 399 | 400 | public func encode(with coder: NSCoder, forKey key: String) { 401 | coder.encode(Int32(self), forKey: key) 402 | } 403 | 404 | public static func decode(with decoder: NSCoder, fromKey key: String) -> UInt8? { 405 | guard decoder.containsValue(forKey: key) else { 406 | return nil 407 | } 408 | return UInt8(decoder.decodeInt32(forKey: key)) 409 | } 410 | } 411 | 412 | extension UInt16: JSONValue, Castable { 413 | public static func value(from object: Any) -> UInt16? { 414 | switch object { 415 | case let x as String: 416 | return UInt16(x) 417 | 418 | case let x as UInt16: 419 | return x 420 | 421 | default: 422 | return nil 423 | } 424 | } 425 | 426 | public func encode(with coder: NSCoder, forKey key: String) { 427 | coder.encode(Int32(self), forKey: key) 428 | } 429 | 430 | public static func decode(with decoder: NSCoder, fromKey key: String) -> UInt16? { 431 | guard decoder.containsValue(forKey: key) else { 432 | return nil 433 | } 434 | return UInt16(decoder.decodeInt32(forKey: key)) 435 | } 436 | } 437 | 438 | extension UInt32: JSONValue, Castable { 439 | public static func value(from object: Any) -> UInt32? { 440 | switch object { 441 | case let x as String: 442 | return UInt32(x) 443 | 444 | case let x as UInt32: 445 | return x 446 | 447 | default: 448 | return nil 449 | } 450 | } 451 | 452 | public func encode(with coder: NSCoder, forKey key: String) { 453 | coder.encode(Int64(self), forKey: key) 454 | } 455 | 456 | public static func decode(with decoder: NSCoder, fromKey key: String) -> UInt32? { 457 | guard decoder.containsValue(forKey: key) else { 458 | return nil 459 | } 460 | return UInt32(decoder.decodeInt64(forKey: key)) 461 | } 462 | } 463 | 464 | extension UInt64: JSONValue, Castable { 465 | public static func value(from object: Any) -> UInt64? { 466 | switch object { 467 | case let x as String: 468 | return UInt64(x) 469 | 470 | case let x as UInt64: 471 | return x 472 | 473 | default: 474 | return nil 475 | } 476 | } 477 | 478 | public func encode(with coder: NSCoder, forKey key: String) { 479 | coder.encode(Int64(self), forKey: key) 480 | } 481 | 482 | public static func decode(with decoder: NSCoder, fromKey key: String) -> UInt64? { 483 | guard decoder.containsValue(forKey: key) else { 484 | return nil 485 | } 486 | return UInt64(decoder.decodeInt64(forKey: key)) 487 | } 488 | } 489 | 490 | private let trueValues = Set(["true", "True", "TRUE", "yes", "Yes", "YES", "1", "on", "On", "ON"]) 491 | 492 | extension Bool: JSONValue, Castable { 493 | public static func value(from object: Any) -> Bool? { 494 | switch object { 495 | case let x as String: 496 | return trueValues.contains(x) 497 | 498 | case let x as Bool: 499 | return x 500 | 501 | default: 502 | return nil 503 | } 504 | } 505 | 506 | public func encode(with coder: NSCoder, forKey key: String) { 507 | coder.encode(self, forKey: key) 508 | } 509 | 510 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Bool? { 511 | guard decoder.containsValue(forKey: key) else { 512 | return nil 513 | } 514 | return decoder.decodeBool(forKey: key) 515 | } 516 | } 517 | 518 | extension Float: JSONValue, Castable { 519 | public static func value(from object: Any) -> Float? { 520 | switch object { 521 | case let x as String: 522 | return Float(x) 523 | 524 | case let x as Float: 525 | return x 526 | 527 | default: 528 | return nil 529 | } 530 | } 531 | 532 | public func encode(with coder: NSCoder, forKey key: String) { 533 | coder.encode(self, forKey: key) 534 | } 535 | 536 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Float? { 537 | guard decoder.containsValue(forKey: key) else { 538 | return nil 539 | } 540 | return decoder.decodeFloat(forKey: key) 541 | } 542 | } 543 | 544 | extension Double: JSONValue, Castable { 545 | public static func value(from object: Any) -> Double? { 546 | switch object { 547 | case let x as String: 548 | return Double(x) 549 | 550 | case let x as Double: 551 | return x 552 | 553 | default: 554 | return nil 555 | } 556 | } 557 | 558 | public func encode(with coder: NSCoder, forKey key: String) { 559 | coder.encode(self, forKey: key) 560 | } 561 | 562 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Double? { 563 | guard decoder.containsValue(forKey: key) else { 564 | return nil 565 | } 566 | return decoder.decodeDouble(forKey: key) 567 | } 568 | } 569 | 570 | extension CGFloat: JSONValue, Castable { 571 | public static func value(from object: Any) -> CGFloat? { 572 | switch object { 573 | case let x as String: 574 | guard let d = Double(x) else { 575 | return nil 576 | } 577 | return CGFloat(d) 578 | 579 | case let x as Double: 580 | return CGFloat(x) 581 | 582 | case let x as Int: 583 | return CGFloat(x) 584 | 585 | case let x as CGFloat: 586 | return x 587 | 588 | default: 589 | return nil 590 | } 591 | } 592 | 593 | public func encode(with coder: NSCoder, forKey key: String) { 594 | coder.encode(Double(self), forKey: key) 595 | } 596 | 597 | public static func decode(with decoder: NSCoder, fromKey key: String) -> CGFloat? { 598 | guard decoder.containsValue(forKey: key) else { 599 | return nil 600 | } 601 | return CGFloat(decoder.decodeDouble(forKey: key)) 602 | } 603 | } 604 | 605 | extension URL: JSONValue, Castable { 606 | public static func value(from object: Any) -> URL? { 607 | if let str = String.value(from: object) { 608 | return URL(string: str) 609 | } 610 | return nil 611 | } 612 | 613 | public var jsonValue: Any? { 614 | return self.absoluteString 615 | } 616 | 617 | public func encode(with coder: NSCoder, forKey key: String) { 618 | coder.encode(self.absoluteString, forKey: key) 619 | } 620 | 621 | public static func decode(with decoder: NSCoder, fromKey key: String) -> URL? { 622 | guard let i = decoder.decodeObject(forKey: key) as? String else { 623 | return nil 624 | } 625 | return URL(string: i) 626 | } 627 | } 628 | 629 | public extension NSCoding { // Make any class conforming to NSCoding work like Castable 630 | public func encode(with coder: NSCoder, forKey key: String) { 631 | coder.encode(self, forKey: key) 632 | } 633 | 634 | public static func decode(with decoder: NSCoder, fromKey key: String) -> Self? { 635 | return decoder.decodeObject(forKey: key) as? Self 636 | } 637 | } 638 | 639 | public protocol DictionaryConvertible: JSONValue { 640 | init?(dictionary: JSONDictionary) 641 | func dictionaryRepresentation() -> [String: Any] 642 | } 643 | 644 | extension DictionaryConvertible { 645 | public static func value(from object: Any) -> Self? { 646 | if let convertedObject = object as? JSONDictionary, let value = self.init(dictionary: convertedObject) { 647 | return value 648 | } 649 | return nil 650 | } 651 | 652 | public var jsonValue: Any? { 653 | return self.dictionaryRepresentation() 654 | } 655 | 656 | public init?(json: Data) { 657 | guard let dict = (try? JSONSerialization.jsonObject(with: json, options: JSONSerialization.ReadingOptions(rawValue: 0))) as? [String: Any] else { 658 | return nil 659 | } 660 | self.init(dictionary: dict) 661 | } 662 | 663 | public init?(json: String) { 664 | guard let dict = (try? JSONSerialization.jsonObject(with: json.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions(rawValue: 0))) as? [String: Any] else { 665 | return nil 666 | } 667 | self.init(dictionary: dict) 668 | } 669 | 670 | public func awake(with dictionary: JSONDictionary) -> Bool { 671 | return true 672 | } 673 | 674 | public func read(from dictionary: JSONDictionary) { 675 | fatalError("read(from:) not implemented, run JSON cast with -read option") 676 | } 677 | } 678 | 679 | extension SCNMatrix4: JSONValue, Castable { 680 | 681 | public static func decode(with decoder: NSCoder, fromKey key: String) -> SCNMatrix4? { 682 | guard decoder.containsValue(forKey: key) else { 683 | return nil 684 | } 685 | let arr: [Float] = decoder.decodeObject(forKey: key) as! [Float] 686 | return SCNMatrix4(m11: arr[0], 687 | m12: arr[1], 688 | m13: arr[2], 689 | m14: arr[3], 690 | m21: arr[4], 691 | m22: arr[5], 692 | m23: arr[6], 693 | m24: arr[7], 694 | m31: arr[8], 695 | m32: arr[9], 696 | m33: arr[10], 697 | m34: arr[11], 698 | m41: arr[12], 699 | m42: arr[13], 700 | m43: arr[14], 701 | m44: arr[15]) 702 | } 703 | 704 | public var jsonValue: Any? { 705 | let array: [Float] = [self.m11, self.m12, self.m13, self.m14, self.m21, self.m22, self.m23, self.m24, self.m31, self.m32, self.m33, self.m34, self.m41, self.m42, self.m43, self.m44] 706 | return array 707 | } 708 | 709 | public static func value(from object: Any) -> SCNMatrix4? { 710 | switch object { 711 | case let x as [String]: 712 | return SCNMatrix4(m11: Float(x[0])!, 713 | m12: Float(x[1])!, 714 | m13: Float(x[2])!, 715 | m14: Float(x[3])!, 716 | m21: Float(x[4])!, 717 | m22: Float(x[5])!, 718 | m23: Float(x[6])!, 719 | m24: Float(x[7])!, 720 | m31: Float(x[8])!, 721 | m32: Float(x[9])!, 722 | m33: Float(x[10])!, 723 | m34: Float(x[11])!, 724 | m41: Float(x[12])!, 725 | m42: Float(x[13])!, 726 | m43: Float(x[14])!, 727 | m44: Float(x[15])!) 728 | 729 | case let x as [Float]: 730 | return SCNMatrix4(m11: x[0], 731 | m12: x[1], 732 | m13: x[2], 733 | m14: x[3], 734 | m21: x[4], 735 | m22: x[5], 736 | m23: x[6], 737 | m24: x[7], 738 | m31: x[8], 739 | m32: x[9], 740 | m33: x[10], 741 | m34: x[11], 742 | m41: x[12], 743 | m42: x[13], 744 | m43: x[14], 745 | m44: x[15]) 746 | 747 | default: 748 | return nil 749 | } 750 | } 751 | 752 | public func encode(with coder: NSCoder, forKey key: String) { 753 | let array: [Float] = [self.m11, self.m12, self.m13, self.m14, self.m21, self.m22, self.m23, self.m24, self.m31, self.m32, self.m33, self.m34, self.m41, self.m42, self.m43, self.m44] 754 | coder.encode(array, forKey: key) 755 | } 756 | } 757 | -------------------------------------------------------------------------------- /cast-script/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 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /cast-script/main.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -i xcrun -sdk macosx swift -swift-version 3 2 | // Copyright © 2016 Houzz. All rights reserved. 3 | 4 | import Foundation 5 | 6 | var classInheritence: [String]? 7 | var className: String? 8 | var output = [String]() 9 | var nscoding = false 10 | var upperCase = false 11 | var enumMapping = [String: String]() 12 | var isStruct = false 13 | var classAccess = "" 14 | var didImportCast = false 15 | var nullEmptyString = false 16 | var doImport = true 17 | var generateRead = false 18 | var isObjc = false 19 | var houzzLogging = false 20 | var disableHouzzzLogging = false 21 | var generateDefaultInit = false 22 | var classWantsDefaultInit = false 23 | var awakeFromRead = false 24 | var superTag: String? = nil 25 | 26 | class Regex { 27 | private let expression: NSRegularExpression 28 | private var match: NSTextCheckingResult? 29 | 30 | init(_ pattern: String, options: NSRegularExpression.Options = []) { 31 | self.expression = try! NSRegularExpression(pattern: pattern, options: options) 32 | } 33 | 34 | func matchGroups(_ input: String) -> [String?]? { 35 | match = expression.firstMatch(in: input, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, input.count)) 36 | if let match = match { 37 | var captures = [String?]() 38 | for group in 0 ..< match.numberOfRanges { 39 | let r = match.rangeAt(group) 40 | if r.location != NSNotFound { 41 | let stringMatch = (input as NSString).substring(with: match.rangeAt(group)) 42 | captures.append(stringMatch) 43 | } else { 44 | captures.append(nil) 45 | } 46 | } 47 | return captures 48 | } else { 49 | return nil 50 | } 51 | } 52 | 53 | func match(_ input: String) -> Bool { 54 | match = expression.firstMatch(in: input, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, input.count)) 55 | return match != nil 56 | } 57 | 58 | func replace(_ input: String, with template: String) -> String { 59 | return expression.stringByReplacingMatches(in: input, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, input.count), withTemplate: template) 60 | } 61 | 62 | func numberOfMatchesIn(_ input: String) -> Int { 63 | return expression.matches(in: input, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, input.count)).count 64 | } 65 | } 66 | 67 | extension String { 68 | func replace(_ regex: Regex, with template: String) -> String { 69 | return regex.replace(self, with: template) 70 | } 71 | 72 | subscript(index: Int) -> String { 73 | return String(self[self.index(self.startIndex, offsetBy: index)]) 74 | } 75 | 76 | subscript(integerRange: Range) -> String { 77 | let start = self.index(self.startIndex, offsetBy: integerRange.lowerBound) 78 | let end = self.index(self.startIndex, offsetBy: integerRange.upperBound) 79 | let range = start ..< end 80 | return String(self[range]) 81 | } 82 | } 83 | 84 | struct VarInfo { 85 | let name: String 86 | let type: String 87 | let defaultValue: String? 88 | let key: [String] 89 | var optional: Bool 90 | let isNullable: Bool 91 | let isLet: Bool 92 | let useCustomParse: Bool 93 | let skip: Bool 94 | 95 | init(name: String, isLet: Bool, type: String, defaultValue: String? = nil, asIsKey: Bool, key in_key: String? = nil, useCustom: Bool = false, skip: Bool = false) { 96 | self.name = name 97 | self.isLet = isLet 98 | self.skip = skip 99 | useCustomParse = useCustom 100 | if type.hasSuffix("?") || type.hasSuffix("!") { 101 | self.isNullable = true 102 | self.type = type.trimmingCharacters(in: CharacterSet(charactersIn: "!?")) 103 | self.optional = type.hasSuffix("?") 104 | } else { 105 | self.type = type 106 | self.optional = false 107 | self.isNullable = false 108 | } 109 | 110 | self.key = (in_key ?? name).components(separatedBy: "??").map { 111 | return $0.components(separatedBy: "/").map({ 112 | let correctCaseKey: String = $0.trimmingCharacters(in: CharacterSet.whitespaces) 113 | if upperCase && !asIsKey { 114 | return "\(correctCaseKey[0].uppercased())\(correctCaseKey[1 ..< correctCaseKey.count])" 115 | } 116 | return correctCaseKey 117 | }).joined(separator:"/") 118 | } 119 | self.defaultValue = defaultValue 120 | } 121 | 122 | var isEnum: Bool { 123 | get { 124 | return enumMapping[type] != nil 125 | } 126 | } 127 | 128 | var rawType: String? { 129 | get { 130 | return enumMapping[type] 131 | } 132 | } 133 | 134 | var encodeCall: String { 135 | get { 136 | var ret = "" 137 | var vv = name 138 | if optional { 139 | vv = "\(name)?" 140 | } 141 | 142 | ret += "\(vv).encode(with: aCoder, forKey: \"\(name)\")" 143 | 144 | return ret 145 | } 146 | } 147 | 148 | var decodeCall: String { 149 | get { 150 | 151 | var v: String 152 | v = "if let v = \(type).decode(with: aDecoder, fromKey:\"\(name)\") {" 153 | v += "\n\t\t\t\(name) = v" 154 | v += "\n\t\t}" 155 | 156 | if let def = defaultValue { 157 | v += " else { \(name) = \(def) }" 158 | } else if optional { 159 | v += " else { \(name) = nil }" 160 | } else { 161 | v += " else { return nil }" 162 | } 163 | 164 | return v 165 | } 166 | } 167 | 168 | func generateRead(nilMissing doNil: Bool) { 169 | guard skip == false else { 170 | return 171 | } 172 | var assignments: [String] 173 | if useCustomParse { 174 | let caseName = "\(name[0].uppercased())\(name[1 ..< name.count])" 175 | assignments = [ "\(className!).parse\(caseName)(from: dict)"] 176 | } else { 177 | assignments = key.map { "dict.value(for: \"\($0)\")" } 178 | } 179 | if doNil { 180 | if let defaultValue = defaultValue { 181 | assignments.append("\(defaultValue)") 182 | } 183 | } 184 | let assignExpr = assignments.joined(separator: " ?? ") 185 | if (optional || isNullable || defaultValue != nil) && doNil { 186 | output.append("\t\t\(name) = \(assignExpr)") 187 | } else { 188 | output.append("\t\tif let v:\(type) = \(assignExpr) {") 189 | output.append("\t\t\t\(name) = v") 190 | output.append("\t\t}") 191 | if doNil { 192 | output.append(" else {") 193 | if houzzLogging && !disableHouzzzLogging { 194 | output.append("LogError(\"Error: \(className!).\(name) failed init\")") 195 | } 196 | output.append(" return nil") 197 | output.append(" }") 198 | } 199 | } 200 | } 201 | 202 | func getInitParam() -> String? { 203 | if skip { 204 | return nil 205 | } 206 | let optPart = optional || self.isNullable ? "?" : "" 207 | let defaultValue = self.defaultValue ?? (self.optional ? "nil" : nil) 208 | return "\(name): \(type)\(optPart)" + (defaultValue.map { " = " + $0 } ?? "") 209 | } 210 | 211 | func getInitAssign() -> String { 212 | return "\t\tself.\(name) = \(name)" 213 | } 214 | } 215 | var variables = [VarInfo]() 216 | 217 | func createFunctions() { 218 | var override = "" 219 | if classInheritence == nil { 220 | classInheritence = [String]() 221 | } 222 | if !classInheritence!.contains("DictionaryConvertible") && !isStruct { 223 | override = "override" 224 | } 225 | 226 | // init 227 | let reqStr = isStruct ? "" : "required" 228 | let initAccess = classAccess == "open" ? "public" : classAccess 229 | 230 | output.append("\(reqStr) \(initAccess) init?(dictionary dict: JSONDictionary) {") 231 | 232 | for variable in variables { 233 | if variable.skip { 234 | continue 235 | } 236 | variable.generateRead(nilMissing: true) 237 | } 238 | 239 | 240 | if !override.isEmpty { 241 | if let superTag = superTag { 242 | output.append("guard let superDict = dict.any(forKeyPath: \"\(superTag)\") as? JSONDictionary else {") 243 | output.append("return nil") 244 | output.append("}") 245 | output.append("super.init(dictionary: superDict)") 246 | } else { 247 | output.append("\t\tsuper.init(dictionary: dict)") 248 | } 249 | } else if classInheritence!.contains("DictionaryConvertible") && classInheritence![0] != "DictionaryConvertible" && !isStruct && override.isEmpty { 250 | output.append("\t\tsuper.init()") 251 | } 252 | if override == "" { 253 | output.append("\t\t\tif !awake(with: dict) { return nil }") 254 | } 255 | output.append("\t}") 256 | 257 | // init(values...) 258 | if generateDefaultInit || classWantsDefaultInit { 259 | let params = variables.flatMap { return $0.getInitParam() }.joined(separator: ", ") 260 | output.append("\t\(initAccess) init(\(params)) {") 261 | for variable in variables { 262 | if variable.skip == false { 263 | output.append(variable.getInitAssign()) 264 | } 265 | } 266 | output.append("\t}") 267 | } 268 | 269 | // read(from:) 270 | if generateRead { 271 | output.append("\(override) \(classAccess) func read(from dict: JSONDictionary) {") 272 | 273 | for variable in variables { 274 | if !variable.isLet { 275 | variable.generateRead(nilMissing: false) 276 | } 277 | } 278 | 279 | if !override.isEmpty { 280 | if let superTag = superTag { 281 | output.append("guard let superDict = dict.any(forKeyPath: \"\(superTag)\") as? JSONDictionary else {") 282 | output.append("return") 283 | output.append("}") 284 | output.append("super.read(from: superDict)") 285 | } else { 286 | output.append("\t\tsuper.read(from: dict)") 287 | } 288 | } 289 | 290 | if awakeFromRead { 291 | output.append("\t\t\t let _ = awake(with: dict)") 292 | } 293 | output.append("\t\t}") 294 | } 295 | 296 | 297 | // dictionaryRepresentation() 298 | 299 | output.append("\t\(isObjc ? "@objc" : "") \(override) \(classAccess) func dictionaryRepresentation() -> [String: Any] {") 300 | if override.isEmpty { 301 | output.append("\t\tvar dict = [String: Any]()") 302 | } else { 303 | if let superTag = superTag { 304 | output.append("\t\tvar dict:[String:Any] = [\"\(superTag)\": super.dictionaryRepresentation()]") 305 | } else { 306 | output.append("\t\tvar dict = super.dictionaryRepresentation()") 307 | } 308 | } 309 | 310 | 311 | for variable in variables { 312 | if variable.skip { 313 | continue; 314 | } 315 | let optStr = variable.optional ? "?" : "" 316 | let keys = variable.key.first!.components(separatedBy: "/") 317 | for (idx, key) in keys.enumerated() { 318 | let dName = (idx == 0) ? "dict" : "dict\(idx)" 319 | if idx == keys.count - 1 { 320 | output.append("\t\tif let x = \(variable.name)\(optStr).jsonValue {") 321 | output.append("\t\t\t\(dName)[\"\(key)\"] = x") 322 | output.append("\t\t}") 323 | 324 | for idx2 in(0 ..< idx).reversed() { 325 | let idx3 = idx2 + 1 326 | let dName = (idx2 == 0) ? "dict" : "dict\(idx2)" 327 | let prevName = "dict\(idx3)" 328 | output.append("\t\t\(dName)[\"\(keys[idx2])\"] = \(prevName)") 329 | output.append("\t\t}") 330 | } 331 | } else { 332 | let nidx = idx + 1 333 | let nextName = "dict\(nidx)" 334 | output.append("\t\tdo {") 335 | output.append("\t\t\t var \(nextName) = \(dName)[\"\(key)\"] as? [String: Any] ?? [String: Any]()") 336 | } 337 | } 338 | } 339 | output.append("\t\treturn dict") 340 | output.append("\t}") 341 | 342 | // nscoding 343 | if (nscoding || classInheritence!.contains("NSCoding")) && !isStruct { 344 | let codingOverride = !classInheritence!.contains("NSCoding") 345 | let codingOverrideString = codingOverride ? "override" : "" 346 | 347 | // init(coder:) 348 | output.append("\trequired \(initAccess) init?(coder aDecoder: NSCoder) {") 349 | 350 | for variable in variables { 351 | output.append(variable.decodeCall) 352 | } 353 | 354 | if codingOverride { 355 | output.append("\t\tsuper.init(coder:aDecoder)") 356 | } 357 | output.append("\t}") 358 | 359 | // encodeWithCoder 360 | output.append(" \(classAccess) \(codingOverrideString) func encode(with aCoder: NSCoder) {") 361 | if codingOverride { 362 | output.append("\t\tsuper.encode(with: aCoder)") 363 | } 364 | 365 | for variable in variables { 366 | output.append(variable.encodeCall) 367 | } 368 | 369 | output.append("\t}") 370 | 371 | // NSCopying 372 | 373 | if classInheritence!.contains("NSCopying") && !isStruct { 374 | output.append("\t\(classAccess) func copy(with zone: NSZone? = nil) -> Any {") 375 | output.append("\t\treturn NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self))!") 376 | output.append("\t}") 377 | } 378 | } 379 | 380 | if isObjc && override.isEmpty { 381 | // class was declard @objc and is a base class not inhereting from other DictionaryConvertible classes 382 | output.append("\t@objc convenience init?(dictionary: [String: Any]) {") 383 | output.append("\t\tself.init(dictionary: dictionary as JSONDictionary)") 384 | output.append("\t}") 385 | if generateRead { 386 | output.append("@objc func read(from dict: [String: Any]) {") 387 | output.append(" read(from: dict as JSONDictionary)") 388 | output.append("}") 389 | } 390 | } 391 | } 392 | 393 | var inputFile: String? = nil 394 | var outputFile: String? = nil 395 | 396 | for (idx, arg) in CommandLine.arguments.enumerated() { 397 | if idx == 0 { 398 | continue 399 | } 400 | switch arg { 401 | case "-c", "-upper", "-uppercase": 402 | upperCase = true 403 | 404 | case "-n", "-null", "-nullempty": 405 | nullEmptyString = true 406 | 407 | case "-m", "-noimport": 408 | doImport = false 409 | 410 | case "-r", "-read": 411 | generateRead = true 412 | 413 | case "-houzz": 414 | houzzLogging = true 415 | 416 | case "-init": 417 | generateDefaultInit = true 418 | 419 | default: 420 | if inputFile == nil { 421 | inputFile = arg 422 | } else { 423 | outputFile = arg 424 | } 425 | } 426 | } 427 | 428 | let input = try! String(contentsOfFile: inputFile!).components(separatedBy: "\n") 429 | 430 | var inClass = false 431 | let classRegex = Regex("(class|struct) +([^ :]+)[ :]+(.*)\\{ *$", options: [.anchorsMatchLines]) 432 | let endBrace = Regex("\\}") 433 | let openBrace = Regex("\\{") 434 | let varRegex = Regex("(var|let) +([^: ]+?) *: *([^ ]+) *(?:= *([^ ]+))? *(?://! *(v?)\"([^\"]+)\")?(?://! *(custom))?") 435 | let skipVarRegex = Regex("(var|let) +([^: ]+?) *: *([^ ]+) *(?:= *([^ ]+))? *//! *ignore json") 436 | let dictRegex = Regex("(var|let) +([^: ]+?) *: *(\\[.*?:.*?\\][!?]) *(?:= *([^ ]+))? *(?://! *(v?)\"([^ ]+)\")?(?://! *(custom))?") 437 | let ignoreRegex = Regex("(.*)//! *ignore", options: [.caseInsensitive]) 438 | let codingRegex = Regex("//! *nscoding", options: [.caseInsensitive]) 439 | let enumRegex = Regex("enum ([^ :]+)[ :]+([^ ]+)") 440 | let accessRegex = Regex("(public|private|internal|open)") 441 | var braceLevel = 0 442 | var importRegex = Regex("import +([^ ]+)") 443 | var inImportBlock = false 444 | var commentRegex = Regex("^ *//[^!].*$") 445 | let disableLogging = Regex("//! *nolog") 446 | let classInit = Regex("//! +init\\b") 447 | let awakeFromReadRegex = Regex("//! +awakeFromRead\\b") 448 | let superTagRegex = Regex("//! +super +\"([^\"]+)\"") 449 | 450 | output.append("// ================================================================== ") 451 | output.append("//") 452 | let last = inputFile!.components(separatedBy: "/").last! 453 | output.append("// Generated from \(last)") 454 | output.append("//") 455 | output.append("// DO NOT EDIT THIS FILE. GENERATED FILE, EDITS WILL BE OVERWRITTEN") 456 | output.append("//") 457 | output.append("// ================================================================== ") 458 | 459 | for line in input { 460 | if commentRegex.match(line) { 461 | continue 462 | } 463 | 464 | var outline = line 465 | 466 | let priorBraceLevel = braceLevel 467 | braceLevel += openBrace.numberOfMatchesIn(outline) - endBrace.numberOfMatchesIn(outline) 468 | 469 | if priorBraceLevel == 0 && !didImportCast { 470 | if let matches: [String?] = importRegex.matchGroups(line) { 471 | if !inImportBlock { 472 | inImportBlock = true 473 | } 474 | if let framework = matches[1] , framework.hasPrefix("Cast") { 475 | didImportCast = true 476 | } 477 | } else if inImportBlock { 478 | inImportBlock = false 479 | if !didImportCast { 480 | didImportCast = true 481 | if doImport { 482 | output.append("#if os(iOS)") 483 | output.append("import Cast") 484 | output.append("#elseif os(tvOS)") 485 | output.append("import CastTV") 486 | output.append("#else") 487 | output.append("import CastX") 488 | output.append("#endif") 489 | } 490 | } 491 | } 492 | } 493 | 494 | if priorBraceLevel <= 1, let matches = enumRegex.matchGroups(line) { 495 | guard let name = matches[1] else { 496 | fatalError() 497 | } 498 | guard let rawType = matches[2] else { 499 | fatalError() 500 | } 501 | enumMapping[name] = rawType 502 | } else { 503 | if inClass { 504 | if braceLevel == 0 { 505 | inClass = false 506 | createFunctions() 507 | } else if priorBraceLevel == 1 { 508 | if ignoreRegex.match(line) && !skipVarRegex.match(line) { 509 | outline = line.replace(ignoreRegex, with: "$1") 510 | } else if codingRegex.match(line) { 511 | nscoding = true && !isStruct 512 | continue 513 | } else if classInit.match(line){ 514 | classWantsDefaultInit = true 515 | continue 516 | } else if awakeFromReadRegex.match(line){ 517 | awakeFromRead = true 518 | continue 519 | } else if disableLogging.match(line) { 520 | disableHouzzzLogging = true 521 | continue 522 | } else if let matches: [String?] = skipVarRegex.matchGroups(line) { 523 | variables.append(VarInfo(name: matches[2]!, isLet: matches[1]! == "let", type: matches[3]!, defaultValue: matches[4], asIsKey: true, key: nil, useCustom: false, skip: true)) 524 | outline = line 525 | } else if let matches: [String?] = dictRegex.matchGroups(line) { 526 | variables.append(VarInfo(name: matches[2]!, isLet: matches[1]! == "let", type: matches[3]!, defaultValue: matches[4], asIsKey: !(matches[5]?.isEmpty ?? true), key: matches[6], useCustom: matches[7] != nil)) 527 | outline = line.replace(dictRegex, with: " $1 $2: $3") 528 | } else if let matches: [String?] = varRegex.matchGroups(line) { 529 | variables.append(VarInfo(name: matches[2]!, isLet: matches[1]! == "let", type: matches[3]!, defaultValue: matches[4], asIsKey: !(matches[5]?.isEmpty ?? true), key: matches[6], useCustom: matches[7] != nil)) 530 | outline = line.replace(varRegex, with: " $1 $2: $3") 531 | } else if let matches: [String?] = superTagRegex.matchGroups(line) { 532 | if let str = matches[1] { 533 | superTag = str 534 | } 535 | } 536 | } 537 | } else if priorBraceLevel == 0 { 538 | if let matches = classRegex.matchGroups(line) { 539 | inClass = true 540 | classInheritence = matches[3]?.replacingOccurrences(of: " ", with: "").components(separatedBy: ",") 541 | className = matches[2] 542 | variables = [VarInfo]() 543 | isStruct = (matches[1] == "struct") 544 | isObjc = line.contains("@objc") 545 | if let matches: [String?] = accessRegex.matchGroups(line) { 546 | classAccess = matches[1] ?? "" 547 | } else { 548 | classAccess = "" 549 | } 550 | nscoding = false 551 | disableHouzzzLogging = false 552 | classWantsDefaultInit = false 553 | superTag = nil 554 | } 555 | } 556 | } 557 | 558 | output.append(outline) 559 | } 560 | 561 | try! output.joined(separator: "\n").write(toFile: outputFile!, atomically: true, encoding: String.Encoding.utf8) 562 | -------------------------------------------------------------------------------- /cast.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E405FEEA1DD8B9E800496CCE /* Cast.h in Headers */ = {isa = PBXBuildFile; fileRef = E405FEE71DD8B8AB00496CCE /* Cast.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | E405FEEB1DD8B9EE00496CCE /* Cast.h in Headers */ = {isa = PBXBuildFile; fileRef = E405FEE71DD8B8AB00496CCE /* Cast.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | E405FEEC1DD8BA5A00496CCE /* Cast.swift in Sources */ = {isa = PBXBuildFile; fileRef = E405FEE81DD8B8AB00496CCE /* Cast.swift */; }; 13 | E405FEED1DD8BA5B00496CCE /* Cast.swift in Sources */ = {isa = PBXBuildFile; fileRef = E405FEE81DD8B8AB00496CCE /* Cast.swift */; }; 14 | E405FEEE1DD8BA5C00496CCE /* Cast.swift in Sources */ = {isa = PBXBuildFile; fileRef = E405FEE81DD8B8AB00496CCE /* Cast.swift */; }; 15 | E46F92441CF04F6B00BFE4AF /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = E46F92431CF04F6B00BFE4AF /* main.swift */; }; 16 | E46F92991CF0802F00BFE4AF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E46F92981CF0802F00BFE4AF /* AppDelegate.swift */; }; 17 | E46F929B1CF0802F00BFE4AF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E46F929A1CF0802F00BFE4AF /* ViewController.swift */; }; 18 | E46F929E1CF0802F00BFE4AF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E46F929C1CF0802F00BFE4AF /* Main.storyboard */; }; 19 | E46F92A01CF0802F00BFE4AF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E46F929F1CF0802F00BFE4AF /* Assets.xcassets */; }; 20 | E46F92A31CF0802F00BFE4AF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E46F92A11CF0802F00BFE4AF /* LaunchScreen.storyboard */; }; 21 | E46F92AE1CF0802F00BFE4AF /* CastingAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E46F92AD1CF0802F00BFE4AF /* CastingAppTests.swift */; }; 22 | E46F92BA1CF081C200BFE4AF /* Cast.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E46F92621CF07BBB00BFE4AF /* Cast.framework */; }; 23 | E46F92BB1CF081C200BFE4AF /* Cast.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E46F92621CF07BBB00BFE4AF /* Cast.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 24 | E4A03A4F1CF08B65007FD77B /* Classy.cast in Sources */ = {isa = PBXBuildFile; fileRef = E46F92B61CF0805900BFE4AF /* Classy.cast */; }; 25 | E4A509AB1CF0A468007DA42E /* Cast.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E46F92621CF07BBB00BFE4AF /* Cast.framework */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXBuildRule section */ 29 | E46F92B81CF080C700BFE4AF /* PBXBuildRule */ = { 30 | isa = PBXBuildRule; 31 | compilerSpec = com.apple.compilers.proxy.script; 32 | filePatterns = "*.cast"; 33 | fileType = pattern.proxy; 34 | isEditable = 1; 35 | outputFiles = ( 36 | "${DERIVED_SOURCES_DIR}/${INPUT_FILE_BASE}.swift", 37 | ); 38 | script = "${SRCROOT}/cast -c \"$INPUT_FILE_PATH\" \"$DERIVED_SOURCES_DIR/$INPUT_FILE_BASE.swift\"\n"; 39 | }; 40 | /* End PBXBuildRule section */ 41 | 42 | /* Begin PBXContainerItemProxy section */ 43 | E46F92AA1CF0802F00BFE4AF /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = E46F92381CF04F6B00BFE4AF /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = E46F92951CF0802F00BFE4AF; 48 | remoteInfo = CastingApp; 49 | }; 50 | E46F92BC1CF081C200BFE4AF /* PBXContainerItemProxy */ = { 51 | isa = PBXContainerItemProxy; 52 | containerPortal = E46F92381CF04F6B00BFE4AF /* Project object */; 53 | proxyType = 1; 54 | remoteGlobalIDString = E46F92611CF07BBB00BFE4AF; 55 | remoteInfo = "Cast iOS"; 56 | }; 57 | /* End PBXContainerItemProxy section */ 58 | 59 | /* Begin PBXCopyFilesBuildPhase section */ 60 | E46F923E1CF04F6B00BFE4AF /* CopyFiles */ = { 61 | isa = PBXCopyFilesBuildPhase; 62 | buildActionMask = 2147483647; 63 | dstPath = /usr/share/man/man1/; 64 | dstSubfolderSpec = 0; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 1; 68 | }; 69 | E46F92BE1CF081C200BFE4AF /* Embed Frameworks */ = { 70 | isa = PBXCopyFilesBuildPhase; 71 | buildActionMask = 2147483647; 72 | dstPath = ""; 73 | dstSubfolderSpec = 10; 74 | files = ( 75 | E46F92BB1CF081C200BFE4AF /* Cast.framework in Embed Frameworks */, 76 | ); 77 | name = "Embed Frameworks"; 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXCopyFilesBuildPhase section */ 81 | 82 | /* Begin PBXFileReference section */ 83 | E405FEE71DD8B8AB00496CCE /* Cast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Cast.h; sourceTree = ""; }; 84 | E405FEE81DD8B8AB00496CCE /* Cast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cast.swift; sourceTree = ""; }; 85 | E46F92401CF04F6B00BFE4AF /* cast */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cast; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | E46F92431CF04F6B00BFE4AF /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = main.swift; path = "../cast-script/main.swift"; sourceTree = ""; }; 87 | E46F92621CF07BBB00BFE4AF /* Cast.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Cast.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 88 | E46F92661CF07BBB00BFE4AF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 89 | E46F92731CF07BCA00BFE4AF /* CastX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CastX.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 90 | E46F92961CF0802F00BFE4AF /* CastingApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CastingApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 91 | E46F92981CF0802F00BFE4AF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 92 | E46F929A1CF0802F00BFE4AF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 93 | E46F929D1CF0802F00BFE4AF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 94 | E46F929F1CF0802F00BFE4AF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 95 | E46F92A21CF0802F00BFE4AF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 96 | E46F92A41CF0802F00BFE4AF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 97 | E46F92A91CF0802F00BFE4AF /* CastingAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CastingAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 98 | E46F92AD1CF0802F00BFE4AF /* CastingAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CastingAppTests.swift; sourceTree = ""; }; 99 | E46F92AF1CF0802F00BFE4AF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 100 | E46F92B61CF0805900BFE4AF /* Classy.cast */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Classy.cast; sourceTree = ""; }; 101 | E4A509B61CF0A496007DA42E /* CastTV.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CastTV.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 102 | /* End PBXFileReference section */ 103 | 104 | /* Begin PBXFrameworksBuildPhase section */ 105 | E46F923D1CF04F6B00BFE4AF /* Frameworks */ = { 106 | isa = PBXFrameworksBuildPhase; 107 | buildActionMask = 2147483647; 108 | files = ( 109 | ); 110 | runOnlyForDeploymentPostprocessing = 0; 111 | }; 112 | E46F925E1CF07BBB00BFE4AF /* Frameworks */ = { 113 | isa = PBXFrameworksBuildPhase; 114 | buildActionMask = 2147483647; 115 | files = ( 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | E46F926C1CF07BCA00BFE4AF /* Frameworks */ = { 120 | isa = PBXFrameworksBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | ); 124 | runOnlyForDeploymentPostprocessing = 0; 125 | }; 126 | E46F92931CF0802F00BFE4AF /* Frameworks */ = { 127 | isa = PBXFrameworksBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | E46F92BA1CF081C200BFE4AF /* Cast.framework in Frameworks */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | E46F92A61CF0802F00BFE4AF /* Frameworks */ = { 135 | isa = PBXFrameworksBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | E4A509AB1CF0A468007DA42E /* Cast.framework in Frameworks */, 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | E4A509AF1CF0A496007DA42E /* Frameworks */ = { 143 | isa = PBXFrameworksBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | ); 147 | runOnlyForDeploymentPostprocessing = 0; 148 | }; 149 | /* End PBXFrameworksBuildPhase section */ 150 | 151 | /* Begin PBXGroup section */ 152 | E46F92371CF04F6B00BFE4AF = { 153 | isa = PBXGroup; 154 | children = ( 155 | E46F92421CF04F6B00BFE4AF /* cast */, 156 | E46F92631CF07BBB00BFE4AF /* Cast */, 157 | E46F92971CF0802F00BFE4AF /* CastingApp */, 158 | E46F92AC1CF0802F00BFE4AF /* CastingAppTests */, 159 | E46F92411CF04F6B00BFE4AF /* Products */, 160 | ); 161 | sourceTree = ""; 162 | }; 163 | E46F92411CF04F6B00BFE4AF /* Products */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | E46F92401CF04F6B00BFE4AF /* cast */, 167 | E46F92621CF07BBB00BFE4AF /* Cast.framework */, 168 | E46F92731CF07BCA00BFE4AF /* CastX.framework */, 169 | E46F92961CF0802F00BFE4AF /* CastingApp.app */, 170 | E46F92A91CF0802F00BFE4AF /* CastingAppTests.xctest */, 171 | E4A509B61CF0A496007DA42E /* CastTV.framework */, 172 | ); 173 | name = Products; 174 | sourceTree = ""; 175 | }; 176 | E46F92421CF04F6B00BFE4AF /* cast */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | E46F92431CF04F6B00BFE4AF /* main.swift */, 180 | ); 181 | path = cast; 182 | sourceTree = ""; 183 | }; 184 | E46F92631CF07BBB00BFE4AF /* Cast */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | E405FEE71DD8B8AB00496CCE /* Cast.h */, 188 | E405FEE81DD8B8AB00496CCE /* Cast.swift */, 189 | E46F92661CF07BBB00BFE4AF /* Info.plist */, 190 | ); 191 | name = Cast; 192 | path = "cast-script"; 193 | sourceTree = ""; 194 | }; 195 | E46F92971CF0802F00BFE4AF /* CastingApp */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | E46F92B61CF0805900BFE4AF /* Classy.cast */, 199 | E46F92981CF0802F00BFE4AF /* AppDelegate.swift */, 200 | E46F929A1CF0802F00BFE4AF /* ViewController.swift */, 201 | E46F929C1CF0802F00BFE4AF /* Main.storyboard */, 202 | E46F929F1CF0802F00BFE4AF /* Assets.xcassets */, 203 | E46F92A11CF0802F00BFE4AF /* LaunchScreen.storyboard */, 204 | E46F92A41CF0802F00BFE4AF /* Info.plist */, 205 | ); 206 | path = CastingApp; 207 | sourceTree = ""; 208 | }; 209 | E46F92AC1CF0802F00BFE4AF /* CastingAppTests */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | E46F92AD1CF0802F00BFE4AF /* CastingAppTests.swift */, 213 | E46F92AF1CF0802F00BFE4AF /* Info.plist */, 214 | ); 215 | path = CastingAppTests; 216 | sourceTree = ""; 217 | }; 218 | /* End PBXGroup section */ 219 | 220 | /* Begin PBXHeadersBuildPhase section */ 221 | E46F925F1CF07BBB00BFE4AF /* Headers */ = { 222 | isa = PBXHeadersBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | E405FEEA1DD8B9E800496CCE /* Cast.h in Headers */, 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | E4A509B01CF0A496007DA42E /* Headers */ = { 230 | isa = PBXHeadersBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | E405FEEB1DD8B9EE00496CCE /* Cast.h in Headers */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXHeadersBuildPhase section */ 238 | 239 | /* Begin PBXNativeTarget section */ 240 | E46F923F1CF04F6B00BFE4AF /* cast */ = { 241 | isa = PBXNativeTarget; 242 | buildConfigurationList = E46F92471CF04F6B00BFE4AF /* Build configuration list for PBXNativeTarget "cast" */; 243 | buildPhases = ( 244 | E46F923C1CF04F6B00BFE4AF /* Sources */, 245 | E46F923D1CF04F6B00BFE4AF /* Frameworks */, 246 | E46F923E1CF04F6B00BFE4AF /* CopyFiles */, 247 | ); 248 | buildRules = ( 249 | ); 250 | dependencies = ( 251 | ); 252 | name = cast; 253 | productName = cast; 254 | productReference = E46F92401CF04F6B00BFE4AF /* cast */; 255 | productType = "com.apple.product-type.tool"; 256 | }; 257 | E46F92611CF07BBB00BFE4AF /* Cast iOS */ = { 258 | isa = PBXNativeTarget; 259 | buildConfigurationList = E46F92671CF07BBB00BFE4AF /* Build configuration list for PBXNativeTarget "Cast iOS" */; 260 | buildPhases = ( 261 | E46F925D1CF07BBB00BFE4AF /* Sources */, 262 | E46F925E1CF07BBB00BFE4AF /* Frameworks */, 263 | E46F925F1CF07BBB00BFE4AF /* Headers */, 264 | E46F92601CF07BBB00BFE4AF /* Resources */, 265 | ); 266 | buildRules = ( 267 | ); 268 | dependencies = ( 269 | ); 270 | name = "Cast iOS"; 271 | productName = Cast; 272 | productReference = E46F92621CF07BBB00BFE4AF /* Cast.framework */; 273 | productType = "com.apple.product-type.framework"; 274 | }; 275 | E46F926A1CF07BCA00BFE4AF /* Cast OSX */ = { 276 | isa = PBXNativeTarget; 277 | buildConfigurationList = E46F92701CF07BCA00BFE4AF /* Build configuration list for PBXNativeTarget "Cast OSX" */; 278 | buildPhases = ( 279 | E46F926B1CF07BCA00BFE4AF /* Sources */, 280 | E46F926C1CF07BCA00BFE4AF /* Frameworks */, 281 | E46F926F1CF07BCA00BFE4AF /* Resources */, 282 | ); 283 | buildRules = ( 284 | ); 285 | dependencies = ( 286 | ); 287 | name = "Cast OSX"; 288 | productName = Cast; 289 | productReference = E46F92731CF07BCA00BFE4AF /* CastX.framework */; 290 | productType = "com.apple.product-type.framework"; 291 | }; 292 | E46F92951CF0802F00BFE4AF /* CastingApp */ = { 293 | isa = PBXNativeTarget; 294 | buildConfigurationList = E46F92B01CF0802F00BFE4AF /* Build configuration list for PBXNativeTarget "CastingApp" */; 295 | buildPhases = ( 296 | E46F92921CF0802F00BFE4AF /* Sources */, 297 | E46F92931CF0802F00BFE4AF /* Frameworks */, 298 | E46F92941CF0802F00BFE4AF /* Resources */, 299 | E46F92BE1CF081C200BFE4AF /* Embed Frameworks */, 300 | ); 301 | buildRules = ( 302 | E46F92B81CF080C700BFE4AF /* PBXBuildRule */, 303 | ); 304 | dependencies = ( 305 | E46F92BD1CF081C200BFE4AF /* PBXTargetDependency */, 306 | ); 307 | name = CastingApp; 308 | productName = CastingApp; 309 | productReference = E46F92961CF0802F00BFE4AF /* CastingApp.app */; 310 | productType = "com.apple.product-type.application"; 311 | }; 312 | E46F92A81CF0802F00BFE4AF /* CastingAppTests */ = { 313 | isa = PBXNativeTarget; 314 | buildConfigurationList = E46F92B31CF0802F00BFE4AF /* Build configuration list for PBXNativeTarget "CastingAppTests" */; 315 | buildPhases = ( 316 | E46F92A51CF0802F00BFE4AF /* Sources */, 317 | E46F92A61CF0802F00BFE4AF /* Frameworks */, 318 | E46F92A71CF0802F00BFE4AF /* Resources */, 319 | ); 320 | buildRules = ( 321 | ); 322 | dependencies = ( 323 | E46F92AB1CF0802F00BFE4AF /* PBXTargetDependency */, 324 | ); 325 | name = CastingAppTests; 326 | productName = CastingAppTests; 327 | productReference = E46F92A91CF0802F00BFE4AF /* CastingAppTests.xctest */; 328 | productType = "com.apple.product-type.bundle.unit-test"; 329 | }; 330 | E4A509AC1CF0A496007DA42E /* Cast tvOS */ = { 331 | isa = PBXNativeTarget; 332 | buildConfigurationList = E4A509B31CF0A496007DA42E /* Build configuration list for PBXNativeTarget "Cast tvOS" */; 333 | buildPhases = ( 334 | E4A509AD1CF0A496007DA42E /* Sources */, 335 | E4A509AF1CF0A496007DA42E /* Frameworks */, 336 | E4A509B01CF0A496007DA42E /* Headers */, 337 | E4A509B21CF0A496007DA42E /* Resources */, 338 | ); 339 | buildRules = ( 340 | ); 341 | dependencies = ( 342 | ); 343 | name = "Cast tvOS"; 344 | productName = Cast; 345 | productReference = E4A509B61CF0A496007DA42E /* CastTV.framework */; 346 | productType = "com.apple.product-type.framework"; 347 | }; 348 | /* End PBXNativeTarget section */ 349 | 350 | /* Begin PBXProject section */ 351 | E46F92381CF04F6B00BFE4AF /* Project object */ = { 352 | isa = PBXProject; 353 | attributes = { 354 | LastSwiftUpdateCheck = 0730; 355 | LastUpgradeCheck = 0810; 356 | ORGANIZATIONNAME = Houzz; 357 | TargetAttributes = { 358 | E46F923F1CF04F6B00BFE4AF = { 359 | CreatedOnToolsVersion = 7.3.1; 360 | LastSwiftMigration = 0800; 361 | }; 362 | E46F92611CF07BBB00BFE4AF = { 363 | CreatedOnToolsVersion = 7.3.1; 364 | LastSwiftMigration = 0800; 365 | }; 366 | E46F92951CF0802F00BFE4AF = { 367 | CreatedOnToolsVersion = 7.3.1; 368 | LastSwiftMigration = 0800; 369 | }; 370 | E46F92A81CF0802F00BFE4AF = { 371 | CreatedOnToolsVersion = 7.3.1; 372 | LastSwiftMigration = 0800; 373 | TestTargetID = E46F92951CF0802F00BFE4AF; 374 | }; 375 | }; 376 | }; 377 | buildConfigurationList = E46F923B1CF04F6B00BFE4AF /* Build configuration list for PBXProject "cast" */; 378 | compatibilityVersion = "Xcode 3.2"; 379 | developmentRegion = English; 380 | hasScannedForEncodings = 0; 381 | knownRegions = ( 382 | en, 383 | Base, 384 | ); 385 | mainGroup = E46F92371CF04F6B00BFE4AF; 386 | productRefGroup = E46F92411CF04F6B00BFE4AF /* Products */; 387 | projectDirPath = ""; 388 | projectRoot = ""; 389 | targets = ( 390 | E46F923F1CF04F6B00BFE4AF /* cast */, 391 | E46F92611CF07BBB00BFE4AF /* Cast iOS */, 392 | E46F926A1CF07BCA00BFE4AF /* Cast OSX */, 393 | E4A509AC1CF0A496007DA42E /* Cast tvOS */, 394 | E46F92951CF0802F00BFE4AF /* CastingApp */, 395 | E46F92A81CF0802F00BFE4AF /* CastingAppTests */, 396 | ); 397 | }; 398 | /* End PBXProject section */ 399 | 400 | /* Begin PBXResourcesBuildPhase section */ 401 | E46F92601CF07BBB00BFE4AF /* Resources */ = { 402 | isa = PBXResourcesBuildPhase; 403 | buildActionMask = 2147483647; 404 | files = ( 405 | ); 406 | runOnlyForDeploymentPostprocessing = 0; 407 | }; 408 | E46F926F1CF07BCA00BFE4AF /* Resources */ = { 409 | isa = PBXResourcesBuildPhase; 410 | buildActionMask = 2147483647; 411 | files = ( 412 | ); 413 | runOnlyForDeploymentPostprocessing = 0; 414 | }; 415 | E46F92941CF0802F00BFE4AF /* Resources */ = { 416 | isa = PBXResourcesBuildPhase; 417 | buildActionMask = 2147483647; 418 | files = ( 419 | E46F92A31CF0802F00BFE4AF /* LaunchScreen.storyboard in Resources */, 420 | E46F92A01CF0802F00BFE4AF /* Assets.xcassets in Resources */, 421 | E46F929E1CF0802F00BFE4AF /* Main.storyboard in Resources */, 422 | ); 423 | runOnlyForDeploymentPostprocessing = 0; 424 | }; 425 | E46F92A71CF0802F00BFE4AF /* Resources */ = { 426 | isa = PBXResourcesBuildPhase; 427 | buildActionMask = 2147483647; 428 | files = ( 429 | ); 430 | runOnlyForDeploymentPostprocessing = 0; 431 | }; 432 | E4A509B21CF0A496007DA42E /* Resources */ = { 433 | isa = PBXResourcesBuildPhase; 434 | buildActionMask = 2147483647; 435 | files = ( 436 | ); 437 | runOnlyForDeploymentPostprocessing = 0; 438 | }; 439 | /* End PBXResourcesBuildPhase section */ 440 | 441 | /* Begin PBXSourcesBuildPhase section */ 442 | E46F923C1CF04F6B00BFE4AF /* Sources */ = { 443 | isa = PBXSourcesBuildPhase; 444 | buildActionMask = 2147483647; 445 | files = ( 446 | E46F92441CF04F6B00BFE4AF /* main.swift in Sources */, 447 | ); 448 | runOnlyForDeploymentPostprocessing = 0; 449 | }; 450 | E46F925D1CF07BBB00BFE4AF /* Sources */ = { 451 | isa = PBXSourcesBuildPhase; 452 | buildActionMask = 2147483647; 453 | files = ( 454 | E405FEEC1DD8BA5A00496CCE /* Cast.swift in Sources */, 455 | ); 456 | runOnlyForDeploymentPostprocessing = 0; 457 | }; 458 | E46F926B1CF07BCA00BFE4AF /* Sources */ = { 459 | isa = PBXSourcesBuildPhase; 460 | buildActionMask = 2147483647; 461 | files = ( 462 | E405FEED1DD8BA5B00496CCE /* Cast.swift in Sources */, 463 | ); 464 | runOnlyForDeploymentPostprocessing = 0; 465 | }; 466 | E46F92921CF0802F00BFE4AF /* Sources */ = { 467 | isa = PBXSourcesBuildPhase; 468 | buildActionMask = 2147483647; 469 | files = ( 470 | E46F929B1CF0802F00BFE4AF /* ViewController.swift in Sources */, 471 | E46F92991CF0802F00BFE4AF /* AppDelegate.swift in Sources */, 472 | E4A03A4F1CF08B65007FD77B /* Classy.cast in Sources */, 473 | ); 474 | runOnlyForDeploymentPostprocessing = 0; 475 | }; 476 | E46F92A51CF0802F00BFE4AF /* Sources */ = { 477 | isa = PBXSourcesBuildPhase; 478 | buildActionMask = 2147483647; 479 | files = ( 480 | E46F92AE1CF0802F00BFE4AF /* CastingAppTests.swift in Sources */, 481 | ); 482 | runOnlyForDeploymentPostprocessing = 0; 483 | }; 484 | E4A509AD1CF0A496007DA42E /* Sources */ = { 485 | isa = PBXSourcesBuildPhase; 486 | buildActionMask = 2147483647; 487 | files = ( 488 | E405FEEE1DD8BA5C00496CCE /* Cast.swift in Sources */, 489 | ); 490 | runOnlyForDeploymentPostprocessing = 0; 491 | }; 492 | /* End PBXSourcesBuildPhase section */ 493 | 494 | /* Begin PBXTargetDependency section */ 495 | E46F92AB1CF0802F00BFE4AF /* PBXTargetDependency */ = { 496 | isa = PBXTargetDependency; 497 | target = E46F92951CF0802F00BFE4AF /* CastingApp */; 498 | targetProxy = E46F92AA1CF0802F00BFE4AF /* PBXContainerItemProxy */; 499 | }; 500 | E46F92BD1CF081C200BFE4AF /* PBXTargetDependency */ = { 501 | isa = PBXTargetDependency; 502 | target = E46F92611CF07BBB00BFE4AF /* Cast iOS */; 503 | targetProxy = E46F92BC1CF081C200BFE4AF /* PBXContainerItemProxy */; 504 | }; 505 | /* End PBXTargetDependency section */ 506 | 507 | /* Begin PBXVariantGroup section */ 508 | E46F929C1CF0802F00BFE4AF /* Main.storyboard */ = { 509 | isa = PBXVariantGroup; 510 | children = ( 511 | E46F929D1CF0802F00BFE4AF /* Base */, 512 | ); 513 | name = Main.storyboard; 514 | sourceTree = ""; 515 | }; 516 | E46F92A11CF0802F00BFE4AF /* LaunchScreen.storyboard */ = { 517 | isa = PBXVariantGroup; 518 | children = ( 519 | E46F92A21CF0802F00BFE4AF /* Base */, 520 | ); 521 | name = LaunchScreen.storyboard; 522 | sourceTree = ""; 523 | }; 524 | /* End PBXVariantGroup section */ 525 | 526 | /* Begin XCBuildConfiguration section */ 527 | E43CEC8A1CF9FB2F00811415 /* Distribution Ad Hoc */ = { 528 | isa = XCBuildConfiguration; 529 | buildSettings = { 530 | ALWAYS_SEARCH_USER_PATHS = NO; 531 | CLANG_ANALYZER_NONNULL = YES; 532 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 533 | CLANG_CXX_LIBRARY = "libc++"; 534 | CLANG_ENABLE_MODULES = YES; 535 | CLANG_ENABLE_OBJC_ARC = YES; 536 | CLANG_WARN_BOOL_CONVERSION = YES; 537 | CLANG_WARN_CONSTANT_CONVERSION = YES; 538 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 539 | CLANG_WARN_EMPTY_BODY = YES; 540 | CLANG_WARN_ENUM_CONVERSION = YES; 541 | CLANG_WARN_INFINITE_RECURSION = YES; 542 | CLANG_WARN_INT_CONVERSION = YES; 543 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 544 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 545 | CLANG_WARN_UNREACHABLE_CODE = YES; 546 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 547 | CODE_SIGN_IDENTITY = "-"; 548 | COPY_PHASE_STRIP = NO; 549 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 550 | ENABLE_NS_ASSERTIONS = NO; 551 | ENABLE_STRICT_OBJC_MSGSEND = YES; 552 | GCC_C_LANGUAGE_STANDARD = gnu99; 553 | GCC_NO_COMMON_BLOCKS = YES; 554 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 555 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 556 | GCC_WARN_UNDECLARED_SELECTOR = YES; 557 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 558 | GCC_WARN_UNUSED_FUNCTION = YES; 559 | GCC_WARN_UNUSED_VARIABLE = YES; 560 | MACOSX_DEPLOYMENT_TARGET = 10.11; 561 | MTL_ENABLE_DEBUG_INFO = NO; 562 | SDKROOT = macosx; 563 | SWIFT_VERSION = 3.0; 564 | }; 565 | name = "Distribution Ad Hoc"; 566 | }; 567 | E43CEC8B1CF9FB2F00811415 /* Distribution Ad Hoc */ = { 568 | isa = XCBuildConfiguration; 569 | buildSettings = { 570 | PRODUCT_NAME = "$(TARGET_NAME)"; 571 | SWIFT_VERSION = 3.0; 572 | }; 573 | name = "Distribution Ad Hoc"; 574 | }; 575 | E43CEC8C1CF9FB2F00811415 /* Distribution Ad Hoc */ = { 576 | isa = XCBuildConfiguration; 577 | buildSettings = { 578 | CLANG_ENABLE_MODULES = YES; 579 | CODE_SIGNING_ALLOWED = NO; 580 | CODE_SIGNING_REQUIRED = NO; 581 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 582 | CURRENT_PROJECT_VERSION = 1; 583 | DEFINES_MODULE = YES; 584 | DYLIB_COMPATIBILITY_VERSION = 1; 585 | DYLIB_CURRENT_VERSION = 1; 586 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 587 | INFOPLIST_FILE = "cast-script/Info.plist"; 588 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 589 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 590 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 591 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.Cast; 592 | PRODUCT_NAME = Cast; 593 | PROVISIONING_PROFILE = "7642293e-12b0-4cd3-81f5-0d8f60a47de5"; 594 | SDKROOT = iphoneos; 595 | SKIP_INSTALL = YES; 596 | SWIFT_INSTALL_OBJC_HEADER = NO; 597 | SWIFT_VERSION = 3.0; 598 | TARGETED_DEVICE_FAMILY = "1,2"; 599 | VALIDATE_PRODUCT = YES; 600 | VERSIONING_SYSTEM = "apple-generic"; 601 | VERSION_INFO_PREFIX = ""; 602 | }; 603 | name = "Distribution Ad Hoc"; 604 | }; 605 | E43CEC8D1CF9FB2F00811415 /* Distribution Ad Hoc */ = { 606 | isa = XCBuildConfiguration; 607 | buildSettings = { 608 | CLANG_ENABLE_MODULES = YES; 609 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 610 | CURRENT_PROJECT_VERSION = 1; 611 | DEFINES_MODULE = YES; 612 | DYLIB_COMPATIBILITY_VERSION = 1; 613 | DYLIB_CURRENT_VERSION = 1; 614 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 615 | INFOPLIST_FILE = "cast-script/Info.plist"; 616 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 617 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 618 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 619 | MACOSX_DEPLOYMENT_TARGET = 10.10; 620 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.Cast; 621 | PRODUCT_NAME = CastX; 622 | SDKROOT = macosx; 623 | SKIP_INSTALL = YES; 624 | TARGETED_DEVICE_FAMILY = "1,2"; 625 | VALIDATE_PRODUCT = YES; 626 | VERSIONING_SYSTEM = "apple-generic"; 627 | VERSION_INFO_PREFIX = ""; 628 | }; 629 | name = "Distribution Ad Hoc"; 630 | }; 631 | E43CEC8E1CF9FB2F00811415 /* Distribution Ad Hoc */ = { 632 | isa = XCBuildConfiguration; 633 | buildSettings = { 634 | CLANG_ENABLE_MODULES = YES; 635 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 636 | CURRENT_PROJECT_VERSION = 1; 637 | DEFINES_MODULE = YES; 638 | DYLIB_COMPATIBILITY_VERSION = 1; 639 | DYLIB_CURRENT_VERSION = 1; 640 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 641 | INFOPLIST_FILE = "cast-script/Info.plist"; 642 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 643 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 644 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 645 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.Cast; 646 | PRODUCT_NAME = CastTV; 647 | PROVISIONING_PROFILE = "69977b94-8eaa-4bb4-9dec-f75f345c3c49"; 648 | SDKROOT = appletvos; 649 | SKIP_INSTALL = YES; 650 | TARGETED_DEVICE_FAMILY = "1,2"; 651 | TVOS_DEPLOYMENT_TARGET = 9.0; 652 | VALIDATE_PRODUCT = YES; 653 | VERSIONING_SYSTEM = "apple-generic"; 654 | VERSION_INFO_PREFIX = ""; 655 | }; 656 | name = "Distribution Ad Hoc"; 657 | }; 658 | E43CEC8F1CF9FB2F00811415 /* Distribution Ad Hoc */ = { 659 | isa = XCBuildConfiguration; 660 | buildSettings = { 661 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 662 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 663 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 664 | INFOPLIST_FILE = CastingApp/Info.plist; 665 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 666 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 667 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.CastingApp; 668 | PRODUCT_NAME = "$(TARGET_NAME)"; 669 | SDKROOT = iphoneos; 670 | SWIFT_VERSION = 3.0; 671 | VALIDATE_PRODUCT = YES; 672 | }; 673 | name = "Distribution Ad Hoc"; 674 | }; 675 | E43CEC901CF9FB2F00811415 /* Distribution Ad Hoc */ = { 676 | isa = XCBuildConfiguration; 677 | buildSettings = { 678 | BUNDLE_LOADER = "$(TEST_HOST)"; 679 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 680 | INFOPLIST_FILE = CastingAppTests/Info.plist; 681 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 682 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 683 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.CastingAppTests; 684 | PRODUCT_NAME = "$(TARGET_NAME)"; 685 | SDKROOT = iphoneos; 686 | SWIFT_VERSION = 3.0; 687 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CastingApp.app/CastingApp"; 688 | VALIDATE_PRODUCT = YES; 689 | }; 690 | name = "Distribution Ad Hoc"; 691 | }; 692 | E46F92451CF04F6B00BFE4AF /* Debug */ = { 693 | isa = XCBuildConfiguration; 694 | buildSettings = { 695 | ALWAYS_SEARCH_USER_PATHS = NO; 696 | CLANG_ANALYZER_NONNULL = YES; 697 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 698 | CLANG_CXX_LIBRARY = "libc++"; 699 | CLANG_ENABLE_MODULES = YES; 700 | CLANG_ENABLE_OBJC_ARC = YES; 701 | CLANG_WARN_BOOL_CONVERSION = YES; 702 | CLANG_WARN_CONSTANT_CONVERSION = YES; 703 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 704 | CLANG_WARN_EMPTY_BODY = YES; 705 | CLANG_WARN_ENUM_CONVERSION = YES; 706 | CLANG_WARN_INFINITE_RECURSION = YES; 707 | CLANG_WARN_INT_CONVERSION = YES; 708 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 709 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 710 | CLANG_WARN_UNREACHABLE_CODE = YES; 711 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 712 | CODE_SIGN_IDENTITY = "-"; 713 | COPY_PHASE_STRIP = NO; 714 | DEBUG_INFORMATION_FORMAT = dwarf; 715 | ENABLE_STRICT_OBJC_MSGSEND = YES; 716 | ENABLE_TESTABILITY = YES; 717 | GCC_C_LANGUAGE_STANDARD = gnu99; 718 | GCC_DYNAMIC_NO_PIC = NO; 719 | GCC_NO_COMMON_BLOCKS = YES; 720 | GCC_OPTIMIZATION_LEVEL = 0; 721 | GCC_PREPROCESSOR_DEFINITIONS = ( 722 | "DEBUG=1", 723 | "$(inherited)", 724 | ); 725 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 726 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 727 | GCC_WARN_UNDECLARED_SELECTOR = YES; 728 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 729 | GCC_WARN_UNUSED_FUNCTION = YES; 730 | GCC_WARN_UNUSED_VARIABLE = YES; 731 | MACOSX_DEPLOYMENT_TARGET = 10.11; 732 | MTL_ENABLE_DEBUG_INFO = YES; 733 | ONLY_ACTIVE_ARCH = YES; 734 | SDKROOT = macosx; 735 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 736 | SWIFT_VERSION = 3.0; 737 | }; 738 | name = Debug; 739 | }; 740 | E46F92461CF04F6B00BFE4AF /* Release */ = { 741 | isa = XCBuildConfiguration; 742 | buildSettings = { 743 | ALWAYS_SEARCH_USER_PATHS = NO; 744 | CLANG_ANALYZER_NONNULL = YES; 745 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 746 | CLANG_CXX_LIBRARY = "libc++"; 747 | CLANG_ENABLE_MODULES = YES; 748 | CLANG_ENABLE_OBJC_ARC = YES; 749 | CLANG_WARN_BOOL_CONVERSION = YES; 750 | CLANG_WARN_CONSTANT_CONVERSION = YES; 751 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 752 | CLANG_WARN_EMPTY_BODY = YES; 753 | CLANG_WARN_ENUM_CONVERSION = YES; 754 | CLANG_WARN_INFINITE_RECURSION = YES; 755 | CLANG_WARN_INT_CONVERSION = YES; 756 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 757 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 758 | CLANG_WARN_UNREACHABLE_CODE = YES; 759 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 760 | CODE_SIGN_IDENTITY = "-"; 761 | COPY_PHASE_STRIP = NO; 762 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 763 | ENABLE_NS_ASSERTIONS = NO; 764 | ENABLE_STRICT_OBJC_MSGSEND = YES; 765 | GCC_C_LANGUAGE_STANDARD = gnu99; 766 | GCC_NO_COMMON_BLOCKS = YES; 767 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 768 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 769 | GCC_WARN_UNDECLARED_SELECTOR = YES; 770 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 771 | GCC_WARN_UNUSED_FUNCTION = YES; 772 | GCC_WARN_UNUSED_VARIABLE = YES; 773 | MACOSX_DEPLOYMENT_TARGET = 10.11; 774 | MTL_ENABLE_DEBUG_INFO = NO; 775 | SDKROOT = macosx; 776 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 777 | SWIFT_VERSION = 3.0; 778 | }; 779 | name = Release; 780 | }; 781 | E46F92481CF04F6B00BFE4AF /* Debug */ = { 782 | isa = XCBuildConfiguration; 783 | buildSettings = { 784 | PRODUCT_NAME = "$(TARGET_NAME)"; 785 | SWIFT_VERSION = 3.0; 786 | }; 787 | name = Debug; 788 | }; 789 | E46F92491CF04F6B00BFE4AF /* Release */ = { 790 | isa = XCBuildConfiguration; 791 | buildSettings = { 792 | PRODUCT_NAME = "$(TARGET_NAME)"; 793 | SWIFT_VERSION = 3.0; 794 | }; 795 | name = Release; 796 | }; 797 | E46F92681CF07BBB00BFE4AF /* Debug */ = { 798 | isa = XCBuildConfiguration; 799 | buildSettings = { 800 | CLANG_ENABLE_MODULES = YES; 801 | CODE_SIGNING_ALLOWED = NO; 802 | CODE_SIGNING_REQUIRED = NO; 803 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 804 | CURRENT_PROJECT_VERSION = 1; 805 | DEFINES_MODULE = YES; 806 | DYLIB_COMPATIBILITY_VERSION = 1; 807 | DYLIB_CURRENT_VERSION = 1; 808 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 809 | EXPANDED_CODE_SIGN_IDENTITY = ""; 810 | INFOPLIST_FILE = "cast-script/Info.plist"; 811 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 812 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 813 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 814 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.Cast; 815 | PRODUCT_NAME = Cast; 816 | SDKROOT = iphoneos; 817 | SKIP_INSTALL = YES; 818 | SWIFT_INSTALL_OBJC_HEADER = NO; 819 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 820 | SWIFT_VERSION = 3.0; 821 | TARGETED_DEVICE_FAMILY = "1,2"; 822 | VERSIONING_SYSTEM = "apple-generic"; 823 | VERSION_INFO_PREFIX = ""; 824 | }; 825 | name = Debug; 826 | }; 827 | E46F92691CF07BBB00BFE4AF /* Release */ = { 828 | isa = XCBuildConfiguration; 829 | buildSettings = { 830 | CLANG_ENABLE_MODULES = YES; 831 | CODE_SIGNING_ALLOWED = NO; 832 | CODE_SIGNING_REQUIRED = NO; 833 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 834 | CURRENT_PROJECT_VERSION = 1; 835 | DEFINES_MODULE = YES; 836 | DYLIB_COMPATIBILITY_VERSION = 1; 837 | DYLIB_CURRENT_VERSION = 1; 838 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 839 | EXPANDED_CODE_SIGN_IDENTITY = ""; 840 | INFOPLIST_FILE = "cast-script/Info.plist"; 841 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 842 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 843 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 844 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.Cast; 845 | PRODUCT_NAME = Cast; 846 | PROVISIONING_PROFILE = "0eb6405e-d817-4762-a524-0e6a2de5cfd7"; 847 | SDKROOT = iphoneos; 848 | SKIP_INSTALL = YES; 849 | SWIFT_INSTALL_OBJC_HEADER = NO; 850 | SWIFT_VERSION = 3.0; 851 | TARGETED_DEVICE_FAMILY = "1,2"; 852 | VALIDATE_PRODUCT = YES; 853 | VERSIONING_SYSTEM = "apple-generic"; 854 | VERSION_INFO_PREFIX = ""; 855 | }; 856 | name = Release; 857 | }; 858 | E46F92711CF07BCA00BFE4AF /* Debug */ = { 859 | isa = XCBuildConfiguration; 860 | buildSettings = { 861 | CLANG_ENABLE_MODULES = YES; 862 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 863 | CURRENT_PROJECT_VERSION = 1; 864 | DEFINES_MODULE = YES; 865 | DYLIB_COMPATIBILITY_VERSION = 1; 866 | DYLIB_CURRENT_VERSION = 1; 867 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 868 | INFOPLIST_FILE = "cast-script/Info.plist"; 869 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 870 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 871 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 872 | MACOSX_DEPLOYMENT_TARGET = 10.10; 873 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.Cast; 874 | PRODUCT_NAME = CastX; 875 | SDKROOT = macosx; 876 | SKIP_INSTALL = YES; 877 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 878 | TARGETED_DEVICE_FAMILY = "1,2"; 879 | VERSIONING_SYSTEM = "apple-generic"; 880 | VERSION_INFO_PREFIX = ""; 881 | }; 882 | name = Debug; 883 | }; 884 | E46F92721CF07BCA00BFE4AF /* Release */ = { 885 | isa = XCBuildConfiguration; 886 | buildSettings = { 887 | CLANG_ENABLE_MODULES = YES; 888 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 889 | CURRENT_PROJECT_VERSION = 1; 890 | DEFINES_MODULE = YES; 891 | DYLIB_COMPATIBILITY_VERSION = 1; 892 | DYLIB_CURRENT_VERSION = 1; 893 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 894 | INFOPLIST_FILE = "cast-script/Info.plist"; 895 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 896 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 897 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 898 | MACOSX_DEPLOYMENT_TARGET = 10.10; 899 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.Cast; 900 | PRODUCT_NAME = CastX; 901 | SDKROOT = macosx; 902 | SKIP_INSTALL = YES; 903 | TARGETED_DEVICE_FAMILY = "1,2"; 904 | VALIDATE_PRODUCT = YES; 905 | VERSIONING_SYSTEM = "apple-generic"; 906 | VERSION_INFO_PREFIX = ""; 907 | }; 908 | name = Release; 909 | }; 910 | E46F92B11CF0802F00BFE4AF /* Debug */ = { 911 | isa = XCBuildConfiguration; 912 | buildSettings = { 913 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 914 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 915 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 916 | INFOPLIST_FILE = CastingApp/Info.plist; 917 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 918 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 919 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.CastingApp; 920 | PRODUCT_NAME = "$(TARGET_NAME)"; 921 | SDKROOT = iphoneos; 922 | SWIFT_VERSION = 3.0; 923 | }; 924 | name = Debug; 925 | }; 926 | E46F92B21CF0802F00BFE4AF /* Release */ = { 927 | isa = XCBuildConfiguration; 928 | buildSettings = { 929 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 930 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 931 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 932 | INFOPLIST_FILE = CastingApp/Info.plist; 933 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 934 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 935 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.CastingApp; 936 | PRODUCT_NAME = "$(TARGET_NAME)"; 937 | SDKROOT = iphoneos; 938 | SWIFT_VERSION = 3.0; 939 | VALIDATE_PRODUCT = YES; 940 | }; 941 | name = Release; 942 | }; 943 | E46F92B41CF0802F00BFE4AF /* Debug */ = { 944 | isa = XCBuildConfiguration; 945 | buildSettings = { 946 | BUNDLE_LOADER = "$(TEST_HOST)"; 947 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 948 | INFOPLIST_FILE = CastingAppTests/Info.plist; 949 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 950 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 951 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.CastingAppTests; 952 | PRODUCT_NAME = "$(TARGET_NAME)"; 953 | SDKROOT = iphoneos; 954 | SWIFT_VERSION = 3.0; 955 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CastingApp.app/CastingApp"; 956 | }; 957 | name = Debug; 958 | }; 959 | E46F92B51CF0802F00BFE4AF /* Release */ = { 960 | isa = XCBuildConfiguration; 961 | buildSettings = { 962 | BUNDLE_LOADER = "$(TEST_HOST)"; 963 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 964 | INFOPLIST_FILE = CastingAppTests/Info.plist; 965 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 966 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 967 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.CastingAppTests; 968 | PRODUCT_NAME = "$(TARGET_NAME)"; 969 | SDKROOT = iphoneos; 970 | SWIFT_VERSION = 3.0; 971 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CastingApp.app/CastingApp"; 972 | VALIDATE_PRODUCT = YES; 973 | }; 974 | name = Release; 975 | }; 976 | E4A509B41CF0A496007DA42E /* Debug */ = { 977 | isa = XCBuildConfiguration; 978 | buildSettings = { 979 | CLANG_ENABLE_MODULES = YES; 980 | CODE_SIGN_IDENTITY = "iPhone Developer"; 981 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 982 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 983 | CURRENT_PROJECT_VERSION = 1; 984 | DEFINES_MODULE = YES; 985 | DYLIB_COMPATIBILITY_VERSION = 1; 986 | DYLIB_CURRENT_VERSION = 1; 987 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 988 | INFOPLIST_FILE = "cast-script/Info.plist"; 989 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 990 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 991 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 992 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.Cast; 993 | PRODUCT_NAME = CastTV; 994 | SDKROOT = appletvos; 995 | SKIP_INSTALL = YES; 996 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 997 | TARGETED_DEVICE_FAMILY = "1,2"; 998 | TVOS_DEPLOYMENT_TARGET = 9.0; 999 | VERSIONING_SYSTEM = "apple-generic"; 1000 | VERSION_INFO_PREFIX = ""; 1001 | }; 1002 | name = Debug; 1003 | }; 1004 | E4A509B51CF0A496007DA42E /* Release */ = { 1005 | isa = XCBuildConfiguration; 1006 | buildSettings = { 1007 | CLANG_ENABLE_MODULES = YES; 1008 | CODE_SIGN_IDENTITY = "iPhone Distribution: Houzz Inc. (J63Q35M68Q)"; 1009 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 1010 | CURRENT_PROJECT_VERSION = 1; 1011 | DEFINES_MODULE = YES; 1012 | DYLIB_COMPATIBILITY_VERSION = 1; 1013 | DYLIB_CURRENT_VERSION = 1; 1014 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1015 | INFOPLIST_FILE = "cast-script/Info.plist"; 1016 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1017 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 1018 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1019 | PRODUCT_BUNDLE_IDENTIFIER = com.houzz.Cast; 1020 | PRODUCT_NAME = CastTV; 1021 | PROVISIONING_PROFILE = "69977b94-8eaa-4bb4-9dec-f75f345c3c49"; 1022 | SDKROOT = appletvos; 1023 | SKIP_INSTALL = YES; 1024 | TARGETED_DEVICE_FAMILY = "1,2"; 1025 | TVOS_DEPLOYMENT_TARGET = 9.0; 1026 | VALIDATE_PRODUCT = YES; 1027 | VERSIONING_SYSTEM = "apple-generic"; 1028 | VERSION_INFO_PREFIX = ""; 1029 | }; 1030 | name = Release; 1031 | }; 1032 | /* End XCBuildConfiguration section */ 1033 | 1034 | /* Begin XCConfigurationList section */ 1035 | E46F923B1CF04F6B00BFE4AF /* Build configuration list for PBXProject "cast" */ = { 1036 | isa = XCConfigurationList; 1037 | buildConfigurations = ( 1038 | E46F92451CF04F6B00BFE4AF /* Debug */, 1039 | E46F92461CF04F6B00BFE4AF /* Release */, 1040 | E43CEC8A1CF9FB2F00811415 /* Distribution Ad Hoc */, 1041 | ); 1042 | defaultConfigurationIsVisible = 0; 1043 | defaultConfigurationName = Release; 1044 | }; 1045 | E46F92471CF04F6B00BFE4AF /* Build configuration list for PBXNativeTarget "cast" */ = { 1046 | isa = XCConfigurationList; 1047 | buildConfigurations = ( 1048 | E46F92481CF04F6B00BFE4AF /* Debug */, 1049 | E46F92491CF04F6B00BFE4AF /* Release */, 1050 | E43CEC8B1CF9FB2F00811415 /* Distribution Ad Hoc */, 1051 | ); 1052 | defaultConfigurationIsVisible = 0; 1053 | defaultConfigurationName = Release; 1054 | }; 1055 | E46F92671CF07BBB00BFE4AF /* Build configuration list for PBXNativeTarget "Cast iOS" */ = { 1056 | isa = XCConfigurationList; 1057 | buildConfigurations = ( 1058 | E46F92681CF07BBB00BFE4AF /* Debug */, 1059 | E46F92691CF07BBB00BFE4AF /* Release */, 1060 | E43CEC8C1CF9FB2F00811415 /* Distribution Ad Hoc */, 1061 | ); 1062 | defaultConfigurationIsVisible = 0; 1063 | defaultConfigurationName = Release; 1064 | }; 1065 | E46F92701CF07BCA00BFE4AF /* Build configuration list for PBXNativeTarget "Cast OSX" */ = { 1066 | isa = XCConfigurationList; 1067 | buildConfigurations = ( 1068 | E46F92711CF07BCA00BFE4AF /* Debug */, 1069 | E46F92721CF07BCA00BFE4AF /* Release */, 1070 | E43CEC8D1CF9FB2F00811415 /* Distribution Ad Hoc */, 1071 | ); 1072 | defaultConfigurationIsVisible = 0; 1073 | defaultConfigurationName = Release; 1074 | }; 1075 | E46F92B01CF0802F00BFE4AF /* Build configuration list for PBXNativeTarget "CastingApp" */ = { 1076 | isa = XCConfigurationList; 1077 | buildConfigurations = ( 1078 | E46F92B11CF0802F00BFE4AF /* Debug */, 1079 | E46F92B21CF0802F00BFE4AF /* Release */, 1080 | E43CEC8F1CF9FB2F00811415 /* Distribution Ad Hoc */, 1081 | ); 1082 | defaultConfigurationIsVisible = 0; 1083 | defaultConfigurationName = Release; 1084 | }; 1085 | E46F92B31CF0802F00BFE4AF /* Build configuration list for PBXNativeTarget "CastingAppTests" */ = { 1086 | isa = XCConfigurationList; 1087 | buildConfigurations = ( 1088 | E46F92B41CF0802F00BFE4AF /* Debug */, 1089 | E46F92B51CF0802F00BFE4AF /* Release */, 1090 | E43CEC901CF9FB2F00811415 /* Distribution Ad Hoc */, 1091 | ); 1092 | defaultConfigurationIsVisible = 0; 1093 | defaultConfigurationName = Release; 1094 | }; 1095 | E4A509B31CF0A496007DA42E /* Build configuration list for PBXNativeTarget "Cast tvOS" */ = { 1096 | isa = XCConfigurationList; 1097 | buildConfigurations = ( 1098 | E4A509B41CF0A496007DA42E /* Debug */, 1099 | E4A509B51CF0A496007DA42E /* Release */, 1100 | E43CEC8E1CF9FB2F00811415 /* Distribution Ad Hoc */, 1101 | ); 1102 | defaultConfigurationIsVisible = 0; 1103 | defaultConfigurationName = Release; 1104 | }; 1105 | /* End XCConfigurationList section */ 1106 | }; 1107 | rootObject = E46F92381CF04F6B00BFE4AF /* Project object */; 1108 | } 1109 | -------------------------------------------------------------------------------- /cast.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /cast.xcodeproj/xcshareddata/xcschemes/Cast (OSX).xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /cast.xcodeproj/xcshareddata/xcschemes/Cast (iOS).xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /cast.xcodeproj/xcshareddata/xcschemes/Cast (tvOS).xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /cast.xcodeproj/xcshareddata/xcschemes/CastingApp.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /cast.xcodeproj/xcshareddata/xcschemes/cast.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 68 | 69 | 72 | 73 | 76 | 77 | 78 | 79 | 80 | 81 | 87 | 89 | 95 | 96 | 97 | 98 | 100 | 101 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houzz/JSONCast/36379eaa74bef605e9e57d959350a636550ae84f/images/1.png -------------------------------------------------------------------------------- /images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houzz/JSONCast/36379eaa74bef605e9e57d959350a636550ae84f/images/2.png --------------------------------------------------------------------------------