├── Sources
├── .gitignore
├── TwoWayMirror
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── DetailViewController.swift
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
├── AppDelegate.swift
└── MasterViewController.swift
├── Package.swift
├── TwoWayMirror.playground
├── contents.xcplayground
├── Contents.swift
└── Sources
│ └── TwoWayMirror.swift
├── TwoWayMirror.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── TwoWayMirror.xcworkspace
└── contents.xcworkspacedata
├── TwoWayMirror.podspec
├── TwoWayMirrorTests
├── Info.plist
└── TwoWayMirrorTests.swift
├── LICENSE
└── README.md
/Sources:
--------------------------------------------------------------------------------
1 | TwoWayMirror.playground/Sources
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/*
2 | *Library/*
3 | *xcuserdata*
4 |
--------------------------------------------------------------------------------
/TwoWayMirror/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Created by John Holdsworth on 14/03/2017.
3 | //
4 |
5 | import PackageDescription
6 |
7 | let package = Package(
8 | name: "TwoWayMirror"
9 | )
10 |
--------------------------------------------------------------------------------
/TwoWayMirror.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TwoWayMirror.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TwoWayMirror.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
15 |
16 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/TwoWayMirror.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "TwoWayMirror"
3 | s.version = "1.2.0"
4 | s.summary = "Bi-directional reflection for Swift"
5 | s.homepage = "https://github.com/johnno1962/TwoWayMirror"
6 | s.social_media_url = "https://twitter.com/Injection4Xcode"
7 | s.documentation_url = "https://github.com/johnno1962/TwoWayMirror/blob/master/README.md"
8 | s.license = { :type => "MIT" }
9 | s.authors = { "johnno1962" => "mirror@johnholdsworth.com" }
10 |
11 | s.ios.deployment_target = "9.0"
12 | s.osx.deployment_target = "10.11"
13 | s.source = { :git => "https://github.com/johnno1962/TwoWayMirror.git", :tag => s.version }
14 | s.source_files = "TwoWayMirror.playground/Sources/*.swift"
15 | end
16 |
--------------------------------------------------------------------------------
/TwoWayMirrorTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 John Holdsworth
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 |
--------------------------------------------------------------------------------
/TwoWayMirror/DetailViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DetailViewController.swift
3 | // TwoWayMirror
4 | //
5 | // Created by John Holdsworth on 14/02/2018.
6 | // Copyright © 2018 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class DetailViewController: UIViewController {
12 |
13 | @IBOutlet weak var detailDescriptionLabel: UILabel!
14 |
15 |
16 | func configureView() {
17 | // Update the user interface for the detail item.
18 | if let detail = detailItem {
19 | if let label = detailDescriptionLabel {
20 | label.text = detail.description
21 | }
22 | }
23 | }
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 | // Do any additional setup after loading the view, typically from a nib.
28 | configureView()
29 | }
30 |
31 | override func didReceiveMemoryWarning() {
32 | super.didReceiveMemoryWarning()
33 | // Dispose of any resources that can be recreated.
34 | }
35 |
36 | var detailItem: NSDate? {
37 | didSet {
38 | // Update the view.
39 | configureView()
40 | }
41 | }
42 |
43 |
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/TwoWayMirror/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 |
--------------------------------------------------------------------------------
/TwoWayMirror/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | arm64
30 |
31 | UIStatusBarTintParameters
32 |
33 | UINavigationBar
34 |
35 | Style
36 | UIBarStyleDefault
37 | Translucent
38 |
39 |
40 |
41 | UISupportedInterfaceOrientations
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 | UISupportedInterfaceOrientations~ipad
48 |
49 | UIInterfaceOrientationPortrait
50 | UIInterfaceOrientationPortraitUpsideDown
51 | UIInterfaceOrientationLandscapeLeft
52 | UIInterfaceOrientationLandscapeRight
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/TwoWayMirror/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/TwoWayMirror/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // TwoWayMirror
4 | //
5 | // Created by John Holdsworth on 14/02/2018.
6 | // Copyright © 2018 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | let splitViewController = window!.rootViewController as! UISplitViewController
20 | let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
21 | navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
22 | splitViewController.delegate = self
23 | return true
24 | }
25 |
26 | func applicationWillResignActive(_ application: UIApplication) {
27 | // 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.
28 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
29 | }
30 |
31 | func applicationDidEnterBackground(_ application: UIApplication) {
32 | // 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.
33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
34 | }
35 |
36 | func applicationWillEnterForeground(_ application: UIApplication) {
37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 | func applicationDidBecomeActive(_ application: UIApplication) {
41 | // 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.
42 | }
43 |
44 | func applicationWillTerminate(_ application: UIApplication) {
45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
46 | }
47 |
48 | // MARK: - Split view
49 |
50 | func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
51 | guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
52 | guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
53 | if topAsDetailController.detailItem == nil {
54 | // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
55 | return true
56 | }
57 | return false
58 | }
59 |
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/TwoWayMirror/MasterViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MasterViewController.swift
3 | // TwoWayMirror
4 | //
5 | // Created by John Holdsworth on 14/02/2018.
6 | // Copyright © 2018 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class MasterViewController: UITableViewController {
12 |
13 | var detailViewController: DetailViewController? = nil
14 | var objects = [Any]()
15 |
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 | // Do any additional setup after loading the view, typically from a nib.
20 | navigationItem.leftBarButtonItem = editButtonItem
21 |
22 | let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
23 | navigationItem.rightBarButtonItem = addButton
24 | if let split = splitViewController {
25 | let controllers = split.viewControllers
26 | detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
27 | }
28 | }
29 |
30 | override func viewWillAppear(_ animated: Bool) {
31 | clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
32 | super.viewWillAppear(animated)
33 | }
34 |
35 | override func didReceiveMemoryWarning() {
36 | super.didReceiveMemoryWarning()
37 | // Dispose of any resources that can be recreated.
38 | }
39 |
40 | @objc
41 | func insertNewObject(_ sender: Any) {
42 | objects.insert(NSDate(), at: 0)
43 | let indexPath = IndexPath(row: 0, section: 0)
44 | tableView.insertRows(at: [indexPath], with: .automatic)
45 | }
46 |
47 | // MARK: - Segues
48 |
49 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
50 | if segue.identifier == "showDetail" {
51 | if let indexPath = tableView.indexPathForSelectedRow {
52 | let object = objects[indexPath.row] as! NSDate
53 | let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
54 | controller.detailItem = object
55 | controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
56 | controller.navigationItem.leftItemsSupplementBackButton = true
57 | }
58 | }
59 | }
60 |
61 | // MARK: - Table View
62 |
63 | override func numberOfSections(in tableView: UITableView) -> Int {
64 | return 1
65 | }
66 |
67 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
68 | return objects.count
69 | }
70 |
71 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
72 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
73 |
74 | let object = objects[indexPath.row] as! NSDate
75 | cell.textLabel!.text = object.description
76 | return cell
77 | }
78 |
79 | override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
80 | // Return false if you do not want the specified item to be editable.
81 | return true
82 | }
83 |
84 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
85 | if editingStyle == .delete {
86 | objects.remove(at: indexPath.row)
87 | tableView.deleteRows(at: [indexPath], with: .fade)
88 | } else if editingStyle == .insert {
89 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
90 | }
91 | }
92 |
93 |
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/TwoWayMirror.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 |
3 | import Foundation
4 |
5 | enum ExampleEnum: TwoWayEnum {
6 | case one, two(str: String), three(int: Int), four(int: Int, int2: Int)
7 |
8 | static func twDecode(data: inout TwoWayMirror, from: [String: Any]) throws {
9 | let ptr = data.pointer(type: ExampleEnum.self)
10 | switch from["case"] as! String {
11 | case "one":
12 | ptr.pointee = .one
13 | case "two":
14 | ptr.pointee = .two(str: from["let"] as! String)
15 | case "three":
16 | ptr.pointee = .three(int: from["let"] as! Int)
17 | case "four":
18 | ptr.pointee = .four(int: from["int"] as! Int,
19 | int2: from["int2"] as! Int)
20 | default:
21 | throw NSError(domain: "ExampleEnum", code: -1,
22 | userInfo: [NSLocalizedDescriptionKey:
23 | "Invalid case in: \(from)"])
24 | }
25 | }
26 | }
27 | struct ExampleStruct {
28 | let i = 123
29 | }
30 | struct ContainableStruct: TwoWayContainable {
31 | var a1 = 0, a2 = 1
32 | }
33 | final class ExampleClass: NSObject, TwoWayContainable {
34 | let a = [98.0]
35 | let b = 199.0
36 | let c = "Hello"
37 | let d = ExampleStruct()
38 | let e = ExampleEnum.four(int: 1, int2: 9)
39 | let f = Date()
40 | let g = ["A", "B"]
41 | let h: [ContainableStruct]? = nil
42 | let i = [Int]()
43 | let j: [Int]? = nil
44 | let k: ContainableStruct? = nil
45 | let l = [[123, 123], [234, 234]]
46 | let m = ["a": [123, 123], "b": [234, 234]]
47 | let n = ["a": ContainableStruct(), "b": ContainableStruct()]
48 | let o = [["a": [123, 123], "b": [234, 234]], ["a": [123, 123], "b": [234, 234]]]
49 | deinit {
50 | print("deinit")
51 | }
52 | }
53 |
54 | if true {
55 | let instance = ExampleClass()
56 |
57 | print(TwoWayMirror.reflectKeys(any: instance))
58 | print(TwoWayMirror.reflectKeys(any: instance, path: "d"))
59 |
60 | TwoWayMirror.reflect(object: instance, path: "a", type: [Double].self).pointee += [11.0]
61 | print(instance["a", [Double].self])
62 |
63 | instance["b", Double.self] += 100.0
64 | print(instance.b)
65 |
66 | instance["c", String.self] += " String"
67 | print(instance.c)
68 |
69 | instance["d.i", Int.self] += 345
70 | print(instance.d.i)
71 |
72 | instance["e", ExampleEnum.self] = .two(str: "TWO")
73 | print(instance.e)
74 |
75 | instance["f", Date.self] = Date()
76 | print(instance["f", Date.self])
77 |
78 | let data = """
79 | [
80 | {
81 | "a1": 11, "a2": 22
82 | },
83 | {
84 | "a1": 111, "a2": 222
85 | }
86 | ]
87 | """.data(using: .utf8)!
88 |
89 | let array = try! TwoWayMirror.decode([ContainableStruct].self, from: data)
90 | dump(array)
91 | }
92 |
93 | let data = """
94 | {
95 | "a": [77.0, 88.0],
96 | "b": 999.0,
97 | "c": "hello",
98 | "d": {
99 | "i": 789
100 | },
101 | "f": "2018-02-14 06:39:41 +0000",
102 | "g": ["Hello", "World"],
103 | "h": [
104 | {
105 | "a1": 11, "a2": 22
106 | },
107 | {
108 | "a1": 111, "a2": 222
109 | }
110 | ],
111 | "i": [12345, 67890],
112 | "j": [99, 101],
113 | "k": {
114 | "a1": 1111, "a2": 2222
115 | },
116 | "m" : {
117 | "b" : [
118 | 111,
119 | 222
120 | ],
121 | "a" : [
122 | 333,
123 | 444
124 | ]
125 | },
126 | "n" : {
127 | "b" : {
128 | "a2" : 1,
129 | "a1" : 2
130 | },
131 | "a" : {
132 | "a2" : 3,
133 | "a1" : 4
134 | }
135 | },
136 | }
137 | """.data(using: .utf8)!
138 |
139 | let start = Date.timeIntervalSinceReferenceDate
140 | for _ in 0..<10 {
141 | let i1 = ExampleClass()
142 | try! TwoWayMirror.decode(object: i1, json: data)
143 | dump(i1)
144 | let json = try! TwoWayMirror.encode(object: i1, options: [.prettyPrinted])
145 | print(String(data: json, encoding: .utf8)!)
146 | let i2 = try! TwoWayMirror.decode(ExampleClass.self, from: json)
147 | dump(i2)
148 | }
149 | print(Date.timeIntervalSinceReferenceDate-start)
150 |
--------------------------------------------------------------------------------
/TwoWayMirrorTests/TwoWayMirrorTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TwoWayMirrorTests.swift
3 | // TwoWayMirrorTests
4 | //
5 | // Created by John Holdsworth on 14/02/2018.
6 | // Copyright © 2018 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import TwoWayMirror
11 |
12 | class TwoWayMirrorTests: 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 testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 | enum ExampleEnum: TwoWayEnum {
28 | case one, two(str: String), three(int: Int), four(int: Int, int2: Int)
29 |
30 | static func twDecode(data: inout TwoWayMirror, from: [String: Any]) throws {
31 | let ptr = data.pointer(type: ExampleEnum.self)
32 | switch from["case"] as! String {
33 | case "one":
34 | ptr.pointee = .one
35 | case "two":
36 | ptr.pointee = .two(str: from["let"] as! String)
37 | case "three":
38 | ptr.pointee = .three(int: from["let"] as! Int)
39 | case "four":
40 | ptr.pointee = .four(int: from["int"] as! Int,
41 | int2: from["int2"] as! Int)
42 | default:
43 | throw NSError(domain: "ExampleEnum", code: -1,
44 | userInfo: [NSLocalizedDescriptionKey:
45 | "Invalid case in: \(from)"])
46 | }
47 | }
48 | }
49 | struct ExampleStruct {
50 | let i = 123
51 | }
52 | struct ContainableStruct: TwoWayContainable {
53 | var a1 = 0, a2 = 0
54 | }
55 | class ExampleClass: NSObject {
56 | let a = [98.0]
57 | let b = 199.0
58 | let c = "Hello"
59 | let d = ExampleStruct()
60 | let e = ExampleEnum.four(int: 1, int2: 9)
61 | let f = Date()
62 | let g = ["A", "B"]
63 | let h = [ContainableStruct]()
64 | let i = [Int]()
65 | let j: [Int]? = nil
66 | let k: ContainableStruct? = nil
67 | let l = [[123, 123], [234, 234]]
68 | let m = ["a": [123, 123], "b": [234, 234]]
69 | let n = ["a": ContainableStruct(), "b": ContainableStruct()]
70 | let o = [["a": [123, 123], "b": [234, 234]], ["a": [123, 123], "b": [234, 234]]]
71 | let p = URL(string: "https://apple.com")
72 | let q = "123".data(using: .utf8)!
73 | deinit {
74 | print("deinit")
75 | }
76 | }
77 |
78 | if true {
79 | let instance = ExampleClass()
80 |
81 | print(TwoWayMirror.reflectKeys(any: instance))
82 | print(TwoWayMirror.reflectKeys(any: instance, path: "d"))
83 |
84 | TwoWayMirror.reflect(object: instance, path: "a", type: [Double].self).pointee += [11.0]
85 | print(instance["a", [Double].self])
86 |
87 | instance["b", Double.self] += 100.0
88 | print(instance.b)
89 |
90 | instance["c", String.self] += " String"
91 | print(instance.c)
92 |
93 | instance["d.i", Int.self] += 345
94 | print(instance.d.i)
95 |
96 | print(TwoWayMirror.reflectKeys(any: instance, path: "e"))
97 | instance["e", ExampleEnum.self] = .two(str: "FFF")
98 | print(instance.e)
99 | print(TwoWayMirror.reflectKeys(any: instance, path: "e"))
100 | print(TwoWayMirror.reflectKeys(any: instance, path: "e.two"))
101 | print(MemoryLayout.size)
102 |
103 | instance["f", Date.self] = Date()
104 | print(instance["f", Date.self])
105 | }
106 |
107 | let data = """
108 | {
109 | "a": [77.0, 88.0],
110 | "b": 999.0,
111 | "c": "hello",
112 | "d": {
113 | "i": 789
114 | },
115 | "f": "2018-02-14 06:39:41 +0000",
116 | "g": ["Hello", "World"],
117 | "h": [
118 | {
119 | "a1": 11, "a2": 22
120 | },
121 | {
122 | "a1": 111, "a2": 222
123 | }
124 | ],
125 | "i": [12345, 67890],
126 | "j": [99, 101],
127 | "k": {
128 | "a1": 1111, "a2": 2222
129 | },
130 | "p" : "https:\\/\\/apple.com",
131 | }
132 | """.data(using: .utf8)!
133 |
134 | for _ in 0..<10 {
135 | let i1 = ExampleClass()
136 | try! TwoWayMirror.decode(object: i1, json: data)
137 | dump(i1)
138 | let json = try! TwoWayMirror.encode(object: i1, options: [.prettyPrinted])
139 | print(String(data: json, encoding: .utf8)!)
140 | let i2 = ExampleClass()
141 | try! TwoWayMirror.decode(object: i2, json: json)
142 | dump(i2)
143 | }
144 | }
145 |
146 | func testPerformanceExample() {
147 | // This is an example of a performance test case.
148 | self.measure {
149 | // Put the code you want to measure the time of here.
150 | self.testExample()
151 | }
152 | }
153 |
154 | }
155 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TwoWayMirror - bidirectional Swift Mirror
2 |
3 | *** [Recent changes to Swift master](https://github.com/apple/swift/pull/14678) indicate this is unlikely to work after Swift 5 ***
4 |
5 | It's a frustrating limitation of Swift reflection that the [Mirror](http://nshipster.com/mirrortype/) type
6 | can be only used in one direction for reading values from Swift data structures. This project leverages
7 | Swift's internal implementation to remove this limitation by falling back to the original underlying
8 | [RefelectionLegacy.swift](https://github.com/apple/swift/blob/master/stdlib/public/core/ReflectionLegacy.swift#L86)
9 | functionality. Think runtime typed keypaths on steroids.
10 |
11 | The [basic api](TwoWayMirror.playground/Sources/TwoWayMirror.swift) declares the following entry point:
12 |
13 | ```Swift
14 | public func reflect(object: AnyObject, path: String, type: T.Type) -> UnsafeMutablePointer
15 | ```
16 | This will return a typed pointer to any ivar of a class object or it's containing structs, enums, collections
17 | that can be read or assigned to as if you were using a typed keypath.
18 | A subscript is defined on any class derived from NSObject for a Swift valueForKey: replacement.
19 |
20 | ```Swift
21 | public protocol SubScriptReflectable: AnyObject {}
22 | extension NSObject: SubScriptReflectable {}
23 | public extension SubScriptReflectable {
24 | public subscript (path: String, type: T.Type) -> T {
25 | get {
26 | return TwoWayMirror.reflect(object: self, path: path, type: T.self).pointee
27 | }
28 | set(newValue) {
29 | TwoWayMirror.reflect(object: self, path: path, type: T.self).pointee = newValue
30 | }
31 | }
32 | }
33 | ```
34 |
35 | Example usage:
36 |
37 | ```Swift
38 | enum ExampleEnum: TwoWayEnum {
39 | case one, two(str: String), three(int: Int), four(int: Int, int2: Int)
40 |
41 | static func twDecode(data: inout TwoWayMirror, from: [String: Any]) throws {
42 | let ptr = data.pointer(type: ExampleEnum.self)
43 | switch from["case"] as! String {
44 | case "one":
45 | ptr.pointee = .one
46 | case "two":
47 | ptr.pointee = .two(str: from["let"] as! String)
48 | case "three":
49 | ptr.pointee = .three(int: from["let"] as! Int)
50 | case "four":
51 | ptr.pointee = .four(int: from["int"] as! Int,
52 | int2: from["int2"] as! Int)
53 | default:
54 | throw NSError(domain: "ExampleEnum", code: -1,
55 | userInfo: [NSLocalizedDescriptionKey:
56 | "Invalid case in: \(from)"])
57 | }
58 | }
59 | }
60 | struct ExampleStruct {
61 | let i = 123
62 | }
63 | struct ContainableStruct: TwoWayContainable {
64 | var a1 = 0, a2 = 1
65 | }
66 | final class ExampleClass: NSObject, TwoWayContainable {
67 | let a = [98.0]
68 | let b = 199.0
69 | let c = "Hello"
70 | let d = ExampleStruct()
71 | let e = ExampleEnum.four(int: 1, int2: 9)
72 | let f = Date()
73 | let g = ["A", "B"]
74 | let h: [ContainableStruct]? = nil
75 | let i = [Int]()
76 | let j: [Int]? = nil
77 | let k: ContainableStruct? = nil
78 | let l = [[123, 123], [234, 234]]
79 | let m = ["a": [123, 123], "b": [234, 234]]
80 | let n = ["a": ContainableStruct(), "b": ContainableStruct()]
81 | let o = [["a": [123, 123], "b": [234, 234]], ["a": [123, 123], "b": [234, 234]]]
82 | deinit {
83 | print("deinit")
84 | }
85 | }
86 |
87 | if true {
88 | let instance = ExampleClass()
89 |
90 | print(TwoWayMirror.reflectKeys(any: instance))
91 | print(TwoWayMirror.reflectKeys(any: instance, path: "d"))
92 |
93 | TwoWayMirror.reflect(object: instance, path: "a", type: [Double].self).pointee += [11.0]
94 | print(instance["a", [Double].self])
95 |
96 | instance["b", Double.self] += 100.0
97 | print(instance.b)
98 |
99 | instance["c", String.self] += " String"
100 | print(instance.c)
101 |
102 | instance["d.i", Int.self] += 345
103 | print(instance.d.i)
104 |
105 | instance["e", ExampleEnum.self] = .two(str: "TWO")
106 | print(instance.e)
107 |
108 | instance["f", Date.self] = Date()
109 | print(instance["f", Date.self])
110 |
111 | let data = """
112 | [
113 | {
114 | "a1": 11, "a2": 22
115 | },
116 | {
117 | "a1": 111, "a2": 222
118 | }
119 | ]
120 | """.data(using: .utf8)!
121 |
122 | let array = try! TwoWayMirror.decode([ContainableStruct].self, from: data)
123 | dump(array)
124 | }
125 | ```
126 |
127 | This has been used to produce an alternative implementation of Codable for working with JSON.
128 |
129 | ```Swift
130 | let data = """
131 | {
132 | "a": [77.0, 88.0],
133 | "b": 999.0,
134 | "c": "hello",
135 | "d": {
136 | "i": 789
137 | },
138 | "f": "2018-02-14 06:39:41 +0000",
139 | "g": ["Hello", "World"],
140 | "h": [
141 | {
142 | "a1": 11, "a2": 22
143 | },
144 | {
145 | "a1": 111, "a2": 222
146 | }
147 | ],
148 | "i": [12345, 67890],
149 | "j": [99, 101],
150 | "k": {
151 | "a1": 1111, "a2": 2222
152 | },
153 | "m" : {
154 | "b" : [
155 | 111,
156 | 222
157 | ],
158 | "a" : [
159 | 333,
160 | 444
161 | ]
162 | },
163 | "n" : {
164 | "b" : {
165 | "a2" : 1,
166 | "a1" : 2
167 | },
168 | "a" : {
169 | "a2" : 3,
170 | "a1" : 4
171 | }
172 | },
173 | }
174 | """.data(using: .utf8)!
175 |
176 | let start = Date.timeIntervalSinceReferenceDate
177 | for _ in 0..<10 {
178 | let i1 = ExampleClass()
179 | try! TwoWayMirror.decode(object: i1, json: data)
180 | dump(i1)
181 | let json = try! TwoWayMirror.encode(object: i1, options: [.prettyPrinted])
182 | print(String(data: json, encoding: .utf8)!)
183 | let i2 = try! TwoWayMirror.decode(ExampleClass.self, from: json)
184 | dump(i2)
185 | }
186 | print(Date.timeIntervalSinceReferenceDate-start)
187 | ```
188 |
189 | The JSON implementation will decode and encode composed structs and class instances,
190 | Ints, Doubles and String along with Arrays or Optionals of these and Arrays or Optionals of
191 | structs or class instances which implement the `TwoWayContainable` protocol (which just
192 | requires they have an init() methhod.) For writing using reflection to work (decoding) the
193 | top level object must be an instance of a class. Otherwise, a copy is taken when the object
194 | is reflected and any changes will be lost.
195 |
196 | Automatic encoding of enums is possible but for decoding you must opt-in to the `TwoWayEnum`
197 | protocol and supply an implementation to initialise an enum from a dictionary.
198 |
199 | While this approach bends a few rules it has proven to be robust making very few
200 | assumptions about the Swift reflection implementation.
201 |
--------------------------------------------------------------------------------
/TwoWayMirror/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 |
27 |
28 |
29 |
30 |
31 |
32 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/TwoWayMirror.playground/Sources/TwoWayMirror.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TwoWayMirror.swift
3 | // TwoWayMirror
4 | //
5 | // Created by John Holdsworth on 13/02/2018.
6 | // Copyright © 2018 John Holdsworth. All rights reserved.
7 | //
8 | // $Id: //depot/TwoWayMirror/TwoWayMirror.playground/Sources/TwoWayMirror.swift#76 $
9 | //
10 |
11 | import Foundation
12 |
13 | // MARK: Assumptions...
14 | // https://medium.com/@vhart/protocols-generics-and-existential-containers-wait-what-e2e698262ab1
15 | // https://github.com/apple/swift/blob/master/stdlib/public/core/ReflectionLegacy.swift#L86
16 |
17 | @_silgen_name("swift_reflectAny")
18 | internal func _reflect(_ x: T) -> _Mirror
19 |
20 | @_silgen_name("swift_EnumMirror_caseName")
21 | internal func _enumMirror_caseName(
22 | _ data: _MagicMirrorData) -> UnsafePointer
23 |
24 | // _Mirror is a protocol with concrete implementations _StructMirror, _ClassMirror etc
25 | // which fit into the 3 slots available in the "existential" data structure representing
26 | // the protocol so we can coerce a pointer to _Mirror to the internal "_MagicMirrorData"
27 |
28 | // MARK: Basic reflection API
29 |
30 | @_fixed_layout
31 | public struct TwoWayMirror {
32 | let owner: UnsafeRawPointer
33 | /// pointer to memory containing ivar
34 | public let ptr: UnsafeMutableRawPointer
35 | /// type represented at that memory location
36 | public let metadata: Any.Type
37 | // let protocolType: Any.Type
38 | // let protocolWitness: UnsafeRawPointer
39 |
40 | /// Cast data pointer to specific type
41 | ///
42 | /// - Parameters:
43 | /// - type: assumed type of ivar
44 | /// - Returns: typed pointer to ivar
45 | public func pointer(type: T.Type, file: StaticString = #file, line: UInt = #line)
46 | -> UnsafeMutablePointer {
47 | guard metadata == T.self else {
48 | fatalError("TwoWayMirror type mismatch: \(metadata) != \(T.self) at \(file)#\(line)")
49 | }
50 | return ptr.assumingMemoryBound(to: T.self)
51 | }
52 |
53 | public subscript(type: T.Type) -> T {
54 | get {
55 | return pointer(type: T.self).pointee
56 | }
57 | set(newValue) {
58 | pointer(type: T.self).pointee = newValue
59 | }
60 | }
61 |
62 | /// Get TwoWayMirror information for a mirrored ivar
63 | ///
64 | /// - Parameters:
65 | /// - mirror: pointer to Swift reflection information
66 | /// - path: dotted keypath to ivar of interest
67 | /// - Returns: TwoWyayMirror information
68 | public static func reflect(mirror: UnsafePointer<_Mirror>, path: [String]? = nil) -> TwoWayMirror {
69 | if var path = path, !path.isEmpty {
70 | let key = path.removeFirst()
71 | for index in 0 ..< mirror.pointee.count {
72 | var (name, submirror) = mirror.pointee[index]
73 | if name == key {
74 | return reflect(mirror: &submirror, path: path)
75 | }
76 | }
77 | fatalError("TwoWayMirror could not find path component: \(key)")
78 | }
79 |
80 | return mirror.withMemoryRebound(to: TwoWayMirror.self, capacity: 1) {
81 | $0.pointee
82 | }
83 | }
84 |
85 | /// Reflect a typed pointer to a class instance ivar
86 | ///
87 | /// - Parameters:
88 | /// - object: pointer to class instance
89 | /// - path: dotted path to ivar of interest
90 | /// - type: assumed type of instance
91 | /// - Returns: typed pointer to memory of ivar
92 | public static func reflect(object: AnyObject, path: String, type: T.Type,
93 | file: StaticString = #file, line: UInt = #line) -> UnsafeMutablePointer {
94 | var mirror = _reflect(object)
95 | let data = reflect(mirror: &mirror, path: path.components(separatedBy: "."))
96 | return data.pointer(type: T.self, file: file, line: line)
97 | }
98 |
99 | /// List ivars of class struct of interest
100 | ///
101 | /// - Parameters:
102 | /// - any: class instance/struct
103 | /// - path: dotted path to ivar of interest
104 | /// - Returns: list of ivars in memory order
105 | public static func reflectKeys(any: Any, path: String? = nil) -> [String] {
106 | var mirror = _reflect(any)
107 | if let path = path {
108 | for key in path.components(separatedBy: ".") {
109 | for index in 0 ..< mirror.count {
110 | let (name, submirror) = mirror[index]
111 | if name == key {
112 | mirror = submirror
113 | break
114 | }
115 | }
116 | }
117 | }
118 | return (0 ..< mirror.count).map { mirror[$0].0 }
119 | }
120 | }
121 |
122 | /// conformance for subscript equivalent of keypaths
123 | public protocol SubScriptReflectable: AnyObject {}
124 | extension NSObject: SubScriptReflectable {}
125 |
126 | public extension SubScriptReflectable {
127 | public subscript(path: String, type: T.Type) -> T {
128 | get {
129 | return TwoWayMirror.reflect(object: self, path: path, type: T.self).pointee
130 | }
131 | set(newValue) {
132 | TwoWayMirror.reflect(object: self, path: path, type: T.self).pointee = newValue
133 | }
134 | }
135 | }
136 |
137 | public func TWError(_ string: String) -> Error {
138 | return NSError(domain: "TwoWayMirror", code: -1,
139 | userInfo: [NSLocalizedDescriptionKey: "TwoWayMirror \(string)"])
140 | }
141 |
142 | // MARK: JSON encoding / decoding as reflection example
143 |
144 | extension TwoWayMirror {
145 |
146 | /// Create and decode object from JSON data
147 | ///
148 | /// - Parameters:
149 | /// - instanceType: Type conforming to TwoWayContainable
150 | /// - json: JSON format instance Data
151 | /// - options: JSON reading options
152 | /// - Returns: New initialised instance of instanceType
153 | /// - Throws: Any error encountered during encoding
154 | public static func decode(_ containableType: T.Type, from json: Data,
155 | options: JSONSerialization.ReadingOptions = []) throws -> T {
156 | let any = try JSONSerialization.jsonObject(with: json, options: options)
157 | return try containableType.decodeElement(from: any)
158 | }
159 |
160 | /// Decode ivar values from JSON onto object
161 | ///
162 | /// - Parameters:
163 | /// - object: class instance to take values
164 | /// - json: JSON Data packet
165 | /// - options: JSON reading options
166 | /// - Throws: JSON/type conversion errors
167 | public static func decode(object: AnyObject, json: Data,
168 | options: JSONSerialization.ReadingOptions = []) throws {
169 | try decode(object: object,
170 | any: try JSONSerialization.jsonObject(with: json, options: options))
171 | }
172 |
173 | /// Decode ivar values from foundation class representation
174 | ///
175 | /// - Parameters:
176 | /// - object: class instance to take values
177 | /// - any: foundation representation of values
178 | /// - Throws: type conversion errors
179 | public static func decode(object: AnyObject, any: Any) throws {
180 | var mirror = _reflect(object)
181 | try decode(mirror: &mirror, any: any)
182 | }
183 |
184 | static func decode(mirror: UnsafePointer<_Mirror>, any: Any) throws {
185 | var data = reflect(mirror: mirror)
186 | if let codableType = data.metadata as? TwoWayCodable.Type {
187 | try codableType.twDecode(data: &data, any: any)
188 | }
189 | else if let enumType = data.metadata as? TwoWayEnum.Type {
190 | try enumType.twDecode(data: &data, from: try cast(any, to: [String: Any].self))
191 | }
192 | else if mirror.pointee.count != 0, let dict = any as? [String: Any] {
193 | for index in 0 ..< mirror.pointee.count {
194 | var (name, submirror) = mirror.pointee[index]
195 | if let value = dict[name] {
196 | try decode(mirror: &submirror, any: value)
197 | }
198 | }
199 | }
200 | }
201 |
202 | /// encode a class instance/struct to JSON
203 | ///
204 | /// - Parameters:
205 | /// - object: class instance/struct
206 | /// - options: JSON writing options
207 | /// - Returns: JSON Data packet
208 | /// - Throws: JSON errors
209 | public static func encode(object: Any,
210 | options: JSONSerialization.WritingOptions) throws -> Data {
211 | return try JSONSerialization.data(withJSONObject: encode(object: object),
212 | options: options)
213 | }
214 |
215 | /// encode class instance/struct to representation as foundation objects
216 | ///
217 | /// - Parameter object: class instance/struct
218 | /// - Returns: NSDictionary
219 | public static func encode(object: Any) -> Any {
220 | var mirror = _reflect(object)
221 | return encode(mirror: &mirror)
222 | }
223 |
224 | static func encode(mirror: UnsafePointer<_Mirror>) -> Any {
225 | let data = reflect(mirror: mirror)
226 | if let codableType = data.metadata as? TwoWayCodable.Type {
227 | return codableType.twEncode(data: data)
228 | }
229 | else if data.metadata is TwoWayEnum.Type {
230 | var dict = [String: Any]()
231 | dict["case"] =
232 | mirror.withMemoryRebound(to: _MagicMirrorData.self, capacity: 1) {
233 | String(utf8String: _enumMirror_caseName($0.pointee))
234 | }
235 | if mirror.pointee.count != 0 {
236 | var (_, submirror) = mirror.pointee[0]
237 | if submirror.count == 1 {
238 | dict["let"] = encode(mirror: &submirror)
239 | } else {
240 | for index in 0 ..< submirror.count {
241 | var (name, letmirror) = submirror[index]
242 | dict[name] = encode(mirror: &letmirror)
243 | }
244 | }
245 | }
246 | return dict
247 | }
248 | else {
249 | var dict = [String: Any]()
250 | for index in 0 ..< mirror.pointee.count {
251 | var (name, submirror) = mirror.pointee[index]
252 | if name == "super" { continue }
253 | dict[name] = encode(mirror: &submirror)
254 | }
255 | return dict
256 | }
257 | }
258 |
259 | public static func cast(_ any: Any, to type: T.Type,
260 | file: StaticString = #file, line: UInt = #line) throws -> T {
261 | guard let cast = any as? T else {
262 | throw TWError("invalid cast of \(any) to \(T.self) at \(file)#\(line)")
263 | }
264 | return cast
265 | }
266 |
267 | public static var dateFormatter: DateFormatter = {
268 | let dateFormatter = DateFormatter()
269 | dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ssZZZZZ"
270 | return dateFormatter
271 | }()
272 | }
273 |
274 | // MARK: Codable conformances
275 |
276 | /// Conform to be codable
277 | public protocol TwoWayCodable {
278 | static func twDecode(data: inout TwoWayMirror, any: Any) throws
279 | static func twEncode(data: TwoWayMirror) -> Any
280 | }
281 |
282 | /// Conformance for decoding enums
283 | public protocol TwoWayEnum {
284 | static func twDecode(data: inout TwoWayMirror, from dict: [String: Any]) throws
285 | }
286 |
287 | /// Objects that can be created decoding containers or optionals
288 | public protocol TwoWayContainable {
289 | init()
290 | }
291 |
292 | private class Crucible {
293 | var instance = T()
294 | }
295 | extension TwoWayContainable {
296 | static func decodeElement(from: Any) throws -> Self {
297 | let instanceHolder = Crucible()
298 | var (_, mirror) = _reflect(instanceHolder)[0]
299 | try TwoWayMirror.decode(mirror: &mirror, any: from)
300 | return instanceHolder.instance
301 | }
302 | }
303 |
304 | public protocol TwoWayCastable: TwoWayContainable {}
305 | extension TwoWayCastable {
306 | public static func twDecode(data: inout TwoWayMirror, any: Any) throws {
307 | #if os(Linux)
308 | if Self.self == Double.self {
309 | data[Double.self] = try (any as? Int).flatMap { Double($0) } ??
310 | TwoWayMirror.cast(any, to: Double.self)
311 | return
312 | }
313 | #endif
314 | data[Self.self] = try TwoWayMirror.cast(any, to: Self.self)
315 | }
316 | public static func twEncode(data: TwoWayMirror) -> Any {
317 | return data[Self.self]
318 | }
319 | }
320 |
321 | extension Int: TwoWayCodable, TwoWayCastable {}
322 | extension Double: TwoWayCodable, TwoWayCastable {}
323 | extension String: TwoWayCodable, TwoWayCastable {}
324 |
325 | extension Date: TwoWayCodable, TwoWayContainable {
326 | public static func twDecode(data: inout TwoWayMirror, any: Any) throws {
327 | let string = try TwoWayMirror.cast(any, to: String.self)
328 | guard let date = TwoWayMirror.dateFormatter.date(from: string) else {
329 | throw TWError("unable to parse date: '\(string)'")
330 | }
331 | data[Date.self] = date
332 | }
333 | public static func twEncode(data: TwoWayMirror) -> Any {
334 | return TwoWayMirror.dateFormatter.string(from: data[Date.self])
335 | }
336 | }
337 |
338 | extension Data: TwoWayCodable, TwoWayContainable {
339 | public static func twDecode(data: inout TwoWayMirror, any: Any) throws {
340 | let string = try TwoWayMirror.cast(any, to: String.self)
341 | guard let base64 = Data(base64Encoded: string) else {
342 | throw TWError("unable to decode base64: '\(string)'")
343 | }
344 | data[Data.self] = base64
345 | }
346 | public static func twEncode(data: TwoWayMirror) -> Any {
347 | return data[Data.self].base64EncodedString()
348 | }
349 | }
350 |
351 | extension URL: TwoWayCodable, TwoWayContainable {
352 | public init() {
353 | self.init(string: "http://void.org")!
354 | }
355 | public static func twDecode(data: inout TwoWayMirror, any: Any) throws {
356 | let string = try TwoWayMirror.cast(any, to: String.self)
357 | guard let url = URL(string: string) else {
358 | throw TWError("unable to parse url: '\(string)'")
359 | }
360 | data[URL.self] = url
361 | }
362 | public static func twEncode(data: TwoWayMirror) -> Any {
363 | return data[URL.self].absoluteString
364 | }
365 | }
366 |
367 | extension Array: TwoWayCodable, TwoWayContainable {
368 | public static func twDecode(data: inout TwoWayMirror, any: Any) throws {
369 | guard let containableType = Element.self as? TwoWayContainable.Type else {
370 | throw TWError("unable to decode array containing \(Element.self)")
371 | }
372 | data[[Element].self] =
373 | try TwoWayMirror.cast(any, to: [Any].self)
374 | .map { try containableType.decodeElement(from: $0) as! Element }
375 | }
376 | public static func twEncode(data: TwoWayMirror) -> Any {
377 | return data[[Element].self].map {
378 | (e: Element) -> Any in
379 | var mirror = _reflect(e)
380 | return TwoWayMirror.encode(mirror: &mirror)
381 | }
382 | }
383 | }
384 |
385 | extension Dictionary: TwoWayCodable, TwoWayContainable {
386 | public static func twDecode(data: inout TwoWayMirror, any: Any) throws {
387 | guard let containableType = Value.self as? TwoWayContainable.Type else {
388 | throw TWError("unable to decode dictionary containing \(Value.self)")
389 | }
390 | let dictPtr = data.pointer(type: [Key: Value].self)
391 | dictPtr.pointee.removeAll()
392 | for (key, value) in try TwoWayMirror.cast(any, to: [Key: Any].self) {
393 | dictPtr.pointee[key] = try containableType.decodeElement(from: value) as? Value
394 | }
395 | }
396 | public static func twEncode(data: TwoWayMirror) -> Any {
397 | var dict = [Key: Any]()
398 | for (key, value) in data[[Key: Value].self] {
399 | var mirror = _reflect(value)
400 | dict[key] = TwoWayMirror.encode(mirror: &mirror)
401 | }
402 | return dict
403 | }
404 | }
405 |
406 | extension Optional: TwoWayCodable {
407 | public static func twDecode(data: inout TwoWayMirror, any: Any) throws {
408 | let optionalPtr = data.pointer(type: Optional.self)
409 | if any is NSNull {
410 | optionalPtr.pointee = nil
411 | }
412 | else {
413 | guard let subtype = Wrapped.self as? TwoWayContainable.Type else {
414 | throw TWError("unable to decode optional of \(Wrapped.self)")
415 | }
416 | let instance = try subtype.decodeElement(from: any)
417 | optionalPtr.pointee = .some(try TwoWayMirror.cast(instance, to: Wrapped.self))
418 | }
419 | }
420 | public static func twEncode(data: TwoWayMirror) -> Any {
421 | guard let some = data[Optional.self] else {
422 | return NSNull()
423 | }
424 | var mirror = _reflect(some)
425 | return TwoWayMirror.encode(mirror: &mirror)
426 | }
427 | }
428 |
--------------------------------------------------------------------------------
/TwoWayMirror.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | BB765361203476E200BBD463 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB765360203476E200BBD463 /* AppDelegate.swift */; };
11 | BB765363203476E200BBD463 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB765362203476E200BBD463 /* MasterViewController.swift */; };
12 | BB765365203476E200BBD463 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB765364203476E200BBD463 /* DetailViewController.swift */; };
13 | BB765368203476E200BBD463 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB765366203476E200BBD463 /* Main.storyboard */; };
14 | BB76536A203476E300BBD463 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BB765369203476E300BBD463 /* Assets.xcassets */; };
15 | BB76536D203476E300BBD463 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB76536B203476E300BBD463 /* LaunchScreen.storyboard */; };
16 | BB765378203476E300BBD463 /* TwoWayMirrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB765377203476E300BBD463 /* TwoWayMirrorTests.swift */; };
17 | BB765385203477AD00BBD463 /* TwoWayMirror.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB765384203477AC00BBD463 /* TwoWayMirror.swift */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXContainerItemProxy section */
21 | BB765374203476E300BBD463 /* PBXContainerItemProxy */ = {
22 | isa = PBXContainerItemProxy;
23 | containerPortal = BB765355203476E200BBD463 /* Project object */;
24 | proxyType = 1;
25 | remoteGlobalIDString = BB76535C203476E200BBD463;
26 | remoteInfo = TwoWayMirror;
27 | };
28 | /* End PBXContainerItemProxy section */
29 |
30 | /* Begin PBXFileReference section */
31 | BB76535D203476E200BBD463 /* TwoWayMirror.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TwoWayMirror.app; sourceTree = BUILT_PRODUCTS_DIR; };
32 | BB765360203476E200BBD463 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
33 | BB765362203476E200BBD463 /* MasterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = ""; };
34 | BB765364203476E200BBD463 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; };
35 | BB765367203476E200BBD463 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
36 | BB765369203476E300BBD463 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
37 | BB76536C203476E300BBD463 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
38 | BB76536E203476E300BBD463 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
39 | BB765373203476E300BBD463 /* TwoWayMirrorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TwoWayMirrorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
40 | BB765377203476E300BBD463 /* TwoWayMirrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoWayMirrorTests.swift; sourceTree = ""; };
41 | BB765379203476E300BBD463 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
42 | BB765384203477AC00BBD463 /* TwoWayMirror.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TwoWayMirror.swift; path = TwoWayMirror.playground/Sources/TwoWayMirror.swift; sourceTree = SOURCE_ROOT; };
43 | /* End PBXFileReference section */
44 |
45 | /* Begin PBXFrameworksBuildPhase section */
46 | BB76535A203476E200BBD463 /* Frameworks */ = {
47 | isa = PBXFrameworksBuildPhase;
48 | buildActionMask = 2147483647;
49 | files = (
50 | );
51 | runOnlyForDeploymentPostprocessing = 0;
52 | };
53 | BB765370203476E300BBD463 /* Frameworks */ = {
54 | isa = PBXFrameworksBuildPhase;
55 | buildActionMask = 2147483647;
56 | files = (
57 | );
58 | runOnlyForDeploymentPostprocessing = 0;
59 | };
60 | /* End PBXFrameworksBuildPhase section */
61 |
62 | /* Begin PBXGroup section */
63 | BB765354203476E200BBD463 = {
64 | isa = PBXGroup;
65 | children = (
66 | BB76535F203476E200BBD463 /* TwoWayMirror */,
67 | BB765376203476E300BBD463 /* TwoWayMirrorTests */,
68 | BB76535E203476E200BBD463 /* Products */,
69 | );
70 | sourceTree = "";
71 | };
72 | BB76535E203476E200BBD463 /* Products */ = {
73 | isa = PBXGroup;
74 | children = (
75 | BB76535D203476E200BBD463 /* TwoWayMirror.app */,
76 | BB765373203476E300BBD463 /* TwoWayMirrorTests.xctest */,
77 | );
78 | name = Products;
79 | sourceTree = "";
80 | };
81 | BB76535F203476E200BBD463 /* TwoWayMirror */ = {
82 | isa = PBXGroup;
83 | children = (
84 | BB765384203477AC00BBD463 /* TwoWayMirror.swift */,
85 | BB765360203476E200BBD463 /* AppDelegate.swift */,
86 | BB765362203476E200BBD463 /* MasterViewController.swift */,
87 | BB765364203476E200BBD463 /* DetailViewController.swift */,
88 | BB765366203476E200BBD463 /* Main.storyboard */,
89 | BB765369203476E300BBD463 /* Assets.xcassets */,
90 | BB76536B203476E300BBD463 /* LaunchScreen.storyboard */,
91 | BB76536E203476E300BBD463 /* Info.plist */,
92 | );
93 | path = TwoWayMirror;
94 | sourceTree = "";
95 | };
96 | BB765376203476E300BBD463 /* TwoWayMirrorTests */ = {
97 | isa = PBXGroup;
98 | children = (
99 | BB765377203476E300BBD463 /* TwoWayMirrorTests.swift */,
100 | BB765379203476E300BBD463 /* Info.plist */,
101 | );
102 | path = TwoWayMirrorTests;
103 | sourceTree = "";
104 | };
105 | /* End PBXGroup section */
106 |
107 | /* Begin PBXNativeTarget section */
108 | BB76535C203476E200BBD463 /* TwoWayMirror */ = {
109 | isa = PBXNativeTarget;
110 | buildConfigurationList = BB76537C203476E300BBD463 /* Build configuration list for PBXNativeTarget "TwoWayMirror" */;
111 | buildPhases = (
112 | BB765359203476E200BBD463 /* Sources */,
113 | BB76535A203476E200BBD463 /* Frameworks */,
114 | BB76535B203476E200BBD463 /* Resources */,
115 | );
116 | buildRules = (
117 | );
118 | dependencies = (
119 | );
120 | name = TwoWayMirror;
121 | productName = TwoWayMirror;
122 | productReference = BB76535D203476E200BBD463 /* TwoWayMirror.app */;
123 | productType = "com.apple.product-type.application";
124 | };
125 | BB765372203476E300BBD463 /* TwoWayMirrorTests */ = {
126 | isa = PBXNativeTarget;
127 | buildConfigurationList = BB76537F203476E300BBD463 /* Build configuration list for PBXNativeTarget "TwoWayMirrorTests" */;
128 | buildPhases = (
129 | BB76536F203476E300BBD463 /* Sources */,
130 | BB765370203476E300BBD463 /* Frameworks */,
131 | BB765371203476E300BBD463 /* Resources */,
132 | );
133 | buildRules = (
134 | );
135 | dependencies = (
136 | BB765375203476E300BBD463 /* PBXTargetDependency */,
137 | );
138 | name = TwoWayMirrorTests;
139 | productName = TwoWayMirrorTests;
140 | productReference = BB765373203476E300BBD463 /* TwoWayMirrorTests.xctest */;
141 | productType = "com.apple.product-type.bundle.unit-test";
142 | };
143 | /* End PBXNativeTarget section */
144 |
145 | /* Begin PBXProject section */
146 | BB765355203476E200BBD463 /* Project object */ = {
147 | isa = PBXProject;
148 | attributes = {
149 | LastSwiftUpdateCheck = 0930;
150 | LastUpgradeCheck = 0930;
151 | ORGANIZATIONNAME = "John Holdsworth";
152 | TargetAttributes = {
153 | BB76535C203476E200BBD463 = {
154 | CreatedOnToolsVersion = 9.3;
155 | LastSwiftMigration = 0930;
156 | ProvisioningStyle = Automatic;
157 | };
158 | BB765372203476E300BBD463 = {
159 | CreatedOnToolsVersion = 9.3;
160 | LastSwiftMigration = 0930;
161 | ProvisioningStyle = Automatic;
162 | TestTargetID = BB76535C203476E200BBD463;
163 | };
164 | };
165 | };
166 | buildConfigurationList = BB765358203476E200BBD463 /* Build configuration list for PBXProject "TwoWayMirror" */;
167 | compatibilityVersion = "Xcode 8.0";
168 | developmentRegion = en;
169 | hasScannedForEncodings = 0;
170 | knownRegions = (
171 | en,
172 | Base,
173 | );
174 | mainGroup = BB765354203476E200BBD463;
175 | productRefGroup = BB76535E203476E200BBD463 /* Products */;
176 | projectDirPath = "";
177 | projectRoot = "";
178 | targets = (
179 | BB76535C203476E200BBD463 /* TwoWayMirror */,
180 | BB765372203476E300BBD463 /* TwoWayMirrorTests */,
181 | );
182 | };
183 | /* End PBXProject section */
184 |
185 | /* Begin PBXResourcesBuildPhase section */
186 | BB76535B203476E200BBD463 /* Resources */ = {
187 | isa = PBXResourcesBuildPhase;
188 | buildActionMask = 2147483647;
189 | files = (
190 | BB76536D203476E300BBD463 /* LaunchScreen.storyboard in Resources */,
191 | BB76536A203476E300BBD463 /* Assets.xcassets in Resources */,
192 | BB765368203476E200BBD463 /* Main.storyboard in Resources */,
193 | );
194 | runOnlyForDeploymentPostprocessing = 0;
195 | };
196 | BB765371203476E300BBD463 /* Resources */ = {
197 | isa = PBXResourcesBuildPhase;
198 | buildActionMask = 2147483647;
199 | files = (
200 | );
201 | runOnlyForDeploymentPostprocessing = 0;
202 | };
203 | /* End PBXResourcesBuildPhase section */
204 |
205 | /* Begin PBXSourcesBuildPhase section */
206 | BB765359203476E200BBD463 /* Sources */ = {
207 | isa = PBXSourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | BB765365203476E200BBD463 /* DetailViewController.swift in Sources */,
211 | BB765363203476E200BBD463 /* MasterViewController.swift in Sources */,
212 | BB765361203476E200BBD463 /* AppDelegate.swift in Sources */,
213 | BB765385203477AD00BBD463 /* TwoWayMirror.swift in Sources */,
214 | );
215 | runOnlyForDeploymentPostprocessing = 0;
216 | };
217 | BB76536F203476E300BBD463 /* Sources */ = {
218 | isa = PBXSourcesBuildPhase;
219 | buildActionMask = 2147483647;
220 | files = (
221 | BB765378203476E300BBD463 /* TwoWayMirrorTests.swift in Sources */,
222 | );
223 | runOnlyForDeploymentPostprocessing = 0;
224 | };
225 | /* End PBXSourcesBuildPhase section */
226 |
227 | /* Begin PBXTargetDependency section */
228 | BB765375203476E300BBD463 /* PBXTargetDependency */ = {
229 | isa = PBXTargetDependency;
230 | target = BB76535C203476E200BBD463 /* TwoWayMirror */;
231 | targetProxy = BB765374203476E300BBD463 /* PBXContainerItemProxy */;
232 | };
233 | /* End PBXTargetDependency section */
234 |
235 | /* Begin PBXVariantGroup section */
236 | BB765366203476E200BBD463 /* Main.storyboard */ = {
237 | isa = PBXVariantGroup;
238 | children = (
239 | BB765367203476E200BBD463 /* Base */,
240 | );
241 | name = Main.storyboard;
242 | sourceTree = "";
243 | };
244 | BB76536B203476E300BBD463 /* LaunchScreen.storyboard */ = {
245 | isa = PBXVariantGroup;
246 | children = (
247 | BB76536C203476E300BBD463 /* Base */,
248 | );
249 | name = LaunchScreen.storyboard;
250 | sourceTree = "";
251 | };
252 | /* End PBXVariantGroup section */
253 |
254 | /* Begin XCBuildConfiguration section */
255 | BB76537A203476E300BBD463 /* Debug */ = {
256 | isa = XCBuildConfiguration;
257 | buildSettings = {
258 | ALWAYS_SEARCH_USER_PATHS = NO;
259 | CLANG_ANALYZER_NONNULL = YES;
260 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
261 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
262 | CLANG_CXX_LIBRARY = "libc++";
263 | CLANG_ENABLE_MODULES = YES;
264 | CLANG_ENABLE_OBJC_ARC = YES;
265 | CLANG_ENABLE_OBJC_WEAK = YES;
266 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
267 | CLANG_WARN_BOOL_CONVERSION = YES;
268 | CLANG_WARN_COMMA = YES;
269 | CLANG_WARN_CONSTANT_CONVERSION = YES;
270 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
271 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
272 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
273 | CLANG_WARN_EMPTY_BODY = YES;
274 | CLANG_WARN_ENUM_CONVERSION = YES;
275 | CLANG_WARN_INFINITE_RECURSION = YES;
276 | CLANG_WARN_INT_CONVERSION = YES;
277 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
278 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
279 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
281 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
282 | CLANG_WARN_STRICT_PROTOTYPES = YES;
283 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
284 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
285 | CLANG_WARN_UNREACHABLE_CODE = YES;
286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
287 | CODE_SIGN_IDENTITY = "iPhone Developer";
288 | COPY_PHASE_STRIP = NO;
289 | DEBUG_INFORMATION_FORMAT = dwarf;
290 | ENABLE_STRICT_OBJC_MSGSEND = YES;
291 | ENABLE_TESTABILITY = YES;
292 | GCC_C_LANGUAGE_STANDARD = gnu11;
293 | GCC_DYNAMIC_NO_PIC = NO;
294 | GCC_NO_COMMON_BLOCKS = YES;
295 | GCC_OPTIMIZATION_LEVEL = 0;
296 | GCC_PREPROCESSOR_DEFINITIONS = (
297 | "DEBUG=1",
298 | "$(inherited)",
299 | );
300 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
301 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
302 | GCC_WARN_UNDECLARED_SELECTOR = YES;
303 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
304 | GCC_WARN_UNUSED_FUNCTION = YES;
305 | GCC_WARN_UNUSED_VARIABLE = YES;
306 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
307 | MTL_ENABLE_DEBUG_INFO = YES;
308 | ONLY_ACTIVE_ARCH = YES;
309 | SDKROOT = iphoneos;
310 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
311 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
312 | SWIFT_VERSION = 3.0;
313 | };
314 | name = Debug;
315 | };
316 | BB76537B203476E300BBD463 /* Release */ = {
317 | isa = XCBuildConfiguration;
318 | buildSettings = {
319 | ALWAYS_SEARCH_USER_PATHS = NO;
320 | CLANG_ANALYZER_NONNULL = YES;
321 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
322 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
323 | CLANG_CXX_LIBRARY = "libc++";
324 | CLANG_ENABLE_MODULES = YES;
325 | CLANG_ENABLE_OBJC_ARC = YES;
326 | CLANG_ENABLE_OBJC_WEAK = YES;
327 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
328 | CLANG_WARN_BOOL_CONVERSION = YES;
329 | CLANG_WARN_COMMA = YES;
330 | CLANG_WARN_CONSTANT_CONVERSION = YES;
331 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
333 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
334 | CLANG_WARN_EMPTY_BODY = YES;
335 | CLANG_WARN_ENUM_CONVERSION = YES;
336 | CLANG_WARN_INFINITE_RECURSION = YES;
337 | CLANG_WARN_INT_CONVERSION = YES;
338 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
339 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
340 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
341 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
342 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
343 | CLANG_WARN_STRICT_PROTOTYPES = YES;
344 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
345 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
346 | CLANG_WARN_UNREACHABLE_CODE = YES;
347 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
348 | CODE_SIGN_IDENTITY = "iPhone Developer";
349 | COPY_PHASE_STRIP = NO;
350 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
351 | ENABLE_NS_ASSERTIONS = NO;
352 | ENABLE_STRICT_OBJC_MSGSEND = YES;
353 | GCC_C_LANGUAGE_STANDARD = gnu11;
354 | GCC_NO_COMMON_BLOCKS = YES;
355 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
356 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
357 | GCC_WARN_UNDECLARED_SELECTOR = YES;
358 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
359 | GCC_WARN_UNUSED_FUNCTION = YES;
360 | GCC_WARN_UNUSED_VARIABLE = YES;
361 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
362 | MTL_ENABLE_DEBUG_INFO = NO;
363 | SDKROOT = iphoneos;
364 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
365 | SWIFT_VERSION = 3.0;
366 | VALIDATE_PRODUCT = YES;
367 | };
368 | name = Release;
369 | };
370 | BB76537D203476E300BBD463 /* Debug */ = {
371 | isa = XCBuildConfiguration;
372 | buildSettings = {
373 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
374 | CODE_SIGN_STYLE = Automatic;
375 | DEVELOPMENT_TEAM = 9V5A8WE85E;
376 | INFOPLIST_FILE = TwoWayMirror/Info.plist;
377 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
378 | PRODUCT_BUNDLE_IDENTIFIER = com.johnholdsworth.TwoWayMirror;
379 | PRODUCT_NAME = "$(TARGET_NAME)";
380 | TARGETED_DEVICE_FAMILY = "1,2";
381 | };
382 | name = Debug;
383 | };
384 | BB76537E203476E300BBD463 /* Release */ = {
385 | isa = XCBuildConfiguration;
386 | buildSettings = {
387 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
388 | CODE_SIGN_STYLE = Automatic;
389 | DEVELOPMENT_TEAM = 9V5A8WE85E;
390 | INFOPLIST_FILE = TwoWayMirror/Info.plist;
391 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
392 | PRODUCT_BUNDLE_IDENTIFIER = com.johnholdsworth.TwoWayMirror;
393 | PRODUCT_NAME = "$(TARGET_NAME)";
394 | TARGETED_DEVICE_FAMILY = "1,2";
395 | };
396 | name = Release;
397 | };
398 | BB765380203476E300BBD463 /* Debug */ = {
399 | isa = XCBuildConfiguration;
400 | buildSettings = {
401 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
402 | BUNDLE_LOADER = "$(TEST_HOST)";
403 | CODE_SIGN_STYLE = Automatic;
404 | DEVELOPMENT_TEAM = 9V5A8WE85E;
405 | INFOPLIST_FILE = TwoWayMirrorTests/Info.plist;
406 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
407 | PRODUCT_BUNDLE_IDENTIFIER = com.johnholdsworth.TwoWayMirrorTests;
408 | PRODUCT_NAME = "$(TARGET_NAME)";
409 | TARGETED_DEVICE_FAMILY = "1,2";
410 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TwoWayMirror.app/TwoWayMirror";
411 | };
412 | name = Debug;
413 | };
414 | BB765381203476E300BBD463 /* Release */ = {
415 | isa = XCBuildConfiguration;
416 | buildSettings = {
417 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
418 | BUNDLE_LOADER = "$(TEST_HOST)";
419 | CODE_SIGN_STYLE = Automatic;
420 | DEVELOPMENT_TEAM = 9V5A8WE85E;
421 | INFOPLIST_FILE = TwoWayMirrorTests/Info.plist;
422 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
423 | PRODUCT_BUNDLE_IDENTIFIER = com.johnholdsworth.TwoWayMirrorTests;
424 | PRODUCT_NAME = "$(TARGET_NAME)";
425 | TARGETED_DEVICE_FAMILY = "1,2";
426 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TwoWayMirror.app/TwoWayMirror";
427 | };
428 | name = Release;
429 | };
430 | /* End XCBuildConfiguration section */
431 |
432 | /* Begin XCConfigurationList section */
433 | BB765358203476E200BBD463 /* Build configuration list for PBXProject "TwoWayMirror" */ = {
434 | isa = XCConfigurationList;
435 | buildConfigurations = (
436 | BB76537A203476E300BBD463 /* Debug */,
437 | BB76537B203476E300BBD463 /* Release */,
438 | );
439 | defaultConfigurationIsVisible = 0;
440 | defaultConfigurationName = Release;
441 | };
442 | BB76537C203476E300BBD463 /* Build configuration list for PBXNativeTarget "TwoWayMirror" */ = {
443 | isa = XCConfigurationList;
444 | buildConfigurations = (
445 | BB76537D203476E300BBD463 /* Debug */,
446 | BB76537E203476E300BBD463 /* Release */,
447 | );
448 | defaultConfigurationIsVisible = 0;
449 | defaultConfigurationName = Release;
450 | };
451 | BB76537F203476E300BBD463 /* Build configuration list for PBXNativeTarget "TwoWayMirrorTests" */ = {
452 | isa = XCConfigurationList;
453 | buildConfigurations = (
454 | BB765380203476E300BBD463 /* Debug */,
455 | BB765381203476E300BBD463 /* Release */,
456 | );
457 | defaultConfigurationIsVisible = 0;
458 | defaultConfigurationName = Release;
459 | };
460 | /* End XCConfigurationList section */
461 | };
462 | rootObject = BB765355203476E200BBD463 /* Project object */;
463 | }
464 |
--------------------------------------------------------------------------------