├── .gitignore ├── .gitmodules ├── .travis.yml ├── CONTRIBUTING.md ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── LICENSE.md ├── README.md ├── ReactiveViewModel.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── ReactiveViewModel Mac.xcscheme │ └── ReactiveViewModel iOS.xcscheme ├── ReactiveViewModel.xcworkspace └── contents.xcworkspacedata ├── ReactiveViewModel ├── Info.plist ├── RVMViewModel.h ├── RVMViewModel.m └── ReactiveViewModel.h ├── ReactiveViewModelTests ├── Info.plist ├── RVMTestViewModel.h ├── RVMTestViewModel.m ├── RVMViewModelSpec.m └── SwiftSpec.swift └── script ├── LICENSE.md ├── README.md ├── bootstrap ├── cibuild ├── schemes.awk ├── targets.awk ├── xcodebuild.awk └── xctool.awk /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Carthage.checkout/Nimble"] 2 | path = Carthage/Checkouts/Nimble 3 | url = https://github.com/Quick/Nimble.git 4 | [submodule "Carthage.checkout/Quick"] 5 | path = Carthage/Checkouts/Quick 6 | url = https://github.com/Quick/Quick.git 7 | [submodule "Carthage.checkout/xcconfigs"] 8 | path = Carthage/Checkouts/xcconfigs 9 | url = https://github.com/jspahrsummers/xcconfigs.git 10 | [submodule "Carthage/Checkouts/Nimble"] 11 | url = https://github.com/Quick/Nimble.git 12 | [submodule "Carthage/Checkouts/Quick"] 13 | url = https://github.com/Quick/Quick.git 14 | [submodule "Carthage/Checkouts/ReactiveObjC"] 15 | path = Carthage/Checkouts/ReactiveObjC 16 | url = https://github.com/ReactiveCocoa/ReactiveObjC.git 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | script: script/cibuild 3 | notifications: 4 | email: false 5 | campfire: 6 | on_success: always 7 | on_failure: always 8 | rooms: 9 | - secure: "QVVzkmaWyLgGMOgtaBF2EUQS3Ot1Bj7jc8CI7L1mqxrDpNnxtemgSNBkvFNrec0RoSMiHBY9t+aVdnHsCLlqhDs3mX55zxlbTJPDpFTVI2p77n90teuOr1kK+a3DYoyaxO6acrAJwhjOe6j9zd3o/su+VG5OsXqCXhBFfnc863w=" 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | We love that you're interested in contributing to this project! 2 | 3 | To make the process as painless as possible, we have just a couple of guidelines 4 | that should make life easier for everyone involved. 5 | 6 | ## Prefer Pull Requests 7 | 8 | If you know exactly how to implement the feature being suggested or fix the bug 9 | being reported, please open a pull request instead of an issue. Pull requests are easier than 10 | patches or inline code blocks for discussing and merging the changes. 11 | 12 | If you can't make the change yourself, please open an issue after making sure 13 | that one isn't already logged. 14 | 15 | ## Contributing Code 16 | 17 | Fork this repository, make it awesomer (preferably in a branch named for the 18 | topic), send a pull request! 19 | 20 | All code contributions should match our [coding 21 | conventions](https://github.com/github/objective-c-conventions). 22 | 23 | Thanks for contributing! :boom::camel: 24 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "ReactiveCocoa/ReactiveObjC" ~> 3.0.0 2 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "jspahrsummers/xcconfigs" >= 0.7.1 2 | github "Quick/Quick" ~> 1.1 3 | github "Quick/Nimble" ~> 7.0 4 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v7.0.1" 2 | github "Quick/Quick" "v1.1.0" 3 | github "ReactiveCocoa/ReactiveObjC" "3.0.0" 4 | github "jspahrsummers/xcconfigs" "0.10" 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | **Copyright (c) 2013 GitHub, Inc. All rights reserved.** 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ReactiveViewModel [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 3 | 4 | ReactiveViewModel is a combination code/documentation project for building Cocoa 5 | applications using [Model-View-ViewModel](#model-view-viewmodel) and 6 | [ReactiveCocoa](#reactivecocoa). 7 | 8 | By explaining rationale, documenting best practices, and providing reusable 9 | library components, we want to make MVVM in Objective-C appealing and easy. 10 | 11 | ## Model-View-ViewModel 12 | 13 | Most Cocoa developers are familiar with the 14 | [Model-View-Controller](http://en.wikipedia.org/wiki/Model-View-Controller) 15 | (MVC) pattern: 16 | 17 | ![Model-View-Controller](https://f.cloud.github.com/assets/432536/867983/280867ea-f760-11e2-9425-8d1345ffdfb9.png) 18 | 19 | **[Model-View-ViewModel](http://en.wikipedia.org/wiki/Model-View-ViewModel) 20 | (MVVM)** is another architectural paradigm for GUI applications: 21 | 22 | ![Model-View-ViewModel](https://f.cloud.github.com/assets/432536/867984/291ed380-f760-11e2-9106-d3158320af39.png) 23 | 24 | Although it seems similar to MVC (except with a "view model" object in place of 25 | the controller), there's one major difference — **the view owns the view 26 | model**. Unlike a controller, a view model has no knowledge of the specific view 27 | that's using it. 28 | 29 | This seemingly minor change offers huge benefits: 30 | 31 | 1. **View models are testable.** Since they don't need a view to do their work, 32 | presentation behavior can be tested without any UI automation or stubbing. 33 | 1. **View models can be used like models.** If desired, view models can be 34 | copied or serialized just like a domain model. This can be used to quickly 35 | implement UI restoration and similar behaviors. 36 | 1. **View models are (mostly) platform-agnostic.** Since the actual UI code 37 | lives in the view, well-designed view models can be used on the iPhone, 38 | iPad, and Mac, with only minor tweaking for each platform. 39 | 1. **Views and view controllers are simpler.** Once the important logic is 40 | moved elsewhere, views and VCs become dumb UI objects. This makes them 41 | easier to understand and redesign. 42 | 43 | In short, replacing MVC with MVVM can lead to more versatile and rigorous UI 44 | code. 45 | 46 | ### What's in a view model? 47 | 48 | A view model is like an [adapter](http://en.wikipedia.org/wiki/Adapter_pattern) 49 | for the model that makes it suitable for presentation. The view model is also 50 | where presentation _behavior_ goes. 51 | 52 | For example, a view model might handle: 53 | 54 | * Kicking off network or database requests 55 | * Determining when information should be hidden or shown 56 | * Date and number formatting 57 | * Localization 58 | 59 | However, the view model is not responsible for actually presenting 60 | information or handling input — that's the sole domain of the view layer. When 61 | the view model needs to communicate something to the view, it does so through 62 | a system of [data binding](#reactivecocoa). 63 | 64 | ### What about view controllers? 65 | 66 | OS X and iOS both have view (or window) controllers, which may be confusing at 67 | first glance, since MVVM only refers to a view. 68 | 69 | But upon closer inspection, it becomes apparent that view controllers are 70 | _actually just part of the view layer_, since they handle things like: 71 | 72 | * Layout 73 | * Animations 74 | * Device rotation 75 | * View and window transitions 76 | * Presenting loaded UI 77 | 78 | So, "the view" actually means the view _layer_, which includes view controllers. 79 | There's no need to have a view and a view controller for the same section of the 80 | screen, though — just pick whichever class is easier for the use case. 81 | 82 | No matter whether you decide to use a view or a view controller, you'll still 83 | have a view model. 84 | 85 | ## ReactiveCocoa 86 | 87 | MVVM is most successful with a powerful system of [data 88 | binding](http://en.wikipedia.org/wiki/UI_data_binding). 89 | [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) is one such 90 | system. 91 | 92 | By modeling changes as 93 | [signals](https://github.com/ReactiveCocoa/ReactiveCocoa#introduction), the view 94 | model can communicate to the view without actually needing to know that it 95 | exists (similarly for model → view model communication). This decoupling is 96 | why view models can be tested without a view in place — the test simply needs to 97 | connect to the VM's signals and verify that the behavior is correct. 98 | 99 | ReactiveCocoa also includes other conveniences that are hugely beneficial for 100 | MVVM, like 101 | [commands](https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/FrameworkOverview.md#commands), 102 | and built-in bindings for AppKit and UIKit. 103 | 104 | ## Getting Started 105 | 106 | To build ReactiveViewModel in isolation, open `ReactiveViewModel.xcworkspace`. To integrate it into your project, include `ReactiveViewModel.xcodeproj` and `ReactiveCocoa.xcodeproj` and link your target against the ReactiveViewModel and ReactiveCocoa targets for your platform. 107 | 108 | ## More Resources 109 | 110 | Model-View-ViewModel was originally developed by 111 | [Microsoft](http://bit.ly/gQY00r), so many of the examples are specific to WPF 112 | or Silverlight, but there are still a few resources that may be useful: 113 | 114 | **Blog posts:** 115 | 116 | * [Basic MVVM with ReactiveCocoa](http://cocoasamurai.blogspot.com/2013/03/basic-mvvm-with-reactivecocoa.html) 117 | * [Model-View-ViewModel for iOS](http://www.teehanlax.com/blog/model-view-viewmodel-for-ios/) 118 | * [Presentation Model](http://martinfowler.com/eaaDev/PresentationModel.html) 119 | 120 | **Presentations:** 121 | 122 | * [Code Reuse with MVVM](https://speakerdeck.com/jspahrsummers/code-reuse-with-mvvm) 123 | -------------------------------------------------------------------------------- /ReactiveViewModel.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BEB7DA4419634C4700F69C4F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEB7DA4219634B5A00F69C4F /* XCTest.framework */; }; 11 | D0731B7219FEFE2A00C01E16 /* SwiftSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0731B7119FEFE2A00C01E16 /* SwiftSpec.swift */; }; 12 | D0731B7319FEFE2A00C01E16 /* SwiftSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0731B7119FEFE2A00C01E16 /* SwiftSpec.swift */; }; 13 | D0948A59178159AD00BA8F23 /* ReactiveViewModel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0948A3B178159AD00BA8F23 /* ReactiveViewModel.framework */; }; 14 | D0948AB417815ABD00BA8F23 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0948AB317815ABD00BA8F23 /* Foundation.framework */; }; 15 | D0948AB517815AC200BA8F23 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0948AB317815ABD00BA8F23 /* Foundation.framework */; }; 16 | D0948ABA17815AF100BA8F23 /* ReactiveViewModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D0948A4B178159AD00BA8F23 /* ReactiveViewModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | D0948B551781610600BA8F23 /* RVMViewModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D0948B531781610600BA8F23 /* RVMViewModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | D0948B571781610600BA8F23 /* RVMViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D0948B541781610600BA8F23 /* RVMViewModel.m */; }; 19 | D0948B5A1781618A00BA8F23 /* RVMViewModelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0948B591781618A00BA8F23 /* RVMViewModelSpec.m */; }; 20 | D0948B5E178161A800BA8F23 /* RVMTestViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D0948B5D178161A800BA8F23 /* RVMTestViewModel.m */; }; 21 | D0C7C3B219FEFB520069B5C0 /* ReactiveViewModel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3A719FEFB520069B5C0 /* ReactiveViewModel.framework */; }; 22 | D0C7C3C419FEFC1C0069B5C0 /* RVMViewModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D0948B531781610600BA8F23 /* RVMViewModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 | D0C7C3C519FEFC1C0069B5C0 /* RVMViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D0948B541781610600BA8F23 /* RVMViewModel.m */; }; 24 | D0C7C3C619FEFC1F0069B5C0 /* ReactiveViewModel.h in Headers */ = {isa = PBXBuildFile; fileRef = D0948A4B178159AD00BA8F23 /* ReactiveViewModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25 | D0C7C3C819FEFC7D0069B5C0 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3C719FEFC7D0069B5C0 /* ReactiveObjC.framework */; }; 26 | D0C7C3C919FEFC950069B5C0 /* ReactiveObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3C719FEFC7D0069B5C0 /* ReactiveObjC.framework */; }; 27 | D0C7C3CA19FEFCBE0069B5C0 /* RVMViewModelSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = D0948B591781618A00BA8F23 /* RVMViewModelSpec.m */; }; 28 | D0C7C3CB19FEFCBE0069B5C0 /* RVMTestViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D0948B5D178161A800BA8F23 /* RVMTestViewModel.m */; }; 29 | D0C7C3D019FEFCDF0069B5C0 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3CE19FEFCDF0069B5C0 /* Quick.framework */; }; 30 | D0C7C3D219FEFD040069B5C0 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3D119FEFD040069B5C0 /* Nimble.framework */; }; 31 | D0C7C3D319FEFD040069B5C0 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3D119FEFD040069B5C0 /* Nimble.framework */; }; 32 | D0C7C3D419FEFD0A0069B5C0 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3CE19FEFCDF0069B5C0 /* Quick.framework */; }; 33 | D0C7C3D619FEFD1B0069B5C0 /* ReactiveObjC.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3C719FEFC7D0069B5C0 /* ReactiveObjC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34 | D0C7C3D719FEFD1D0069B5C0 /* Nimble.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3D119FEFD040069B5C0 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 35 | D0C7C3D819FEFD1F0069B5C0 /* Quick.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D0C7C3CE19FEFCDF0069B5C0 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 36 | D0C7C3DA19FEFD260069B5C0 /* ReactiveViewModel.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D0948A3B178159AD00BA8F23 /* ReactiveViewModel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 37 | /* End PBXBuildFile section */ 38 | 39 | /* Begin PBXContainerItemProxy section */ 40 | D0948A57178159AD00BA8F23 /* PBXContainerItemProxy */ = { 41 | isa = PBXContainerItemProxy; 42 | containerPortal = D0948A32178159AD00BA8F23 /* Project object */; 43 | proxyType = 1; 44 | remoteGlobalIDString = D0948A3A178159AD00BA8F23; 45 | remoteInfo = ReactiveViewModel; 46 | }; 47 | D0C7C3B319FEFB520069B5C0 /* PBXContainerItemProxy */ = { 48 | isa = PBXContainerItemProxy; 49 | containerPortal = D0948A32178159AD00BA8F23 /* Project object */; 50 | proxyType = 1; 51 | remoteGlobalIDString = D0C7C3A619FEFB520069B5C0; 52 | remoteInfo = ReactiveViewModel; 53 | }; 54 | /* End PBXContainerItemProxy section */ 55 | 56 | /* Begin PBXCopyFilesBuildPhase section */ 57 | D0C7C3D519FEFD120069B5C0 /* Copy Frameworks */ = { 58 | isa = PBXCopyFilesBuildPhase; 59 | buildActionMask = 2147483647; 60 | dstPath = ""; 61 | dstSubfolderSpec = 10; 62 | files = ( 63 | D0C7C3D619FEFD1B0069B5C0 /* ReactiveObjC.framework in Copy Frameworks */, 64 | D0C7C3D719FEFD1D0069B5C0 /* Nimble.framework in Copy Frameworks */, 65 | D0C7C3D819FEFD1F0069B5C0 /* Quick.framework in Copy Frameworks */, 66 | D0C7C3DA19FEFD260069B5C0 /* ReactiveViewModel.framework in Copy Frameworks */, 67 | ); 68 | name = "Copy Frameworks"; 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXCopyFilesBuildPhase section */ 72 | 73 | /* Begin PBXFileReference section */ 74 | BEB7DA4219634B5A00F69C4F /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 75 | D0731B7119FEFE2A00C01E16 /* SwiftSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftSpec.swift; sourceTree = ""; }; 76 | D0948A3B178159AD00BA8F23 /* ReactiveViewModel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveViewModel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 77 | D0948A46178159AD00BA8F23 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 78 | D0948A4B178159AD00BA8F23 /* ReactiveViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveViewModel.h; sourceTree = ""; }; 79 | D0948A53178159AD00BA8F23 /* ReactiveViewModel Mac Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ReactiveViewModel Mac Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | D0948A5C178159AD00BA8F23 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 81 | D0948A75178159DA00BA8F23 /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; 82 | D0948A77178159DA00BA8F23 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 83 | D0948A78178159DA00BA8F23 /* Profile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Profile.xcconfig; sourceTree = ""; }; 84 | D0948A79178159DA00BA8F23 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 85 | D0948A7A178159DA00BA8F23 /* Test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Test.xcconfig; sourceTree = ""; }; 86 | D0948A7C178159DA00BA8F23 /* Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Application.xcconfig; sourceTree = ""; }; 87 | D0948A7D178159DA00BA8F23 /* StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibrary.xcconfig; sourceTree = ""; }; 88 | D0948A7F178159DA00BA8F23 /* iOS-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Application.xcconfig"; sourceTree = ""; }; 89 | D0948A80178159DA00BA8F23 /* iOS-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-Base.xcconfig"; sourceTree = ""; }; 90 | D0948A81178159DA00BA8F23 /* iOS-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "iOS-StaticLibrary.xcconfig"; sourceTree = ""; }; 91 | D0948A83178159DA00BA8F23 /* Mac-Application.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Application.xcconfig"; sourceTree = ""; }; 92 | D0948A84178159DA00BA8F23 /* Mac-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Base.xcconfig"; sourceTree = ""; }; 93 | D0948A85178159DA00BA8F23 /* Mac-DynamicLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-DynamicLibrary.xcconfig"; sourceTree = ""; }; 94 | D0948A86178159DA00BA8F23 /* Mac-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-Framework.xcconfig"; sourceTree = ""; }; 95 | D0948A87178159DA00BA8F23 /* Mac-StaticLibrary.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Mac-StaticLibrary.xcconfig"; sourceTree = ""; }; 96 | D0948A88178159DA00BA8F23 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.md; sourceTree = ""; }; 97 | D0948AB317815ABD00BA8F23 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 98 | D0948AB617815AC700BA8F23 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 99 | D0948B3E17815E9700BA8F23 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; 100 | D0948B531781610600BA8F23 /* RVMViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RVMViewModel.h; sourceTree = ""; }; 101 | D0948B541781610600BA8F23 /* RVMViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RVMViewModel.m; sourceTree = ""; }; 102 | D0948B591781618A00BA8F23 /* RVMViewModelSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RVMViewModelSpec.m; sourceTree = ""; }; 103 | D0948B5C178161A800BA8F23 /* RVMTestViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RVMTestViewModel.h; sourceTree = ""; }; 104 | D0948B5D178161A800BA8F23 /* RVMTestViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RVMTestViewModel.m; sourceTree = ""; }; 105 | D0C7C39E19FEF8F60069B5C0 /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Framework.xcconfig; path = Carthage/Checkouts/xcconfigs/Base/Targets/Framework.xcconfig; sourceTree = SOURCE_ROOT; }; 106 | D0C7C39F19FEF8FC0069B5C0 /* iOS-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "iOS-Framework.xcconfig"; path = "Carthage/Checkouts/xcconfigs/iOS/iOS-Framework.xcconfig"; sourceTree = SOURCE_ROOT; }; 107 | D0C7C3A719FEFB520069B5C0 /* ReactiveViewModel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveViewModel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 108 | D0C7C3B119FEFB520069B5C0 /* ReactiveViewModel iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ReactiveViewModel iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 109 | D0C7C3C719FEFC7D0069B5C0 /* ReactiveObjC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ReactiveObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 110 | D0C7C3CE19FEFCDF0069B5C0 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 111 | D0C7C3D119FEFD040069B5C0 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 112 | /* End PBXFileReference section */ 113 | 114 | /* Begin PBXFrameworksBuildPhase section */ 115 | D0948A37178159AD00BA8F23 /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | D0948AB417815ABD00BA8F23 /* Foundation.framework in Frameworks */, 120 | D0C7C3C919FEFC950069B5C0 /* ReactiveObjC.framework in Frameworks */, 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | D0948A4F178159AD00BA8F23 /* Frameworks */ = { 125 | isa = PBXFrameworksBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | D0C7C3D219FEFD040069B5C0 /* Nimble.framework in Frameworks */, 129 | D0C7C3D019FEFCDF0069B5C0 /* Quick.framework in Frameworks */, 130 | BEB7DA4419634C4700F69C4F /* XCTest.framework in Frameworks */, 131 | D0948A59178159AD00BA8F23 /* ReactiveViewModel.framework in Frameworks */, 132 | D0948AB517815AC200BA8F23 /* Foundation.framework in Frameworks */, 133 | ); 134 | runOnlyForDeploymentPostprocessing = 0; 135 | }; 136 | D0C7C3A319FEFB520069B5C0 /* Frameworks */ = { 137 | isa = PBXFrameworksBuildPhase; 138 | buildActionMask = 2147483647; 139 | files = ( 140 | D0C7C3C819FEFC7D0069B5C0 /* ReactiveObjC.framework in Frameworks */, 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | D0C7C3AE19FEFB520069B5C0 /* Frameworks */ = { 145 | isa = PBXFrameworksBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | D0C7C3B219FEFB520069B5C0 /* ReactiveViewModel.framework in Frameworks */, 149 | D0C7C3D319FEFD040069B5C0 /* Nimble.framework in Frameworks */, 150 | D0C7C3D419FEFD0A0069B5C0 /* Quick.framework in Frameworks */, 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXFrameworksBuildPhase section */ 155 | 156 | /* Begin PBXGroup section */ 157 | D0948A31178159AD00BA8F23 = { 158 | isa = PBXGroup; 159 | children = ( 160 | D0948A44178159AD00BA8F23 /* ReactiveViewModel */, 161 | D0948A5A178159AD00BA8F23 /* ReactiveViewModelTests */, 162 | D0948A3D178159AD00BA8F23 /* Frameworks */, 163 | D0948A73178159DA00BA8F23 /* Configuration */, 164 | D0948A3C178159AD00BA8F23 /* Products */, 165 | ); 166 | sourceTree = ""; 167 | }; 168 | D0948A3C178159AD00BA8F23 /* Products */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | D0948A3B178159AD00BA8F23 /* ReactiveViewModel.framework */, 172 | D0948A53178159AD00BA8F23 /* ReactiveViewModel Mac Tests.xctest */, 173 | D0C7C3A719FEFB520069B5C0 /* ReactiveViewModel.framework */, 174 | D0C7C3B119FEFB520069B5C0 /* ReactiveViewModel iOS Tests.xctest */, 175 | ); 176 | name = Products; 177 | sourceTree = ""; 178 | }; 179 | D0948A3D178159AD00BA8F23 /* Frameworks */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | D0C7C3D119FEFD040069B5C0 /* Nimble.framework */, 183 | D0C7C3CE19FEFCDF0069B5C0 /* Quick.framework */, 184 | D0C7C3C719FEFC7D0069B5C0 /* ReactiveObjC.framework */, 185 | BEB7DA4219634B5A00F69C4F /* XCTest.framework */, 186 | D0948AB617815AC700BA8F23 /* Foundation.framework */, 187 | D0948AB317815ABD00BA8F23 /* Foundation.framework */, 188 | D0948B3E17815E9700BA8F23 /* UIKit.framework */, 189 | ); 190 | name = Frameworks; 191 | sourceTree = ""; 192 | }; 193 | D0948A44178159AD00BA8F23 /* ReactiveViewModel */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | D0948A4B178159AD00BA8F23 /* ReactiveViewModel.h */, 197 | D0948B4A178160FC00BA8F23 /* View Model */, 198 | D0948A45178159AD00BA8F23 /* Supporting Files */, 199 | ); 200 | path = ReactiveViewModel; 201 | sourceTree = ""; 202 | }; 203 | D0948A45178159AD00BA8F23 /* Supporting Files */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | D0948A46178159AD00BA8F23 /* Info.plist */, 207 | ); 208 | name = "Supporting Files"; 209 | sourceTree = ""; 210 | }; 211 | D0948A5A178159AD00BA8F23 /* ReactiveViewModelTests */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | D0948ABD17815B2000BA8F23 /* Specs */, 215 | D0948A5B178159AD00BA8F23 /* Supporting Files */, 216 | ); 217 | path = ReactiveViewModelTests; 218 | sourceTree = ""; 219 | }; 220 | D0948A5B178159AD00BA8F23 /* Supporting Files */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | D0948A5C178159AD00BA8F23 /* Info.plist */, 224 | D0948B5C178161A800BA8F23 /* RVMTestViewModel.h */, 225 | D0948B5D178161A800BA8F23 /* RVMTestViewModel.m */, 226 | D0731B7119FEFE2A00C01E16 /* SwiftSpec.swift */, 227 | ); 228 | name = "Supporting Files"; 229 | sourceTree = ""; 230 | }; 231 | D0948A73178159DA00BA8F23 /* Configuration */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | D0948A74178159DA00BA8F23 /* Base */, 235 | D0948A7E178159DA00BA8F23 /* iOS */, 236 | D0948A82178159DA00BA8F23 /* Mac OS X */, 237 | D0948A88178159DA00BA8F23 /* README.md */, 238 | ); 239 | name = Configuration; 240 | path = Carthage/Checkouts/xcconfigs; 241 | sourceTree = ""; 242 | }; 243 | D0948A74178159DA00BA8F23 /* Base */ = { 244 | isa = PBXGroup; 245 | children = ( 246 | D0948A75178159DA00BA8F23 /* Common.xcconfig */, 247 | D0948A76178159DA00BA8F23 /* Configurations */, 248 | D0948A7B178159DA00BA8F23 /* Targets */, 249 | ); 250 | path = Base; 251 | sourceTree = ""; 252 | }; 253 | D0948A76178159DA00BA8F23 /* Configurations */ = { 254 | isa = PBXGroup; 255 | children = ( 256 | D0948A77178159DA00BA8F23 /* Debug.xcconfig */, 257 | D0948A78178159DA00BA8F23 /* Profile.xcconfig */, 258 | D0948A79178159DA00BA8F23 /* Release.xcconfig */, 259 | D0948A7A178159DA00BA8F23 /* Test.xcconfig */, 260 | ); 261 | path = Configurations; 262 | sourceTree = ""; 263 | }; 264 | D0948A7B178159DA00BA8F23 /* Targets */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | D0948A7C178159DA00BA8F23 /* Application.xcconfig */, 268 | D0C7C39E19FEF8F60069B5C0 /* Framework.xcconfig */, 269 | D0948A7D178159DA00BA8F23 /* StaticLibrary.xcconfig */, 270 | ); 271 | path = Targets; 272 | sourceTree = ""; 273 | }; 274 | D0948A7E178159DA00BA8F23 /* iOS */ = { 275 | isa = PBXGroup; 276 | children = ( 277 | D0948A7F178159DA00BA8F23 /* iOS-Application.xcconfig */, 278 | D0948A80178159DA00BA8F23 /* iOS-Base.xcconfig */, 279 | D0C7C39F19FEF8FC0069B5C0 /* iOS-Framework.xcconfig */, 280 | D0948A81178159DA00BA8F23 /* iOS-StaticLibrary.xcconfig */, 281 | ); 282 | path = iOS; 283 | sourceTree = ""; 284 | }; 285 | D0948A82178159DA00BA8F23 /* Mac OS X */ = { 286 | isa = PBXGroup; 287 | children = ( 288 | D0948A83178159DA00BA8F23 /* Mac-Application.xcconfig */, 289 | D0948A84178159DA00BA8F23 /* Mac-Base.xcconfig */, 290 | D0948A85178159DA00BA8F23 /* Mac-DynamicLibrary.xcconfig */, 291 | D0948A86178159DA00BA8F23 /* Mac-Framework.xcconfig */, 292 | D0948A87178159DA00BA8F23 /* Mac-StaticLibrary.xcconfig */, 293 | ); 294 | path = "Mac OS X"; 295 | sourceTree = ""; 296 | }; 297 | D0948ABD17815B2000BA8F23 /* Specs */ = { 298 | isa = PBXGroup; 299 | children = ( 300 | D0948B591781618A00BA8F23 /* RVMViewModelSpec.m */, 301 | ); 302 | name = Specs; 303 | sourceTree = ""; 304 | }; 305 | D0948B4A178160FC00BA8F23 /* View Model */ = { 306 | isa = PBXGroup; 307 | children = ( 308 | D0948B531781610600BA8F23 /* RVMViewModel.h */, 309 | D0948B541781610600BA8F23 /* RVMViewModel.m */, 310 | ); 311 | name = "View Model"; 312 | sourceTree = ""; 313 | }; 314 | /* End PBXGroup section */ 315 | 316 | /* Begin PBXHeadersBuildPhase section */ 317 | D0948A38178159AD00BA8F23 /* Headers */ = { 318 | isa = PBXHeadersBuildPhase; 319 | buildActionMask = 2147483647; 320 | files = ( 321 | D0948ABA17815AF100BA8F23 /* ReactiveViewModel.h in Headers */, 322 | D0948B551781610600BA8F23 /* RVMViewModel.h in Headers */, 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | }; 326 | D0C7C3A419FEFB520069B5C0 /* Headers */ = { 327 | isa = PBXHeadersBuildPhase; 328 | buildActionMask = 2147483647; 329 | files = ( 330 | D0C7C3C619FEFC1F0069B5C0 /* ReactiveViewModel.h in Headers */, 331 | D0C7C3C419FEFC1C0069B5C0 /* RVMViewModel.h in Headers */, 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | }; 335 | /* End PBXHeadersBuildPhase section */ 336 | 337 | /* Begin PBXNativeTarget section */ 338 | D0948A3A178159AD00BA8F23 /* ReactiveViewModel Mac */ = { 339 | isa = PBXNativeTarget; 340 | buildConfigurationList = D0948A65178159AD00BA8F23 /* Build configuration list for PBXNativeTarget "ReactiveViewModel Mac" */; 341 | buildPhases = ( 342 | D0948A36178159AD00BA8F23 /* Sources */, 343 | D0948A37178159AD00BA8F23 /* Frameworks */, 344 | D0948A38178159AD00BA8F23 /* Headers */, 345 | D0948A39178159AD00BA8F23 /* Resources */, 346 | ); 347 | buildRules = ( 348 | ); 349 | dependencies = ( 350 | ); 351 | name = "ReactiveViewModel Mac"; 352 | productName = ReactiveViewModel; 353 | productReference = D0948A3B178159AD00BA8F23 /* ReactiveViewModel.framework */; 354 | productType = "com.apple.product-type.framework"; 355 | }; 356 | D0948A52178159AD00BA8F23 /* ReactiveViewModel Mac Tests */ = { 357 | isa = PBXNativeTarget; 358 | buildConfigurationList = D0948A68178159AD00BA8F23 /* Build configuration list for PBXNativeTarget "ReactiveViewModel Mac Tests" */; 359 | buildPhases = ( 360 | D0948A4E178159AD00BA8F23 /* Sources */, 361 | D0948A4F178159AD00BA8F23 /* Frameworks */, 362 | D0948A50178159AD00BA8F23 /* Resources */, 363 | ); 364 | buildRules = ( 365 | ); 366 | dependencies = ( 367 | D0948A58178159AD00BA8F23 /* PBXTargetDependency */, 368 | ); 369 | name = "ReactiveViewModel Mac Tests"; 370 | productName = ReactiveViewModelTests; 371 | productReference = D0948A53178159AD00BA8F23 /* ReactiveViewModel Mac Tests.xctest */; 372 | productType = "com.apple.product-type.bundle.unit-test"; 373 | }; 374 | D0C7C3A619FEFB520069B5C0 /* ReactiveViewModel iOS */ = { 375 | isa = PBXNativeTarget; 376 | buildConfigurationList = D0C7C3BA19FEFB520069B5C0 /* Build configuration list for PBXNativeTarget "ReactiveViewModel iOS" */; 377 | buildPhases = ( 378 | D0C7C3A219FEFB520069B5C0 /* Sources */, 379 | D0C7C3A319FEFB520069B5C0 /* Frameworks */, 380 | D0C7C3A419FEFB520069B5C0 /* Headers */, 381 | D0C7C3A519FEFB520069B5C0 /* Resources */, 382 | ); 383 | buildRules = ( 384 | ); 385 | dependencies = ( 386 | ); 387 | name = "ReactiveViewModel iOS"; 388 | productName = ReactiveViewModel; 389 | productReference = D0C7C3A719FEFB520069B5C0 /* ReactiveViewModel.framework */; 390 | productType = "com.apple.product-type.framework"; 391 | }; 392 | D0C7C3B019FEFB520069B5C0 /* ReactiveViewModel iOS Tests */ = { 393 | isa = PBXNativeTarget; 394 | buildConfigurationList = D0C7C3BF19FEFB520069B5C0 /* Build configuration list for PBXNativeTarget "ReactiveViewModel iOS Tests" */; 395 | buildPhases = ( 396 | D0C7C3AD19FEFB520069B5C0 /* Sources */, 397 | D0C7C3AE19FEFB520069B5C0 /* Frameworks */, 398 | D0C7C3AF19FEFB520069B5C0 /* Resources */, 399 | D0C7C3D519FEFD120069B5C0 /* Copy Frameworks */, 400 | ); 401 | buildRules = ( 402 | ); 403 | dependencies = ( 404 | D0C7C3B419FEFB520069B5C0 /* PBXTargetDependency */, 405 | ); 406 | name = "ReactiveViewModel iOS Tests"; 407 | productName = ReactiveViewModelTests; 408 | productReference = D0C7C3B119FEFB520069B5C0 /* ReactiveViewModel iOS Tests.xctest */; 409 | productType = "com.apple.product-type.bundle.unit-test"; 410 | }; 411 | /* End PBXNativeTarget section */ 412 | 413 | /* Begin PBXProject section */ 414 | D0948A32178159AD00BA8F23 /* Project object */ = { 415 | isa = PBXProject; 416 | attributes = { 417 | LastTestingUpgradeCheck = 0510; 418 | LastUpgradeCheck = 0830; 419 | ORGANIZATIONNAME = GitHub; 420 | TargetAttributes = { 421 | D0948A52178159AD00BA8F23 = { 422 | LastSwiftMigration = 0830; 423 | }; 424 | D0C7C3A619FEFB520069B5C0 = { 425 | CreatedOnToolsVersion = 6.1; 426 | }; 427 | D0C7C3B019FEFB520069B5C0 = { 428 | CreatedOnToolsVersion = 6.1; 429 | LastSwiftMigration = 0830; 430 | }; 431 | }; 432 | }; 433 | buildConfigurationList = D0948A35178159AD00BA8F23 /* Build configuration list for PBXProject "ReactiveViewModel" */; 434 | compatibilityVersion = "Xcode 3.2"; 435 | developmentRegion = English; 436 | hasScannedForEncodings = 0; 437 | knownRegions = ( 438 | en, 439 | ); 440 | mainGroup = D0948A31178159AD00BA8F23; 441 | productRefGroup = D0948A3C178159AD00BA8F23 /* Products */; 442 | projectDirPath = ""; 443 | projectRoot = ""; 444 | targets = ( 445 | D0948A3A178159AD00BA8F23 /* ReactiveViewModel Mac */, 446 | D0948A52178159AD00BA8F23 /* ReactiveViewModel Mac Tests */, 447 | D0C7C3A619FEFB520069B5C0 /* ReactiveViewModel iOS */, 448 | D0C7C3B019FEFB520069B5C0 /* ReactiveViewModel iOS Tests */, 449 | ); 450 | }; 451 | /* End PBXProject section */ 452 | 453 | /* Begin PBXResourcesBuildPhase section */ 454 | D0948A39178159AD00BA8F23 /* Resources */ = { 455 | isa = PBXResourcesBuildPhase; 456 | buildActionMask = 2147483647; 457 | files = ( 458 | ); 459 | runOnlyForDeploymentPostprocessing = 0; 460 | }; 461 | D0948A50178159AD00BA8F23 /* Resources */ = { 462 | isa = PBXResourcesBuildPhase; 463 | buildActionMask = 2147483647; 464 | files = ( 465 | ); 466 | runOnlyForDeploymentPostprocessing = 0; 467 | }; 468 | D0C7C3A519FEFB520069B5C0 /* Resources */ = { 469 | isa = PBXResourcesBuildPhase; 470 | buildActionMask = 2147483647; 471 | files = ( 472 | ); 473 | runOnlyForDeploymentPostprocessing = 0; 474 | }; 475 | D0C7C3AF19FEFB520069B5C0 /* Resources */ = { 476 | isa = PBXResourcesBuildPhase; 477 | buildActionMask = 2147483647; 478 | files = ( 479 | ); 480 | runOnlyForDeploymentPostprocessing = 0; 481 | }; 482 | /* End PBXResourcesBuildPhase section */ 483 | 484 | /* Begin PBXSourcesBuildPhase section */ 485 | D0948A36178159AD00BA8F23 /* Sources */ = { 486 | isa = PBXSourcesBuildPhase; 487 | buildActionMask = 2147483647; 488 | files = ( 489 | D0948B571781610600BA8F23 /* RVMViewModel.m in Sources */, 490 | ); 491 | runOnlyForDeploymentPostprocessing = 0; 492 | }; 493 | D0948A4E178159AD00BA8F23 /* Sources */ = { 494 | isa = PBXSourcesBuildPhase; 495 | buildActionMask = 2147483647; 496 | files = ( 497 | D0731B7219FEFE2A00C01E16 /* SwiftSpec.swift in Sources */, 498 | D0948B5A1781618A00BA8F23 /* RVMViewModelSpec.m in Sources */, 499 | D0948B5E178161A800BA8F23 /* RVMTestViewModel.m in Sources */, 500 | ); 501 | runOnlyForDeploymentPostprocessing = 0; 502 | }; 503 | D0C7C3A219FEFB520069B5C0 /* Sources */ = { 504 | isa = PBXSourcesBuildPhase; 505 | buildActionMask = 2147483647; 506 | files = ( 507 | D0C7C3C519FEFC1C0069B5C0 /* RVMViewModel.m in Sources */, 508 | ); 509 | runOnlyForDeploymentPostprocessing = 0; 510 | }; 511 | D0C7C3AD19FEFB520069B5C0 /* Sources */ = { 512 | isa = PBXSourcesBuildPhase; 513 | buildActionMask = 2147483647; 514 | files = ( 515 | D0731B7319FEFE2A00C01E16 /* SwiftSpec.swift in Sources */, 516 | D0C7C3CB19FEFCBE0069B5C0 /* RVMTestViewModel.m in Sources */, 517 | D0C7C3CA19FEFCBE0069B5C0 /* RVMViewModelSpec.m in Sources */, 518 | ); 519 | runOnlyForDeploymentPostprocessing = 0; 520 | }; 521 | /* End PBXSourcesBuildPhase section */ 522 | 523 | /* Begin PBXTargetDependency section */ 524 | D0948A58178159AD00BA8F23 /* PBXTargetDependency */ = { 525 | isa = PBXTargetDependency; 526 | target = D0948A3A178159AD00BA8F23 /* ReactiveViewModel Mac */; 527 | targetProxy = D0948A57178159AD00BA8F23 /* PBXContainerItemProxy */; 528 | }; 529 | D0C7C3B419FEFB520069B5C0 /* PBXTargetDependency */ = { 530 | isa = PBXTargetDependency; 531 | target = D0C7C3A619FEFB520069B5C0 /* ReactiveViewModel iOS */; 532 | targetProxy = D0C7C3B319FEFB520069B5C0 /* PBXContainerItemProxy */; 533 | }; 534 | /* End PBXTargetDependency section */ 535 | 536 | /* Begin XCBuildConfiguration section */ 537 | D0948A63178159AD00BA8F23 /* Debug */ = { 538 | isa = XCBuildConfiguration; 539 | baseConfigurationReference = D0948A77178159DA00BA8F23 /* Debug.xcconfig */; 540 | buildSettings = { 541 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 542 | MACOSX_DEPLOYMENT_TARGET = 10.8; 543 | ONLY_ACTIVE_ARCH = YES; 544 | TARGETED_DEVICE_FAMILY = "1,2"; 545 | }; 546 | name = Debug; 547 | }; 548 | D0948A64178159AD00BA8F23 /* Release */ = { 549 | isa = XCBuildConfiguration; 550 | baseConfigurationReference = D0948A79178159DA00BA8F23 /* Release.xcconfig */; 551 | buildSettings = { 552 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 553 | MACOSX_DEPLOYMENT_TARGET = 10.8; 554 | TARGETED_DEVICE_FAMILY = "1,2"; 555 | }; 556 | name = Release; 557 | }; 558 | D0948A66178159AD00BA8F23 /* Debug */ = { 559 | isa = XCBuildConfiguration; 560 | baseConfigurationReference = D0948A86178159DA00BA8F23 /* Mac-Framework.xcconfig */; 561 | buildSettings = { 562 | DYLIB_COMPATIBILITY_VERSION = 1; 563 | DYLIB_CURRENT_VERSION = 1; 564 | FRAMEWORK_VERSION = A; 565 | INFOPLIST_FILE = ReactiveViewModel/Info.plist; 566 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 567 | WRAPPER_EXTENSION = framework; 568 | }; 569 | name = Debug; 570 | }; 571 | D0948A67178159AD00BA8F23 /* Release */ = { 572 | isa = XCBuildConfiguration; 573 | baseConfigurationReference = D0948A86178159DA00BA8F23 /* Mac-Framework.xcconfig */; 574 | buildSettings = { 575 | DYLIB_COMPATIBILITY_VERSION = 1; 576 | DYLIB_CURRENT_VERSION = 1; 577 | FRAMEWORK_VERSION = A; 578 | INFOPLIST_FILE = ReactiveViewModel/Info.plist; 579 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 580 | WRAPPER_EXTENSION = framework; 581 | }; 582 | name = Release; 583 | }; 584 | D0948A69178159AD00BA8F23 /* Debug */ = { 585 | isa = XCBuildConfiguration; 586 | baseConfigurationReference = D0948A83178159DA00BA8F23 /* Mac-Application.xcconfig */; 587 | buildSettings = { 588 | CLANG_ENABLE_MODULES = YES; 589 | INFOPLIST_FILE = ReactiveViewModelTests/Info.plist; 590 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 591 | MACOSX_DEPLOYMENT_TARGET = 10.9; 592 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 593 | PRODUCT_NAME = "$(TARGET_NAME)"; 594 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 595 | SWIFT_VERSION = 3.0; 596 | }; 597 | name = Debug; 598 | }; 599 | D0948A6A178159AD00BA8F23 /* Release */ = { 600 | isa = XCBuildConfiguration; 601 | baseConfigurationReference = D0948A83178159DA00BA8F23 /* Mac-Application.xcconfig */; 602 | buildSettings = { 603 | CLANG_ENABLE_MODULES = YES; 604 | INFOPLIST_FILE = ReactiveViewModelTests/Info.plist; 605 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 606 | MACOSX_DEPLOYMENT_TARGET = 10.9; 607 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 608 | PRODUCT_NAME = "$(TARGET_NAME)"; 609 | SWIFT_VERSION = 3.0; 610 | }; 611 | name = Release; 612 | }; 613 | D0948AFA17815BAC00BA8F23 /* Profile */ = { 614 | isa = XCBuildConfiguration; 615 | baseConfigurationReference = D0948A78178159DA00BA8F23 /* Profile.xcconfig */; 616 | buildSettings = { 617 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 618 | MACOSX_DEPLOYMENT_TARGET = 10.8; 619 | TARGETED_DEVICE_FAMILY = "1,2"; 620 | }; 621 | name = Profile; 622 | }; 623 | D0948AFB17815BAC00BA8F23 /* Profile */ = { 624 | isa = XCBuildConfiguration; 625 | baseConfigurationReference = D0948A86178159DA00BA8F23 /* Mac-Framework.xcconfig */; 626 | buildSettings = { 627 | DYLIB_COMPATIBILITY_VERSION = 1; 628 | DYLIB_CURRENT_VERSION = 1; 629 | FRAMEWORK_VERSION = A; 630 | INFOPLIST_FILE = ReactiveViewModel/Info.plist; 631 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 632 | WRAPPER_EXTENSION = framework; 633 | }; 634 | name = Profile; 635 | }; 636 | D0948AFC17815BAC00BA8F23 /* Profile */ = { 637 | isa = XCBuildConfiguration; 638 | baseConfigurationReference = D0948A83178159DA00BA8F23 /* Mac-Application.xcconfig */; 639 | buildSettings = { 640 | CLANG_ENABLE_MODULES = YES; 641 | INFOPLIST_FILE = ReactiveViewModelTests/Info.plist; 642 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 643 | MACOSX_DEPLOYMENT_TARGET = 10.9; 644 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 645 | PRODUCT_NAME = "$(TARGET_NAME)"; 646 | SWIFT_VERSION = 3.0; 647 | }; 648 | name = Profile; 649 | }; 650 | D0948AFF17815BB500BA8F23 /* Test */ = { 651 | isa = XCBuildConfiguration; 652 | baseConfigurationReference = D0948A7A178159DA00BA8F23 /* Test.xcconfig */; 653 | buildSettings = { 654 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 655 | MACOSX_DEPLOYMENT_TARGET = 10.8; 656 | TARGETED_DEVICE_FAMILY = "1,2"; 657 | }; 658 | name = Test; 659 | }; 660 | D0948B0017815BB500BA8F23 /* Test */ = { 661 | isa = XCBuildConfiguration; 662 | baseConfigurationReference = D0948A86178159DA00BA8F23 /* Mac-Framework.xcconfig */; 663 | buildSettings = { 664 | DYLIB_COMPATIBILITY_VERSION = 1; 665 | DYLIB_CURRENT_VERSION = 1; 666 | FRAMEWORK_VERSION = A; 667 | INFOPLIST_FILE = ReactiveViewModel/Info.plist; 668 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 669 | WRAPPER_EXTENSION = framework; 670 | }; 671 | name = Test; 672 | }; 673 | D0948B0117815BB500BA8F23 /* Test */ = { 674 | isa = XCBuildConfiguration; 675 | baseConfigurationReference = D0948A83178159DA00BA8F23 /* Mac-Application.xcconfig */; 676 | buildSettings = { 677 | CLANG_ENABLE_MODULES = YES; 678 | INFOPLIST_FILE = ReactiveViewModelTests/Info.plist; 679 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 680 | MACOSX_DEPLOYMENT_TARGET = 10.9; 681 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 682 | PRODUCT_NAME = "$(TARGET_NAME)"; 683 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 684 | SWIFT_VERSION = 3.0; 685 | }; 686 | name = Test; 687 | }; 688 | D0C7C3BB19FEFB520069B5C0 /* Debug */ = { 689 | isa = XCBuildConfiguration; 690 | baseConfigurationReference = D0C7C39F19FEF8FC0069B5C0 /* iOS-Framework.xcconfig */; 691 | buildSettings = { 692 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 693 | CURRENT_PROJECT_VERSION = 1; 694 | DYLIB_COMPATIBILITY_VERSION = 1; 695 | DYLIB_CURRENT_VERSION = 1; 696 | INFOPLIST_FILE = ReactiveViewModel/Info.plist; 697 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 698 | VERSIONING_SYSTEM = "apple-generic"; 699 | VERSION_INFO_PREFIX = ""; 700 | }; 701 | name = Debug; 702 | }; 703 | D0C7C3BC19FEFB520069B5C0 /* Release */ = { 704 | isa = XCBuildConfiguration; 705 | baseConfigurationReference = D0C7C39F19FEF8FC0069B5C0 /* iOS-Framework.xcconfig */; 706 | buildSettings = { 707 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 708 | CURRENT_PROJECT_VERSION = 1; 709 | DYLIB_COMPATIBILITY_VERSION = 1; 710 | DYLIB_CURRENT_VERSION = 1; 711 | INFOPLIST_FILE = ReactiveViewModel/Info.plist; 712 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 713 | VERSIONING_SYSTEM = "apple-generic"; 714 | VERSION_INFO_PREFIX = ""; 715 | }; 716 | name = Release; 717 | }; 718 | D0C7C3BD19FEFB520069B5C0 /* Test */ = { 719 | isa = XCBuildConfiguration; 720 | baseConfigurationReference = D0C7C39F19FEF8FC0069B5C0 /* iOS-Framework.xcconfig */; 721 | buildSettings = { 722 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 723 | CURRENT_PROJECT_VERSION = 1; 724 | DYLIB_COMPATIBILITY_VERSION = 1; 725 | DYLIB_CURRENT_VERSION = 1; 726 | INFOPLIST_FILE = ReactiveViewModel/Info.plist; 727 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 728 | VERSIONING_SYSTEM = "apple-generic"; 729 | VERSION_INFO_PREFIX = ""; 730 | }; 731 | name = Test; 732 | }; 733 | D0C7C3BE19FEFB520069B5C0 /* Profile */ = { 734 | isa = XCBuildConfiguration; 735 | baseConfigurationReference = D0C7C39F19FEF8FC0069B5C0 /* iOS-Framework.xcconfig */; 736 | buildSettings = { 737 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 738 | CURRENT_PROJECT_VERSION = 1; 739 | DYLIB_COMPATIBILITY_VERSION = 1; 740 | DYLIB_CURRENT_VERSION = 1; 741 | INFOPLIST_FILE = ReactiveViewModel/Info.plist; 742 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 743 | VERSIONING_SYSTEM = "apple-generic"; 744 | VERSION_INFO_PREFIX = ""; 745 | }; 746 | name = Profile; 747 | }; 748 | D0C7C3C019FEFB520069B5C0 /* Debug */ = { 749 | isa = XCBuildConfiguration; 750 | baseConfigurationReference = D0948A7F178159DA00BA8F23 /* iOS-Application.xcconfig */; 751 | buildSettings = { 752 | CLANG_ENABLE_MODULES = YES; 753 | INFOPLIST_FILE = ReactiveViewModelTests/Info.plist; 754 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 755 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 756 | PRODUCT_NAME = "$(TARGET_NAME)"; 757 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 758 | SWIFT_VERSION = 3.0; 759 | }; 760 | name = Debug; 761 | }; 762 | D0C7C3C119FEFB520069B5C0 /* Release */ = { 763 | isa = XCBuildConfiguration; 764 | baseConfigurationReference = D0948A7F178159DA00BA8F23 /* iOS-Application.xcconfig */; 765 | buildSettings = { 766 | CLANG_ENABLE_MODULES = YES; 767 | INFOPLIST_FILE = ReactiveViewModelTests/Info.plist; 768 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 769 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 770 | PRODUCT_NAME = "$(TARGET_NAME)"; 771 | SWIFT_VERSION = 3.0; 772 | }; 773 | name = Release; 774 | }; 775 | D0C7C3C219FEFB520069B5C0 /* Test */ = { 776 | isa = XCBuildConfiguration; 777 | baseConfigurationReference = D0948A7F178159DA00BA8F23 /* iOS-Application.xcconfig */; 778 | buildSettings = { 779 | CLANG_ENABLE_MODULES = YES; 780 | INFOPLIST_FILE = ReactiveViewModelTests/Info.plist; 781 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 782 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 783 | PRODUCT_NAME = "$(TARGET_NAME)"; 784 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 785 | SWIFT_VERSION = 3.0; 786 | }; 787 | name = Test; 788 | }; 789 | D0C7C3C319FEFB520069B5C0 /* Profile */ = { 790 | isa = XCBuildConfiguration; 791 | baseConfigurationReference = D0948A7F178159DA00BA8F23 /* iOS-Application.xcconfig */; 792 | buildSettings = { 793 | CLANG_ENABLE_MODULES = YES; 794 | INFOPLIST_FILE = ReactiveViewModelTests/Info.plist; 795 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 796 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; 797 | PRODUCT_NAME = "$(TARGET_NAME)"; 798 | SWIFT_VERSION = 3.0; 799 | }; 800 | name = Profile; 801 | }; 802 | /* End XCBuildConfiguration section */ 803 | 804 | /* Begin XCConfigurationList section */ 805 | D0948A35178159AD00BA8F23 /* Build configuration list for PBXProject "ReactiveViewModel" */ = { 806 | isa = XCConfigurationList; 807 | buildConfigurations = ( 808 | D0948A63178159AD00BA8F23 /* Debug */, 809 | D0948A64178159AD00BA8F23 /* Release */, 810 | D0948AFF17815BB500BA8F23 /* Test */, 811 | D0948AFA17815BAC00BA8F23 /* Profile */, 812 | ); 813 | defaultConfigurationIsVisible = 0; 814 | defaultConfigurationName = Release; 815 | }; 816 | D0948A65178159AD00BA8F23 /* Build configuration list for PBXNativeTarget "ReactiveViewModel Mac" */ = { 817 | isa = XCConfigurationList; 818 | buildConfigurations = ( 819 | D0948A66178159AD00BA8F23 /* Debug */, 820 | D0948A67178159AD00BA8F23 /* Release */, 821 | D0948B0017815BB500BA8F23 /* Test */, 822 | D0948AFB17815BAC00BA8F23 /* Profile */, 823 | ); 824 | defaultConfigurationIsVisible = 0; 825 | defaultConfigurationName = Release; 826 | }; 827 | D0948A68178159AD00BA8F23 /* Build configuration list for PBXNativeTarget "ReactiveViewModel Mac Tests" */ = { 828 | isa = XCConfigurationList; 829 | buildConfigurations = ( 830 | D0948A69178159AD00BA8F23 /* Debug */, 831 | D0948A6A178159AD00BA8F23 /* Release */, 832 | D0948B0117815BB500BA8F23 /* Test */, 833 | D0948AFC17815BAC00BA8F23 /* Profile */, 834 | ); 835 | defaultConfigurationIsVisible = 0; 836 | defaultConfigurationName = Release; 837 | }; 838 | D0C7C3BA19FEFB520069B5C0 /* Build configuration list for PBXNativeTarget "ReactiveViewModel iOS" */ = { 839 | isa = XCConfigurationList; 840 | buildConfigurations = ( 841 | D0C7C3BB19FEFB520069B5C0 /* Debug */, 842 | D0C7C3BC19FEFB520069B5C0 /* Release */, 843 | D0C7C3BD19FEFB520069B5C0 /* Test */, 844 | D0C7C3BE19FEFB520069B5C0 /* Profile */, 845 | ); 846 | defaultConfigurationIsVisible = 0; 847 | defaultConfigurationName = Release; 848 | }; 849 | D0C7C3BF19FEFB520069B5C0 /* Build configuration list for PBXNativeTarget "ReactiveViewModel iOS Tests" */ = { 850 | isa = XCConfigurationList; 851 | buildConfigurations = ( 852 | D0C7C3C019FEFB520069B5C0 /* Debug */, 853 | D0C7C3C119FEFB520069B5C0 /* Release */, 854 | D0C7C3C219FEFB520069B5C0 /* Test */, 855 | D0C7C3C319FEFB520069B5C0 /* Profile */, 856 | ); 857 | defaultConfigurationIsVisible = 0; 858 | defaultConfigurationName = Release; 859 | }; 860 | /* End XCConfigurationList section */ 861 | }; 862 | rootObject = D0948A32178159AD00BA8F23 /* Project object */; 863 | } 864 | -------------------------------------------------------------------------------- /ReactiveViewModel.xcodeproj/xcshareddata/xcschemes/ReactiveViewModel Mac.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 79 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /ReactiveViewModel.xcodeproj/xcshareddata/xcschemes/ReactiveViewModel iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /ReactiveViewModel.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ReactiveViewModel/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 | -------------------------------------------------------------------------------- /ReactiveViewModel/RVMViewModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // RVMViewModel.h 3 | // ReactiveViewModel 4 | // 5 | // Created by Josh Abernathy on 9/11/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RACSignal; 12 | 13 | // Implements behaviors that drive the UI, and/or adapts a domain model to be 14 | // user-presentable. 15 | @interface RVMViewModel : NSObject 16 | 17 | // Whether the view model is currently "active." 18 | // 19 | // This generally implies that the associated view is visible. When set to NO, 20 | // the view model should throttle or cancel low-priority or UI-related work. 21 | // 22 | // This property defaults to NO. 23 | @property (nonatomic, assign, getter = isActive) BOOL active; 24 | 25 | // Observes the receiver's `active` property, and sends the receiver whenever it 26 | // changes from NO to YES. 27 | // 28 | // If the receiver is currently active, this signal will send once immediately 29 | // upon subscription. 30 | @property (nonatomic, strong, readonly) RACSignal *didBecomeActiveSignal; 31 | 32 | // Observes the receiver's `active` property, and sends the receiver whenever it 33 | // changes from YES to NO. 34 | // 35 | // If the receiver is currently inactive, this signal will send once immediately 36 | // upon subscription. 37 | @property (nonatomic, strong, readonly) RACSignal *didBecomeInactiveSignal; 38 | 39 | // Subscribes (or resubscribes) to the given signal whenever 40 | // `didBecomeActiveSignal` fires. 41 | // 42 | // When `didBecomeInactiveSignal` fires, any active subscription to `signal` is 43 | // disposed. 44 | // 45 | // Returns a signal which forwards `next`s from the latest subscription to 46 | // `signal`, and completes when the receiver is deallocated. If `signal` sends 47 | // an error at any point, the returned signal will error out as well. 48 | - (RACSignal *)forwardSignalWhileActive:(RACSignal *)signal; 49 | 50 | // Throttles events on the given signal while the receiver is inactive. 51 | // 52 | // Unlike -forwardSignalWhileActive:, this method will stay subscribed to 53 | // `signal` the entire time, except that its events will be throttled when the 54 | // receiver becomes inactive. 55 | // 56 | // Returns a signal which forwards events from `signal` (throttled while the 57 | // receiver is inactive), and completes when `signal` completes or the receiver 58 | // is deallocated. 59 | - (RACSignal *)throttleSignalWhileInactive:(RACSignal *)signal; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /ReactiveViewModel/RVMViewModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // RVMViewModel.m 3 | // ReactiveViewModel 4 | // 5 | // Created by Josh Abernathy on 9/11/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "RVMViewModel.h" 10 | #import 11 | #import 12 | 13 | // The number of seconds by which signal events are throttled when using 14 | // -throttleSignalWhileInactive:. 15 | static const NSTimeInterval RVMViewModelInactiveThrottleInterval = 1; 16 | 17 | @interface RVMViewModel () 18 | 19 | // Improves the performance of KVO on the receiver. 20 | // 21 | // See the documentation for for more information. 22 | @property (atomic) void *observationInfo; 23 | 24 | @end 25 | 26 | @implementation RVMViewModel 27 | 28 | #pragma mark Properties 29 | 30 | // We create many, many view models, so these properties need to be as lazy and 31 | // memory-conscious as possible. 32 | @synthesize didBecomeActiveSignal = _didBecomeActiveSignal; 33 | @synthesize didBecomeInactiveSignal = _didBecomeInactiveSignal; 34 | 35 | - (void)setActive:(BOOL)active { 36 | // Skip KVO notifications when the property hasn't actually changed. This is 37 | // especially important because self.active can have very expensive 38 | // observers attached. 39 | if (active == _active) return; 40 | 41 | [self willChangeValueForKey:@keypath(self.active)]; 42 | _active = active; 43 | [self didChangeValueForKey:@keypath(self.active)]; 44 | } 45 | 46 | - (RACSignal *)didBecomeActiveSignal { 47 | if (_didBecomeActiveSignal == nil) { 48 | @weakify(self); 49 | 50 | _didBecomeActiveSignal = [[[RACObserve(self, active) 51 | filter:^(NSNumber *active) { 52 | return active.boolValue; 53 | }] 54 | map:^(id _) { 55 | @strongify(self); 56 | return self; 57 | }] 58 | setNameWithFormat:@"%@ -didBecomeActiveSignal", self]; 59 | } 60 | 61 | return _didBecomeActiveSignal; 62 | } 63 | 64 | - (RACSignal *)didBecomeInactiveSignal { 65 | if (_didBecomeInactiveSignal == nil) { 66 | @weakify(self); 67 | 68 | _didBecomeInactiveSignal = [[[RACObserve(self, active) 69 | filter:^ BOOL (NSNumber *active) { 70 | return !active.boolValue; 71 | }] 72 | map:^(id _) { 73 | @strongify(self); 74 | return self; 75 | }] 76 | setNameWithFormat:@"%@ -didBecomeInactiveSignal", self]; 77 | } 78 | 79 | return _didBecomeInactiveSignal; 80 | } 81 | 82 | #pragma mark Activation 83 | 84 | - (RACSignal *)forwardSignalWhileActive:(RACSignal *)signal { 85 | NSParameterAssert(signal != nil); 86 | 87 | RACSignal *activeSignal = RACObserve(self, active); 88 | 89 | return [[RACSignal 90 | createSignal:^(id subscriber) { 91 | RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; 92 | 93 | __block RACDisposable *signalDisposable = nil; 94 | 95 | RACDisposable *activeDisposable = [activeSignal subscribeNext:^(NSNumber *active) { 96 | if (active.boolValue) { 97 | signalDisposable = [signal subscribeNext:^(id value) { 98 | [subscriber sendNext:value]; 99 | } error:^(NSError *error) { 100 | [subscriber sendError:error]; 101 | }]; 102 | 103 | if (signalDisposable != nil) [disposable addDisposable:signalDisposable]; 104 | } else { 105 | [signalDisposable dispose]; 106 | [disposable removeDisposable:signalDisposable]; 107 | signalDisposable = nil; 108 | } 109 | } error:^(NSError *error) { 110 | [subscriber sendError:error]; 111 | } completed:^{ 112 | [subscriber sendCompleted]; 113 | }]; 114 | 115 | if (activeDisposable != nil) [disposable addDisposable:activeDisposable]; 116 | return disposable; 117 | }] 118 | setNameWithFormat:@"%@ -forwardSignalWhileActive: %@", self, signal]; 119 | } 120 | 121 | - (RACSignal *)throttleSignalWhileInactive:(RACSignal *)signal { 122 | NSParameterAssert(signal != nil); 123 | 124 | signal = [signal replayLast]; 125 | 126 | return [[[[[RACObserve(self, active) 127 | takeUntil:[signal ignoreValues]] 128 | combineLatestWith:signal] 129 | throttle:RVMViewModelInactiveThrottleInterval valuesPassingTest:^ BOOL (RACTuple *xs) { 130 | BOOL active = [xs.first boolValue]; 131 | return !active; 132 | }] 133 | reduceEach:^(NSNumber *active, id value) { 134 | return value; 135 | }] 136 | setNameWithFormat:@"%@ -throttleSignalWhileInactive: %@", self, signal]; 137 | } 138 | 139 | #pragma mark NSKeyValueObserving 140 | 141 | + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { 142 | // We'll generate notifications for this property manually. 143 | if ([key isEqual:@keypath(RVMViewModel.new, active)]) { 144 | return NO; 145 | } 146 | 147 | return [super automaticallyNotifiesObserversForKey:key]; 148 | } 149 | 150 | @end 151 | -------------------------------------------------------------------------------- /ReactiveViewModel/ReactiveViewModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // ReactiveViewModel.h 3 | // ReactiveViewModel 4 | // 5 | // Created by Justin Spahr-Summers on 2014-10-27. 6 | // Copyright (c) 2014 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ReactiveViewModel. 12 | FOUNDATION_EXPORT double ReactiveViewModelVersionNumber; 13 | 14 | //! Project version string for ReactiveViewModel. 15 | FOUNDATION_EXPORT const unsigned char ReactiveViewModelVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import -------------------------------------------------------------------------------- /ReactiveViewModelTests/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 | -------------------------------------------------------------------------------- /ReactiveViewModelTests/RVMTestViewModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // RVMTestViewModel.h 3 | // ReactiveViewModel 4 | // 5 | // Created by Josh Abernathy on 9/12/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RVMTestViewModel : RVMViewModel 12 | @end 13 | -------------------------------------------------------------------------------- /ReactiveViewModelTests/RVMTestViewModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // RVMTestViewModel.m 3 | // ReactiveViewModel 4 | // 5 | // Created by Josh Abernathy on 9/12/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | #import "RVMTestViewModel.h" 10 | 11 | @implementation RVMTestViewModel 12 | @end 13 | -------------------------------------------------------------------------------- /ReactiveViewModelTests/RVMViewModelSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // RVMViewModelSpec.m 3 | // ReactiveViewModel 4 | // 5 | // Created by Josh Abernathy on 9/11/12. 6 | // Copyright (c) 2012 GitHub. All rights reserved. 7 | // 8 | 9 | @import Nimble; 10 | @import Quick; 11 | #import 12 | #import 13 | 14 | #import "RVMTestViewModel.h" 15 | 16 | QuickSpecBegin(RVMViewModelSpec) 17 | 18 | __block RVMTestViewModel *viewModel; 19 | 20 | beforeEach(^{ 21 | viewModel = [[RVMTestViewModel alloc] init]; 22 | }); 23 | 24 | describe(@"active property", ^{ 25 | it(@"should default to NO", ^{ 26 | expect(@(viewModel.active)).to(beFalsy()); 27 | }); 28 | 29 | it(@"should send on didBecomeActiveSignal when set to YES", ^{ 30 | __block NSUInteger nextEvents = 0; 31 | [viewModel.didBecomeActiveSignal subscribeNext:^(RVMViewModel *viewModel) { 32 | expect(viewModel).to(beIdenticalTo(viewModel)); 33 | expect(@(viewModel.active)).to(beTruthy()); 34 | 35 | nextEvents++; 36 | }]; 37 | 38 | expect(@(nextEvents)).to(equal(@0)); 39 | 40 | viewModel.active = YES; 41 | expect(@(nextEvents)).to(equal(@1)); 42 | 43 | // Indistinct changes should not trigger the signal again. 44 | viewModel.active = YES; 45 | expect(@(nextEvents)).to(equal(@1)); 46 | 47 | viewModel.active = NO; 48 | viewModel.active = YES; 49 | expect(@(nextEvents)).to(equal(@2)); 50 | }); 51 | 52 | it(@"should send on didBecomeInactiveSignal when set to NO", ^{ 53 | __block NSUInteger nextEvents = 0; 54 | [viewModel.didBecomeInactiveSignal subscribeNext:^(RVMViewModel *viewModel) { 55 | expect(viewModel).to(beIdenticalTo(viewModel)); 56 | expect(@(viewModel.active)).to(beFalsy()); 57 | 58 | nextEvents++; 59 | }]; 60 | 61 | expect(@(nextEvents)).to(equal(@1)); 62 | 63 | viewModel.active = YES; 64 | viewModel.active = NO; 65 | expect(@(nextEvents)).to(equal(@2)); 66 | 67 | // Indistinct changes should not trigger the signal again. 68 | viewModel.active = NO; 69 | expect(@(nextEvents)).to(equal(@2)); 70 | }); 71 | 72 | describe(@"signal manipulation", ^{ 73 | __block NSMutableArray *values; 74 | __block NSArray *expectedValues; 75 | __block BOOL completed; 76 | __block BOOL deallocated; 77 | 78 | __block RVMTestViewModel * (^createViewModel)(); 79 | 80 | beforeEach(^{ 81 | values = [NSMutableArray array]; 82 | expectedValues = @[]; 83 | completed = NO; 84 | deallocated = NO; 85 | 86 | createViewModel = ^{ 87 | RVMTestViewModel *viewModel = [[RVMTestViewModel alloc] init]; 88 | [viewModel.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ 89 | deallocated = YES; 90 | }]]; 91 | 92 | viewModel.active = YES; 93 | return viewModel; 94 | }; 95 | }); 96 | 97 | afterEach(^{ 98 | expect(@(deallocated)).toEventually(beTruthy()); 99 | expect(@(completed)).to(beTruthy()); 100 | }); 101 | 102 | it(@"should forward a signal", ^{ 103 | @autoreleasepool { 104 | RVMTestViewModel *viewModel __attribute__((objc_precise_lifetime)) = createViewModel(); 105 | 106 | RACSignal *input = [RACSignal createSignal:^ id (id subscriber) { 107 | [subscriber sendNext:@1]; 108 | [subscriber sendNext:@2]; 109 | return nil; 110 | }]; 111 | 112 | [[viewModel 113 | forwardSignalWhileActive:input] 114 | subscribeNext:^(NSNumber *x) { 115 | [values addObject:x]; 116 | } completed:^{ 117 | completed = YES; 118 | }]; 119 | 120 | expectedValues = @[ @1, @2 ]; 121 | expect(values).to(equal(expectedValues)); 122 | expect(@(completed)).to(beFalsy()); 123 | 124 | viewModel.active = NO; 125 | 126 | expect(values).to(equal(expectedValues)); 127 | expect(@(completed)).to(beFalsy()); 128 | 129 | viewModel.active = YES; 130 | 131 | expectedValues = @[ @1, @2, @1, @2 ]; 132 | expect(values).to(equal(expectedValues)); 133 | expect(@(completed)).to(beFalsy()); 134 | } 135 | }); 136 | 137 | it(@"should throttle a signal", ^{ 138 | @autoreleasepool { 139 | RVMTestViewModel *viewModel __attribute__((objc_precise_lifetime)) = createViewModel(); 140 | RACSubject *subject = [RACSubject subject]; 141 | 142 | [[viewModel 143 | throttleSignalWhileInactive:[subject startWith:@0]] 144 | subscribeNext:^(NSNumber *x) { 145 | [values addObject:x]; 146 | } completed:^{ 147 | completed = YES; 148 | }]; 149 | 150 | expectedValues = @[ @0 ]; 151 | expect(values).to(equal(expectedValues)); 152 | expect(@(completed)).to(beFalsy()); 153 | 154 | [subject sendNext:@1]; 155 | 156 | expectedValues = @[ @0, @1 ]; 157 | expect(values).to(equal(expectedValues)); 158 | expect(@(completed)).to(beFalsy()); 159 | 160 | viewModel.active = NO; 161 | 162 | // Since the VM is inactive, these events should be throttled. 163 | [subject sendNext:@2]; 164 | [subject sendNext:@3]; 165 | 166 | expect(values).to(equal(expectedValues)); 167 | expect(@(completed)).to(beFalsy()); 168 | 169 | expectedValues = @[ @0, @1, @3 ]; 170 | 171 | // FIXME: Nimble doesn't support custom timeouts right now, and 172 | // our operation may take longer than 1 second (the default 173 | // timeout), sooo... trololo 174 | [NSThread sleepForTimeInterval:1]; 175 | 176 | expect(values).toEventually(equal(expectedValues)); 177 | expect(@(completed)).to(beFalsy()); 178 | 179 | // After reactivating, we should still get this event. 180 | [subject sendNext:@4]; 181 | viewModel.active = YES; 182 | 183 | expectedValues = @[ @0, @1, @3, @4 ]; 184 | expect(values).toEventually(equal(expectedValues)); 185 | expect(@(completed)).to(beFalsy()); 186 | 187 | // And now new events should be instant. 188 | [subject sendNext:@5]; 189 | 190 | expectedValues = @[ @0, @1, @3, @4, @5 ]; 191 | expect(values).to(equal(expectedValues)); 192 | expect(@(completed)).to(beFalsy()); 193 | 194 | [subject sendCompleted]; 195 | 196 | expect(values).to(equal(expectedValues)); 197 | expect(@(completed)).to(beTruthy()); 198 | } 199 | }); 200 | }); 201 | }); 202 | 203 | QuickSpecEnd 204 | -------------------------------------------------------------------------------- /ReactiveViewModelTests/SwiftSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftSpec.swift 3 | // Archimedes 4 | // 5 | // Created by Justin Spahr-Summers on 2014-10-02. 6 | // Copyright (c) 2014 GitHub. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Nimble 11 | import Quick 12 | 13 | // Without this, the Swift stdlib won't be linked into the test target (even if 14 | // “Embedded Content Contains Swift Code” is enabled). 15 | class SwiftSpec: QuickSpec { 16 | override func spec() { 17 | expect(true).to(beTruthy()) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /script/LICENSE.md: -------------------------------------------------------------------------------- 1 | **Copyright (c) 2013 Justin Spahr-Summers** 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /script/README.md: -------------------------------------------------------------------------------- 1 | # objc-build-scripts 2 | 3 | This project is a collection of scripts created with two goals: 4 | 5 | 1. To standardize how Objective-C projects are bootstrapped after cloning 6 | 1. To easily build Objective-C projects on continuous integration servers 7 | 8 | ## Scripts 9 | 10 | Right now, there are two important scripts: [`bootstrap`](#bootstrap) and 11 | [`cibuild`](#cibuild). Both are Bash scripts, to maximize compatibility and 12 | eliminate pesky system configuration issues (like setting up a working Ruby 13 | environment). 14 | 15 | The structure of the scripts on disk is meant to follow that of a typical Ruby 16 | project: 17 | 18 | ``` 19 | script/ 20 | bootstrap 21 | cibuild 22 | ``` 23 | 24 | ### bootstrap 25 | 26 | This script is responsible for bootstrapping (initializing) your project after 27 | it's been checked out. Here, you should install or clone any dependencies that 28 | are required for a working build and development environment. 29 | 30 | By default, the script will verify that [xctool][] is installed, then initialize 31 | and update submodules recursively. If any submodules contain `script/bootstrap`, 32 | that will be run as well. 33 | 34 | To check that other tools are installed, you can set the `REQUIRED_TOOLS` 35 | environment variable before running `script/bootstrap`, or edit it within the 36 | script directly. Note that no installation is performed automatically, though 37 | this can always be added within your specific project. 38 | 39 | ### cibuild 40 | 41 | This script is responsible for building the project, as you would want it built 42 | for continuous integration. This is preferable to putting the logic on the CI 43 | server itself, since it ensures that any changes are versioned along with the 44 | source. 45 | 46 | By default, the script will run [`bootstrap`](#bootstrap), look for any Xcode 47 | workspace or project in the working directory, then build all targets/schemes 48 | (as found by `xcodebuild -list`) using [xctool][]. 49 | 50 | You can also specify the schemes to build by passing them into the script: 51 | 52 | ```sh 53 | script/cibuild ReactiveCocoa-Mac ReactiveCocoa-iOS 54 | ``` 55 | 56 | As with the `bootstrap` script, there are several environment variables that can 57 | be used to customize behavior. They can be set on the command line before 58 | invoking the script, or the defaults changed within the script directly. 59 | 60 | ## Getting Started 61 | 62 | To add the scripts to your project, read the contents of this repository into 63 | a `script` folder: 64 | 65 | ``` 66 | $ git remote add objc-build-scripts https://github.com/jspahrsummers/objc-build-scripts.git 67 | $ git fetch objc-build-scripts 68 | $ git read-tree --prefix=script/ -u objc-build-scripts/master 69 | ``` 70 | 71 | Then commit the changes, to incorporate the scripts into your own repository's 72 | history. You can also freely tweak the scripts for your specific project's 73 | needs. 74 | 75 | To merge in upstream changes later: 76 | 77 | ``` 78 | $ git fetch -p objc-build-scripts 79 | $ git merge --ff --squash -Xsubtree=script objc-build-scripts/master 80 | ``` 81 | 82 | [xctool]: https://github.com/facebook/xctool 83 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SCRIPT_DIR=$(dirname "$0") 4 | 5 | ## 6 | ## Configuration Variables 7 | ## 8 | 9 | config () 10 | { 11 | # A whitespace-separated list of executables that must be present and locatable. 12 | : ${REQUIRED_TOOLS="xctool"} 13 | 14 | export REQUIRED_TOOLS 15 | } 16 | 17 | ## 18 | ## Bootstrap Process 19 | ## 20 | 21 | main () 22 | { 23 | config 24 | 25 | if [ -n "$REQUIRED_TOOLS" ] 26 | then 27 | echo "*** Checking dependencies..." 28 | check_deps 29 | fi 30 | 31 | local submodules=$(git submodule status) 32 | local result=$? 33 | 34 | if [ "$result" -ne "0" ] 35 | then 36 | exit $result 37 | fi 38 | 39 | if [ -n "$submodules" ] 40 | then 41 | echo "*** Updating submodules..." 42 | update_submodules 43 | fi 44 | } 45 | 46 | check_deps () 47 | { 48 | for tool in $REQUIRED_TOOLS 49 | do 50 | which -s "$tool" 51 | if [ "$?" -ne "0" ] 52 | then 53 | echo "*** Error: $tool not found. Please install it and bootstrap again." 54 | exit 1 55 | fi 56 | done 57 | } 58 | 59 | bootstrap_submodule () 60 | { 61 | local bootstrap="script/bootstrap" 62 | 63 | if [ -e "$bootstrap" ] 64 | then 65 | echo "*** Bootstrapping $name..." 66 | "$bootstrap" >/dev/null 67 | else 68 | update_submodules 69 | fi 70 | } 71 | 72 | update_submodules () 73 | { 74 | git submodule sync --quiet && git submodule update --init && git submodule foreach --quiet bootstrap_submodule 75 | } 76 | 77 | export -f bootstrap_submodule 78 | export -f update_submodules 79 | 80 | main 81 | -------------------------------------------------------------------------------- /script/cibuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SCRIPT_DIR=$(dirname "$0") 4 | 5 | ## 6 | ## Configuration Variables 7 | ## 8 | 9 | SCHEMES="$@" 10 | 11 | config () 12 | { 13 | # The workspace to build. 14 | # 15 | # If not set and no workspace is found, the -workspace flag will not be passed 16 | # to `xctool`. 17 | # 18 | # Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will 19 | # take precedence. 20 | : ${XCWORKSPACE=$(find_pattern "*.xcworkspace")} 21 | 22 | # The project to build. 23 | # 24 | # If not set and no project is found, the -project flag will not be passed 25 | # to `xctool`. 26 | # 27 | # Only one of `XCWORKSPACE` and `XCODEPROJ` needs to be set. The former will 28 | # take precedence. 29 | : ${XCODEPROJ=$(find_pattern "*.xcodeproj")} 30 | 31 | # A bootstrap script to run before building. 32 | # 33 | # If this file does not exist, it is not considered an error. 34 | : ${BOOTSTRAP="$SCRIPT_DIR/bootstrap"} 35 | 36 | # Extra options to pass to xctool. 37 | : ${XCTOOL_OPTIONS="RUN_CLANG_STATIC_ANALYZER=NO"} 38 | 39 | # A whitespace-separated list of default schemes to build. 40 | # 41 | # Individual names can be quoted to avoid word splitting. 42 | : ${SCHEMES:=$(xcodebuild -list -project "$XCODEPROJ" 2>/dev/null | awk -f "$SCRIPT_DIR/schemes.awk")} 43 | 44 | export XCWORKSPACE 45 | export XCODEPROJ 46 | export BOOTSTRAP 47 | export XCTOOL_OPTIONS 48 | export SCHEMES 49 | } 50 | 51 | ## 52 | ## Build Process 53 | ## 54 | 55 | main () 56 | { 57 | config 58 | 59 | if [ -f "$BOOTSTRAP" ] 60 | then 61 | echo "*** Bootstrapping..." 62 | "$BOOTSTRAP" || exit $? 63 | fi 64 | 65 | echo "*** The following schemes will be built:" 66 | echo "$SCHEMES" | xargs -n 1 echo " " 67 | echo 68 | 69 | echo "$SCHEMES" | xargs -n 1 | ( 70 | local status=0 71 | 72 | while read scheme 73 | do 74 | build_scheme "$scheme" || status=1 75 | done 76 | 77 | exit $status 78 | ) 79 | } 80 | 81 | find_pattern () 82 | { 83 | ls -d $1 2>/dev/null | head -n 1 84 | } 85 | 86 | run_xctool () 87 | { 88 | if [ -n "$XCWORKSPACE" ] 89 | then 90 | xctool -workspace "$XCWORKSPACE" $XCTOOL_OPTIONS "$@" 2>&1 91 | elif [ -n "$XCODEPROJ" ] 92 | then 93 | xctool -project "$XCODEPROJ" $XCTOOL_OPTIONS "$@" 2>&1 94 | else 95 | echo "*** No workspace or project file found." 96 | exit 1 97 | fi 98 | } 99 | 100 | parse_build () 101 | { 102 | awk -f "$SCRIPT_DIR/xctool.awk" 2>&1 >/dev/null 103 | } 104 | 105 | build_scheme () 106 | { 107 | local scheme=$1 108 | 109 | echo "*** Cleaning $scheme..." 110 | run_xctool -scheme "$scheme" clean >/dev/null || exit $? 111 | 112 | echo "*** Building and testing $scheme..." 113 | echo 114 | 115 | local sdkflag= 116 | local action=test 117 | 118 | # Determine whether we can run unit tests for this target. 119 | run_xctool -scheme "$scheme" run-tests | parse_build 120 | 121 | local awkstatus=$? 122 | 123 | if [ "$awkstatus" -eq "1" ] 124 | then 125 | # SDK not found, try for iphonesimulator. 126 | sdkflag="-sdk iphonesimulator" 127 | 128 | # Determine whether the unit tests will run with iphonesimulator 129 | run_xctool $sdkflag -scheme "$scheme" run-tests | parse_build 130 | 131 | awkstatus=$? 132 | 133 | if [ "$awkstatus" -ne "0" ] 134 | then 135 | # Unit tests will not run on iphonesimulator. 136 | sdkflag="" 137 | fi 138 | fi 139 | 140 | if [ "$awkstatus" -ne "0" ] 141 | then 142 | # Unit tests aren't supported. 143 | action=build 144 | fi 145 | 146 | run_xctool $sdkflag -scheme "$scheme" $action 147 | } 148 | 149 | export -f build_scheme 150 | export -f run_xctool 151 | export -f parse_build 152 | 153 | main 154 | -------------------------------------------------------------------------------- /script/schemes.awk: -------------------------------------------------------------------------------- 1 | BEGIN { 2 | FS = "\n"; 3 | } 4 | 5 | /Schemes:/ { 6 | while (getline && $0 != "") { 7 | sub(/^ +/, ""); 8 | print "'" $0 "'"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /script/targets.awk: -------------------------------------------------------------------------------- 1 | BEGIN { 2 | FS = "\n"; 3 | } 4 | 5 | /Targets:/ { 6 | while (getline && $0 != "") { 7 | if ($0 ~ /Tests/) continue; 8 | 9 | sub(/^ +/, ""); 10 | print "'" $0 "'"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /script/xcodebuild.awk: -------------------------------------------------------------------------------- 1 | # Exit statuses: 2 | # 3 | # 0 - No errors found. 4 | # 1 - Build or test failure. Errors will be logged automatically. 5 | # 2 - Untestable target. Retry with the "build" action. 6 | 7 | BEGIN { 8 | status = 0; 9 | } 10 | 11 | { 12 | print; 13 | fflush(stdout); 14 | } 15 | 16 | /is not valid for Testing/ { 17 | exit 2; 18 | } 19 | 20 | /[0-9]+: (error|warning):/ { 21 | errors = errors $0 "\n"; 22 | } 23 | 24 | /(TEST|BUILD) FAILED/ { 25 | status = 1; 26 | } 27 | 28 | END { 29 | if (length(errors) > 0) { 30 | print "\n*** All errors:\n" errors; 31 | } 32 | 33 | fflush(stdout); 34 | exit status; 35 | } 36 | -------------------------------------------------------------------------------- /script/xctool.awk: -------------------------------------------------------------------------------- 1 | # Exit statuses: 2 | # 3 | # 0 - No errors found. 4 | # 1 - Wrong SDK. Retry with SDK `iphonesimulator`. 5 | # 2 - Missing target. 6 | 7 | BEGIN { 8 | status = 0; 9 | } 10 | 11 | { 12 | print; 13 | } 14 | 15 | /Testing with the '(.+)' SDK is not yet supported/ { 16 | status = 1; 17 | } 18 | 19 | /does not contain a target named/ { 20 | status = 2; 21 | } 22 | 23 | END { 24 | exit status; 25 | } 26 | --------------------------------------------------------------------------------