├── .gitignore ├── .travis.yml ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── ReactiveArray.podspec ├── ReactiveArray.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── ReactiveArray.xcscheme ├── ReactiveArray ├── Info.plist ├── Operation.swift ├── ReactiveArray.h └── ReactiveArray.swift ├── ReactiveArrayTests ├── Info.plist ├── OperationSpec.swift └── ReactiveArraySpec.swift ├── circle.yml ├── coverage └── .gitkeep └── script ├── .env ├── .env.template ├── bootstrap ├── build ├── certificates ├── .gitkeep └── cibot.p12 ├── cibuild ├── common ├── carthage └── install_carthage ├── coverage ├── git_hooks └── pre-push ├── script_hooks ├── .gitkeep ├── destinations └── schemes ├── test └── update /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Pods/ 26 | Carthage/ 27 | coverage/ 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.1 3 | script: script/cibuild 4 | branches: 5 | only: 6 | - master 7 | env: 8 | global: 9 | - secure: S6eJVfzn+y3ZrEI0kMDQlkwJos83eG+hPXJ0tOZ+8M+F3vZRDLI9M8MRdrLnh8TnInVtNCD8ItsmoDWsVCa13RwlH7VNRHBZWHFNJwWiBi2Qtjl/r5Tz+NOeY/hvMmEiL1VmymFm0OF6kFKzGinLKxr3ypI0tbF36VT8SaPFCbThX5iIoTnPn911XvKyRyswbQka8WnkXzHOLHyzQ2Mb6O4pxRrETW9QfYdgZq2rDQoBEmJlhif3u1ORXmcHY/pIERlzOm/VJMkT9rYiLIuMfg9p+5OEqNHjmAJO0GZ1CP7yF2AjnAp01ljWS/jcN7pjEt2u1+jPV1zSDjKTBGJhS9mJ45ebmZhQNWitIjjaeN5C/8CCqkRwuYH7jjhJe+J7sBrdUwoB1Kc99QTZEnh9l8lhHzTk4xZwwYuSiVO8bhhY0t8GTpuo0Lf2nrZMMLgoJZ98yDliaSH91V7kovIdnHWpwJvZo/a9imHNNFWrig5YYMDRwVT9eSZodBfPgdSoFW3MfyTV86uvUo5a/WxyQpn7oJnNMkkg0nTNN2uPc7sulshps8458RI1G2HK78Zkuz4NHNx2kiUgTv/v1AuWfvM6JpOHeVJwbjXrznv7/cU+eaZXwm5+LpOqX70V9CIfsJloIub+7UJTMakefHW73tnKEtm+Y/QxL7yMV8fEucU= 10 | - secure: fQdQWv/6cNDfXjSYAIqZVtW3wA0mSpZFDzVv1gZj+25ZYqGTCtGmVTa8T+c1HFF5MhF3MPUwGD6V+7yDX9gGX57vGPrSCcYChv0Qa3yqdQNgfM+6XUXSBNeRT9ti6pQNa2lSRk4Qn+8Am9weVRbhkypflG8BZcVVWbEQwgBqktQtE7xxQ/m0UtrNVwRgThi0U0Qi/hvdi5z+3129Yljk6nbJ1AmPSY/JWjMVdAuzJVloE58KtTzV1hHEzvEjYlsd4bVTFR6DX2MfT7ekqOs7FwR4ewzjeRgBU0VBt8zcRMNJk8dWcErSpRmBxEs+xOC9EAIZG+8pyYAwbtjkneR6wj/mvgJmk/v7sCTkFz3bmqVCtO8HSiMSLgNY3iDHcZIlcVEBVP8iOHoMMelu49uLUjWtMAx/UlBdLdhOz8CHbNRTtBPb+0kBu1ChQn5tkYvUAZgrPCIz10Bu/IhUasDo0vYEbc4v5aMXQswFMDuLxKBiysthbbaAfL/elsdA/rZXHn0XVK0I4FH5rQcA0jlQDSJbRIHGIG7IB0sL+uLI2qgKG6OTXWM3smcZ7Lvty9FZTCeaEHSZpEHy68W8fwrd8dtlBHbPog8rmTqcDSQoigIEHvJvFg1ElmymBq/OM/wrHZ2pEFRNhsWdE9F/QOhP4bC05xu4fhZescUTeCRXPeQ= 11 | - secure: DuYs7kuYAVcBD1wv7ArZ3elBcr/5XIjlqmmRqLPa03cowlM8HLyexuArNrxZ24Fs4SxjV0N6DwewX82AdHojKcYc54r3ntS5tgYLBD+EDq5MFgd/z6MvbhH6S8wJONUC++b2LnBUohSi3OybOXUdtzZTPWHVtFUeiev3QfeJQ6/UGmhnSarcp9WnVUrhVaAX6cnE9Uti7xPG9c6dz8iyl6xoanpvyDz1P/iM9TZMBlS5XV3lIvcLZEkBal6ivujRNEOHtTANMfD1kbLtxYwwX7KCgj03x9/OZJ/WA9BcZ97/jKL5HVFwtEj61CRfUGNozH6fDvLA5MRUKDdEai1CPfjOAFE3+n/sg+l7x0Ipl7PfEQ0zttqlxQaqB5/ujdelq9iIvelHPXT5fSm/tjk69hWvnh60g+/EeMUNgXVsjzfFMu5inlBgPes/aKm+G5x3FLD2ps28cqeCExdjd0E9eJyR34Xw9GGZNVAW6dDoaB2RCEQzJPNd46y25Y4VYX501yGS59l3vk3wDhwjN+CuJV4Blz/eOlP+RxJQAgtDHiDmDj/077wgjOUK9OTyG8IsQD680X+Ar2hmEtLgxnk0EJpuAa99pyKBlB3zNxMSk+uMDxEhgck413Y8AUIV90RBCoymbtzJxAm58kOCYE2+pdy/T8YxwLvDum9n257SdCk= 12 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.5" 2 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" ~> 3.0.0 2 | github "Quick/Quick" ~> 0.8.0 3 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v3.0.0" 2 | github "Quick/Quick" "v0.8.0" 3 | github "antitypical/Result" "1.0.1" 4 | github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.5" 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'cocoapods' 4 | gem 'xcpretty' 5 | gem 'coveralls-gcov' 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (4.2.5) 5 | i18n (~> 0.7) 6 | json (~> 1.7, >= 1.7.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | claide (0.9.1) 11 | cocoapods (0.39.0) 12 | activesupport (>= 4.0.2) 13 | claide (~> 0.9.1) 14 | cocoapods-core (= 0.39.0) 15 | cocoapods-downloader (~> 0.9.3) 16 | cocoapods-plugins (~> 0.4.2) 17 | cocoapods-search (~> 0.1.0) 18 | cocoapods-stats (~> 0.6.2) 19 | cocoapods-trunk (~> 0.6.4) 20 | cocoapods-try (~> 0.5.1) 21 | colored (~> 1.2) 22 | escape (~> 0.0.4) 23 | molinillo (~> 0.4.0) 24 | nap (~> 1.0) 25 | xcodeproj (~> 0.28.2) 26 | cocoapods-core (0.39.0) 27 | activesupport (>= 4.0.2) 28 | fuzzy_match (~> 2.0.4) 29 | nap (~> 1.0) 30 | cocoapods-downloader (0.9.3) 31 | cocoapods-plugins (0.4.2) 32 | nap 33 | cocoapods-search (0.1.0) 34 | cocoapods-stats (0.6.2) 35 | cocoapods-trunk (0.6.4) 36 | nap (>= 0.8, < 2.0) 37 | netrc (= 0.7.8) 38 | cocoapods-try (0.5.1) 39 | colored (1.2) 40 | commander (4.3.4) 41 | highline (~> 1.7.2) 42 | coveralls-gcov (0.1.2) 43 | commander 44 | escape (0.0.4) 45 | fuzzy_match (2.0.4) 46 | highline (1.7.2) 47 | i18n (0.7.0) 48 | json (1.8.3) 49 | minitest (5.8.3) 50 | molinillo (0.4.0) 51 | nap (1.0.0) 52 | netrc (0.7.8) 53 | thread_safe (0.3.5) 54 | tzinfo (1.2.2) 55 | thread_safe (~> 0.1) 56 | xcodeproj (0.28.2) 57 | activesupport (>= 3) 58 | claide (~> 0.9.1) 59 | colored (~> 1.2) 60 | xcpretty (0.1.10) 61 | 62 | PLATFORMS 63 | ruby 64 | 65 | DEPENDENCIES 66 | cocoapods 67 | coveralls-gcov 68 | xcpretty 69 | 70 | BUNDLED WITH 71 | 1.10.6 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Guido Marucci Blas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all 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, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ReactiveArray 2 | 3 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 4 | [![Circle CI](https://circleci.com/gh/Wolox/ReactiveArray.svg?style=svg)](https://circleci.com/gh/Wolox/ReactiveArray) 5 | [![Build Status](https://travis-ci.org/Wolox/ReactiveArray.svg?branch=master)](https://travis-ci.org/Wolox/ReactiveArray) 6 | [![Coverage Status](https://coveralls.io/repos/Wolox/ReactiveArray/badge.svg?branch=master&service=github)](https://coveralls.io/github/Wolox/ReactiveArray?branch=master) 7 | [![Release](https://img.shields.io/github/release/Wolox/ReactiveArray.svg)](https://github.com/Wolox/ReactiveArray/releases) 8 | [![Join the chat at https://gitter.im/Wolox/ReactiveArray](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Wolox/ReactiveArray?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 9 | 10 | An array class implemented in Swift that can be observed using [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa)'s Signals. 11 | 12 | ## Installation 13 | 14 | ### [Carthage] 15 | 16 | [Carthage]: https://github.com/Carthage/Carthage 17 | 18 | Add the following to your Cartfile: 19 | 20 | ``` 21 | github "Wolox/ReactiveArray" 22 | ``` 23 | 24 | Then run `carthage update`. 25 | 26 | Follow the current instructions in [Carthage's README][carthage-installation] 27 | for up to date installation instructions. 28 | 29 | [carthage-installation]: https://github.com/Carthage/Carthage#adding-frameworks-to-an-application 30 | 31 | You'll also need to add `Result.framework`, `Box.framework` and `ReactiveCocoa.framework` to your Xcode 32 | project. 33 | 34 | [Box]: https://github.com/robrix/box 35 | [Result]: https://github.com/antitypical/Result 36 | [ReactiveCocoa]: https://github.com/ReactiveCocoa/ReactiveCocoa 37 | 38 | ## Usage 39 | 40 | ### Array operations 41 | 42 | ```swift 43 | let array = ReactiveArray(elements: [1,2,3]) 44 | 45 | array.append(4) // => [1,2,3,4] 46 | array.append(5) // => [1,2,3,4,5] 47 | 48 | array[0] = 5 // => [5,2,3,4,5] 49 | array.removeAtIndex(4) // => [5,2,3,4] 50 | ``` 51 | 52 | `ReactiveArray` conforms to `CollectionType` and `MutableCollectionType` which allows you to perform operations like 53 | `map` or `filter`. 54 | 55 | ### Observing changes 56 | 57 | The array can be observed for mutations using the `signal` or `producer` properties. Both `Signal` and `SignalProducer` will emit `Operation` values on observation for any of the mutating 58 | operations performed to the array. 59 | 60 | `Operation` is an enumeration with four cases. One for each 61 | mutating operation: `Append`, `Insert`, `Update` and `RemoveElement` 62 | 63 | #### Using `SignalProducer` 64 | 65 | Prints an append operation for each element 66 | that has already been stored in the array and then 67 | the corresponding operation for any new operation performed to the array. 68 | 69 | ```swift 70 | let array = ReactiveArray(elements: [1,2,3]) 71 | array.producer |> start(next: { println($0) }) 72 | array[0] = 5 73 | array[1] = 4 74 | array.append(2) 75 | array.removeAtIndex(2) 76 | ``` 77 | 78 | will print the following output: 79 | 80 | ``` 81 | .Append(value: 1) 82 | .Append(value: 2) 83 | .Append(value: 3) 84 | .Update(value: 5, atIndex: 0) 85 | .Update(value: 4, atIndex: 1) 86 | .RemoveElement(atIndex:2) 87 | ``` 88 | 89 | #### Using `Signal` 90 | 91 | The signal property will only emit values for operations performed in the array after observation. 92 | 93 | ```swift 94 | let array = ReactiveArray(elements: [1,2,3]) 95 | array.signal.observe { println($0) } 96 | array[0] = 5 97 | array[1] = 4 98 | array.append(2) 99 | array.removeAtIndex(2) 100 | ``` 101 | 102 | will print the following output: 103 | 104 | ``` 105 | .Update(value: 5, atIndex: 0) 106 | .Update(value: 4, atIndex: 1) 107 | .RemoveElement(atIndex:2) 108 | ``` 109 | 110 | #### Using `observableCount` 111 | 112 | You can also observe the `observableCount` property that exposes a producer that emits the current amount of elements each time the array is mutated. 113 | 114 | ```swift 115 | let array = ReactiveArray() 116 | array.observableCount.producer |> start(next: { println($0) }) 117 | array.append("Hello") 118 | array.append("World") 119 | ``` 120 | 121 | will print the following output: 122 | 123 | ``` 124 | 0 125 | 1 126 | 2 127 | ``` 128 | 129 | ### Mirror 130 | 131 | Mirror creates a new `ReactiveArray` by applying a `transform` operation for each element contained in the original array. It is like `map` but the returned array will mutate everytime the original array mutates. 132 | 133 | ```swift 134 | let array = ReactiveArray(elements: [1,2,3]) 135 | let doubles = array.mirror { $0 * 2 } 136 | doubles.producer |> start(next: { println($0) }) 137 | array.append(4) 138 | array.append(5) 139 | array[0] = 6 140 | ``` 141 | 142 | will print the following output: 143 | 144 | ``` 145 | .Append(value: 2) 146 | .Append(value: 4) 147 | .Append(value: 6) 148 | .Append(value: 8) 149 | .Append(value: 10) 150 | .Update(value: 12, atIndex: 0) 151 | ``` 152 | 153 | 154 | ## Contributing 155 | 156 | ### Setup project 157 | 158 | If you want to contribute you need to setup your 159 | development environment first. 160 | 161 | git clone https://github.com/Wolox/ReactiveArray.git 162 | cd ReactiveArray 163 | script/bootstrap 164 | open ReactiveArray.xcodeproj 165 | 166 | #### Environment dependencies 167 | 168 | The following dependencies must be installed in your development machine. 169 | 170 | * XCode & XCode command line tools 171 | * Homebrew 172 | * Ruby 173 | * Bundler (sudoless) 174 | * Carthage 175 | * Gcovr 176 | * SwiftCov 177 | 178 | ## About 179 | 180 | This project is maintained by [Guido Marucci Blas](https://github.com/guidomb) and it was written by [Wolox](http://www.wolox.com.ar). 181 | 182 | ![Wolox](https://raw.githubusercontent.com/Wolox/press-kit/master/logos/logo_banner.png) 183 | 184 | ## License 185 | 186 | **ReactiveArray** is available under the MIT [license](https://raw.githubusercontent.com/Wolox/ReactiveArray/master/LICENSE). 187 | 188 | Copyright (c) 2015 Guido Marucci Blas 189 | 190 | Permission is hereby granted, free of charge, to any person obtaining a copy 191 | of this software and associated documentation files (the "Software"), to deal 192 | in the Software without restriction, including without limitation the rights 193 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 194 | copies of the Software, and to permit persons to whom the Software is 195 | furnished to do so, subject to the following conditions: 196 | 197 | The above copyright notice and this permission notice shall be included in 198 | all copies or substantial portions of the Software. 199 | 200 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 201 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 202 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 203 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 204 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 205 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 206 | THE SOFTWARE. 207 | -------------------------------------------------------------------------------- /ReactiveArray.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'ReactiveArray' 3 | spec.version = '0.5.0' 4 | spec.summary = "An array class implemented in Swift that can be observed using ReactiveCocoa's Signals." 5 | spec.homepage = 'https://github.com/Wolox/ReactiveArray' 6 | spec.license = { :type => 'MIT', :file => 'LICENSE' } 7 | spec.author = { 8 | 'Guido Marucci Blas' => 'guidomb@wolox.com.ar', 9 | 'Wolox' => nil, 10 | } 11 | spec.social_media_url = 'http://twitter.com/wolox' 12 | spec.source = { :git => 'https://github.com/Wolox/ReactiveArray.git', :tag => "v#{spec.version}" } 13 | spec.source_files = 'ReactiveArray/**/*.{h,swift}' 14 | spec.requires_arc = true 15 | spec.ios.deployment_target = '8.0' 16 | #spec.osx.deployment_target = '10.9' 17 | 18 | spec.dependency 'ReactiveCocoa', '~> 4.0' 19 | spec.framework = "Foundation" 20 | end 21 | -------------------------------------------------------------------------------- /ReactiveArray.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9DC6B5431B45B15800190357 /* OperationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DC6B5421B45B15800190357 /* OperationSpec.swift */; }; 11 | 9DF1E5401B41E438007A6182 /* ReactiveArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 9DF1E53F1B41E438007A6182 /* ReactiveArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | 9DF1E5461B41E438007A6182 /* ReactiveArray.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF1E53A1B41E438007A6182 /* ReactiveArray.framework */; }; 13 | 9DF1E54D1B41E438007A6182 /* ReactiveArraySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF1E54C1B41E438007A6182 /* ReactiveArraySpec.swift */; }; 14 | 9DF1E5571B41E4A2007A6182 /* ReactiveArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF1E5561B41E4A2007A6182 /* ReactiveArray.swift */; }; 15 | 9DF1E55F1B41E6D1007A6182 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF1E55C1B41E6D1007A6182 /* ReactiveCocoa.framework */; }; 16 | 9DF1E5601B41E6D1007A6182 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF1E55D1B41E6D1007A6182 /* Result.framework */; }; 17 | 9DF1E5621B41E6E1007A6182 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF1E55C1B41E6D1007A6182 /* ReactiveCocoa.framework */; }; 18 | 9DF1E5631B41E6E1007A6182 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF1E55D1B41E6D1007A6182 /* Result.framework */; }; 19 | 9DF1E5661B41F937007A6182 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF1E5641B41F937007A6182 /* Nimble.framework */; }; 20 | 9DF1E5671B41F937007A6182 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF1E5651B41F937007A6182 /* Quick.framework */; }; 21 | 9DF1E5691B444BCA007A6182 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DF1E5681B444BCA007A6182 /* Operation.swift */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | 9DF1E5471B41E438007A6182 /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = 9DF1E5311B41E438007A6182 /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = 9DF1E5391B41E438007A6182; 30 | remoteInfo = ReactiveArray; 31 | }; 32 | /* End PBXContainerItemProxy section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 9DC6B5421B45B15800190357 /* OperationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationSpec.swift; sourceTree = ""; }; 36 | 9DF1E53A1B41E438007A6182 /* ReactiveArray.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactiveArray.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 9DF1E53E1B41E438007A6182 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 9DF1E53F1B41E438007A6182 /* ReactiveArray.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReactiveArray.h; sourceTree = ""; }; 39 | 9DF1E5451B41E438007A6182 /* ReactiveArrayTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveArrayTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 9DF1E54B1B41E438007A6182 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 9DF1E54C1B41E438007A6182 /* ReactiveArraySpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactiveArraySpec.swift; sourceTree = ""; }; 42 | 9DF1E5561B41E4A2007A6182 /* ReactiveArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactiveArray.swift; sourceTree = ""; }; 43 | 9DF1E55C1B41E6D1007A6182 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = Carthage/Build/iOS/ReactiveCocoa.framework; sourceTree = SOURCE_ROOT; }; 44 | 9DF1E55D1B41E6D1007A6182 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = SOURCE_ROOT; }; 45 | 9DF1E5641B41F937007A6182 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = SOURCE_ROOT; }; 46 | 9DF1E5651B41F937007A6182 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = SOURCE_ROOT; }; 47 | 9DF1E5681B444BCA007A6182 /* Operation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | 9DF1E5361B41E438007A6182 /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | 9DF1E55F1B41E6D1007A6182 /* ReactiveCocoa.framework in Frameworks */, 56 | 9DF1E5601B41E6D1007A6182 /* Result.framework in Frameworks */, 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | 9DF1E5421B41E438007A6182 /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | 9DF1E5661B41F937007A6182 /* Nimble.framework in Frameworks */, 65 | 9DF1E5671B41F937007A6182 /* Quick.framework in Frameworks */, 66 | 9DF1E5621B41E6E1007A6182 /* ReactiveCocoa.framework in Frameworks */, 67 | 9DF1E5631B41E6E1007A6182 /* Result.framework in Frameworks */, 68 | 9DF1E5461B41E438007A6182 /* ReactiveArray.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 9DF1E5301B41E438007A6182 = { 76 | isa = PBXGroup; 77 | children = ( 78 | 9DF1E53C1B41E438007A6182 /* ReactiveArray */, 79 | 9DF1E5491B41E438007A6182 /* ReactiveArrayTests */, 80 | 9DF1E53B1B41E438007A6182 /* Products */, 81 | 9DF1E55A1B41E6A4007A6182 /* Frameworks */, 82 | ); 83 | sourceTree = ""; 84 | }; 85 | 9DF1E53B1B41E438007A6182 /* Products */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9DF1E53A1B41E438007A6182 /* ReactiveArray.framework */, 89 | 9DF1E5451B41E438007A6182 /* ReactiveArrayTests.xctest */, 90 | ); 91 | name = Products; 92 | sourceTree = ""; 93 | }; 94 | 9DF1E53C1B41E438007A6182 /* ReactiveArray */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 9DF1E53F1B41E438007A6182 /* ReactiveArray.h */, 98 | 9DF1E5561B41E4A2007A6182 /* ReactiveArray.swift */, 99 | 9DF1E5681B444BCA007A6182 /* Operation.swift */, 100 | 9DF1E53D1B41E438007A6182 /* Supporting Files */, 101 | ); 102 | path = ReactiveArray; 103 | sourceTree = ""; 104 | }; 105 | 9DF1E53D1B41E438007A6182 /* Supporting Files */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 9DF1E53E1B41E438007A6182 /* Info.plist */, 109 | ); 110 | name = "Supporting Files"; 111 | sourceTree = ""; 112 | }; 113 | 9DF1E5491B41E438007A6182 /* ReactiveArrayTests */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 9DF1E54C1B41E438007A6182 /* ReactiveArraySpec.swift */, 117 | 9DC6B5421B45B15800190357 /* OperationSpec.swift */, 118 | 9DF1E54A1B41E438007A6182 /* Supporting Files */, 119 | ); 120 | path = ReactiveArrayTests; 121 | sourceTree = ""; 122 | }; 123 | 9DF1E54A1B41E438007A6182 /* Supporting Files */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 9DF1E54B1B41E438007A6182 /* Info.plist */, 127 | ); 128 | name = "Supporting Files"; 129 | sourceTree = ""; 130 | }; 131 | 9DF1E55A1B41E6A4007A6182 /* Frameworks */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 9DF1E5641B41F937007A6182 /* Nimble.framework */, 135 | 9DF1E5651B41F937007A6182 /* Quick.framework */, 136 | 9DF1E55C1B41E6D1007A6182 /* ReactiveCocoa.framework */, 137 | 9DF1E55D1B41E6D1007A6182 /* Result.framework */, 138 | ); 139 | name = Frameworks; 140 | path = ReactiveArray; 141 | sourceTree = ""; 142 | }; 143 | /* End PBXGroup section */ 144 | 145 | /* Begin PBXHeadersBuildPhase section */ 146 | 9DF1E5371B41E438007A6182 /* Headers */ = { 147 | isa = PBXHeadersBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | 9DF1E5401B41E438007A6182 /* ReactiveArray.h in Headers */, 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXHeadersBuildPhase section */ 155 | 156 | /* Begin PBXNativeTarget section */ 157 | 9DF1E5391B41E438007A6182 /* ReactiveArray */ = { 158 | isa = PBXNativeTarget; 159 | buildConfigurationList = 9DF1E5501B41E438007A6182 /* Build configuration list for PBXNativeTarget "ReactiveArray" */; 160 | buildPhases = ( 161 | 9DF1E5351B41E438007A6182 /* Sources */, 162 | 9DF1E5361B41E438007A6182 /* Frameworks */, 163 | 9DF1E5371B41E438007A6182 /* Headers */, 164 | 9DF1E5381B41E438007A6182 /* Resources */, 165 | 9DF1E5581B41E637007A6182 /* Carthage */, 166 | ); 167 | buildRules = ( 168 | ); 169 | dependencies = ( 170 | ); 171 | name = ReactiveArray; 172 | productName = ReactiveArray; 173 | productReference = 9DF1E53A1B41E438007A6182 /* ReactiveArray.framework */; 174 | productType = "com.apple.product-type.framework"; 175 | }; 176 | 9DF1E5441B41E438007A6182 /* ReactiveArrayTests */ = { 177 | isa = PBXNativeTarget; 178 | buildConfigurationList = 9DF1E5531B41E438007A6182 /* Build configuration list for PBXNativeTarget "ReactiveArrayTests" */; 179 | buildPhases = ( 180 | 9DF1E5411B41E438007A6182 /* Sources */, 181 | 9DF1E5421B41E438007A6182 /* Frameworks */, 182 | 9DF1E5431B41E438007A6182 /* Resources */, 183 | 9DF1E5591B41E667007A6182 /* Carthage */, 184 | ); 185 | buildRules = ( 186 | ); 187 | dependencies = ( 188 | 9DF1E5481B41E438007A6182 /* PBXTargetDependency */, 189 | ); 190 | name = ReactiveArrayTests; 191 | productName = ReactiveArrayTests; 192 | productReference = 9DF1E5451B41E438007A6182 /* ReactiveArrayTests.xctest */; 193 | productType = "com.apple.product-type.bundle.unit-test"; 194 | }; 195 | /* End PBXNativeTarget section */ 196 | 197 | /* Begin PBXProject section */ 198 | 9DF1E5311B41E438007A6182 /* Project object */ = { 199 | isa = PBXProject; 200 | attributes = { 201 | LastSwiftMigration = 0700; 202 | LastSwiftUpdateCheck = 0700; 203 | LastUpgradeCheck = 0700; 204 | ORGANIZATIONNAME = Wolox; 205 | TargetAttributes = { 206 | 9DF1E5391B41E438007A6182 = { 207 | CreatedOnToolsVersion = 6.3.2; 208 | }; 209 | 9DF1E5441B41E438007A6182 = { 210 | CreatedOnToolsVersion = 6.3.2; 211 | }; 212 | }; 213 | }; 214 | buildConfigurationList = 9DF1E5341B41E438007A6182 /* Build configuration list for PBXProject "ReactiveArray" */; 215 | compatibilityVersion = "Xcode 3.2"; 216 | developmentRegion = English; 217 | hasScannedForEncodings = 0; 218 | knownRegions = ( 219 | en, 220 | ); 221 | mainGroup = 9DF1E5301B41E438007A6182; 222 | productRefGroup = 9DF1E53B1B41E438007A6182 /* Products */; 223 | projectDirPath = ""; 224 | projectRoot = ""; 225 | targets = ( 226 | 9DF1E5391B41E438007A6182 /* ReactiveArray */, 227 | 9DF1E5441B41E438007A6182 /* ReactiveArrayTests */, 228 | ); 229 | }; 230 | /* End PBXProject section */ 231 | 232 | /* Begin PBXResourcesBuildPhase section */ 233 | 9DF1E5381B41E438007A6182 /* Resources */ = { 234 | isa = PBXResourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | 9DF1E5431B41E438007A6182 /* Resources */ = { 241 | isa = PBXResourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | }; 247 | /* End PBXResourcesBuildPhase section */ 248 | 249 | /* Begin PBXShellScriptBuildPhase section */ 250 | 9DF1E5581B41E637007A6182 /* Carthage */ = { 251 | isa = PBXShellScriptBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | inputPaths = ( 256 | "$(SRCROOT)/Carthage/Build/iOS/ReactiveCocoa.framework", 257 | "$(SRCROOT)/Carthage/Build/iOS/Result.framework", 258 | ); 259 | name = Carthage; 260 | outputPaths = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | shellPath = /bin/sh; 264 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 265 | }; 266 | 9DF1E5591B41E667007A6182 /* Carthage */ = { 267 | isa = PBXShellScriptBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | ); 271 | inputPaths = ( 272 | "$(SRCROOT)/Carthage/Build/iOS/ReactiveCocoa.framework", 273 | "$(SRCROOT)/Carthage/Build/iOS/Result.framework", 274 | "$(SRCROOT)/Carthage/Build/iOS/Nimble.framework", 275 | "$(SRCROOT)/Carthage/Build/iOS/Quick.framework", 276 | ); 277 | name = Carthage; 278 | outputPaths = ( 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | shellPath = /bin/sh; 282 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 283 | }; 284 | /* End PBXShellScriptBuildPhase section */ 285 | 286 | /* Begin PBXSourcesBuildPhase section */ 287 | 9DF1E5351B41E438007A6182 /* Sources */ = { 288 | isa = PBXSourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | 9DF1E5571B41E4A2007A6182 /* ReactiveArray.swift in Sources */, 292 | 9DF1E5691B444BCA007A6182 /* Operation.swift in Sources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | 9DF1E5411B41E438007A6182 /* Sources */ = { 297 | isa = PBXSourcesBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | 9DC6B5431B45B15800190357 /* OperationSpec.swift in Sources */, 301 | 9DF1E54D1B41E438007A6182 /* ReactiveArraySpec.swift in Sources */, 302 | ); 303 | runOnlyForDeploymentPostprocessing = 0; 304 | }; 305 | /* End PBXSourcesBuildPhase section */ 306 | 307 | /* Begin PBXTargetDependency section */ 308 | 9DF1E5481B41E438007A6182 /* PBXTargetDependency */ = { 309 | isa = PBXTargetDependency; 310 | target = 9DF1E5391B41E438007A6182 /* ReactiveArray */; 311 | targetProxy = 9DF1E5471B41E438007A6182 /* PBXContainerItemProxy */; 312 | }; 313 | /* End PBXTargetDependency section */ 314 | 315 | /* Begin XCBuildConfiguration section */ 316 | 9DF1E54E1B41E438007A6182 /* Debug */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | ALWAYS_SEARCH_USER_PATHS = NO; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BOOL_CONVERSION = YES; 325 | CLANG_WARN_CONSTANT_CONVERSION = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | COPY_PHASE_STRIP = NO; 335 | CURRENT_PROJECT_VERSION = 0.3.0; 336 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 337 | ENABLE_STRICT_OBJC_MSGSEND = YES; 338 | ENABLE_TESTABILITY = YES; 339 | GCC_C_LANGUAGE_STANDARD = gnu99; 340 | GCC_DYNAMIC_NO_PIC = NO; 341 | GCC_NO_COMMON_BLOCKS = YES; 342 | GCC_OPTIMIZATION_LEVEL = 0; 343 | GCC_PREPROCESSOR_DEFINITIONS = ( 344 | "DEBUG=1", 345 | "$(inherited)", 346 | ); 347 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 348 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 349 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 350 | GCC_WARN_UNDECLARED_SELECTOR = YES; 351 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 352 | GCC_WARN_UNUSED_FUNCTION = YES; 353 | GCC_WARN_UNUSED_VARIABLE = YES; 354 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 355 | MTL_ENABLE_DEBUG_INFO = YES; 356 | ONLY_ACTIVE_ARCH = YES; 357 | SDKROOT = iphoneos; 358 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 359 | TARGETED_DEVICE_FAMILY = "1,2"; 360 | VERSIONING_SYSTEM = "apple-generic"; 361 | VERSION_INFO_PREFIX = ""; 362 | }; 363 | name = Debug; 364 | }; 365 | 9DF1E54F1B41E438007A6182 /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ALWAYS_SEARCH_USER_PATHS = NO; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_WARN_BOOL_CONVERSION = YES; 374 | CLANG_WARN_CONSTANT_CONVERSION = YES; 375 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 376 | CLANG_WARN_EMPTY_BODY = YES; 377 | CLANG_WARN_ENUM_CONVERSION = YES; 378 | CLANG_WARN_INT_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_UNREACHABLE_CODE = YES; 381 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 382 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 383 | COPY_PHASE_STRIP = NO; 384 | CURRENT_PROJECT_VERSION = 0.3.0; 385 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 386 | ENABLE_NS_ASSERTIONS = NO; 387 | ENABLE_STRICT_OBJC_MSGSEND = YES; 388 | GCC_C_LANGUAGE_STANDARD = gnu99; 389 | GCC_NO_COMMON_BLOCKS = YES; 390 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 391 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 392 | GCC_WARN_UNDECLARED_SELECTOR = YES; 393 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 394 | GCC_WARN_UNUSED_FUNCTION = YES; 395 | GCC_WARN_UNUSED_VARIABLE = YES; 396 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 397 | MTL_ENABLE_DEBUG_INFO = NO; 398 | SDKROOT = iphoneos; 399 | TARGETED_DEVICE_FAMILY = "1,2"; 400 | VALIDATE_PRODUCT = YES; 401 | VERSIONING_SYSTEM = "apple-generic"; 402 | VERSION_INFO_PREFIX = ""; 403 | }; 404 | name = Release; 405 | }; 406 | 9DF1E5511B41E438007A6182 /* Debug */ = { 407 | isa = XCBuildConfiguration; 408 | buildSettings = { 409 | CLANG_ENABLE_MODULES = YES; 410 | DEFINES_MODULE = YES; 411 | DYLIB_COMPATIBILITY_VERSION = 1; 412 | DYLIB_CURRENT_VERSION = 1; 413 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 414 | ENABLE_BITCODE = NO; 415 | FRAMEWORK_SEARCH_PATHS = ( 416 | "$(inherited)", 417 | "$(PROJECT_DIR)/Carthage/Build/iOS", 418 | ); 419 | INFOPLIST_FILE = ReactiveArray/Info.plist; 420 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 421 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 422 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 423 | PRODUCT_BUNDLE_IDENTIFIER = "ar.com.wolox.$(PRODUCT_NAME:rfc1034identifier)"; 424 | PRODUCT_NAME = "$(TARGET_NAME)"; 425 | SKIP_INSTALL = YES; 426 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 427 | }; 428 | name = Debug; 429 | }; 430 | 9DF1E5521B41E438007A6182 /* Release */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | CLANG_ENABLE_MODULES = YES; 434 | DEFINES_MODULE = YES; 435 | DYLIB_COMPATIBILITY_VERSION = 1; 436 | DYLIB_CURRENT_VERSION = 1; 437 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 438 | ENABLE_BITCODE = NO; 439 | FRAMEWORK_SEARCH_PATHS = ( 440 | "$(inherited)", 441 | "$(PROJECT_DIR)/Carthage/Build/iOS", 442 | ); 443 | INFOPLIST_FILE = ReactiveArray/Info.plist; 444 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 445 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 446 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 447 | PRODUCT_BUNDLE_IDENTIFIER = "ar.com.wolox.$(PRODUCT_NAME:rfc1034identifier)"; 448 | PRODUCT_NAME = "$(TARGET_NAME)"; 449 | SKIP_INSTALL = YES; 450 | }; 451 | name = Release; 452 | }; 453 | 9DF1E5541B41E438007A6182 /* Debug */ = { 454 | isa = XCBuildConfiguration; 455 | buildSettings = { 456 | FRAMEWORK_SEARCH_PATHS = ( 457 | "$(inherited)", 458 | "$(PROJECT_DIR)/Carthage/Build/iOS", 459 | ); 460 | GCC_PREPROCESSOR_DEFINITIONS = ( 461 | "DEBUG=1", 462 | "$(inherited)", 463 | ); 464 | INFOPLIST_FILE = ReactiveArrayTests/Info.plist; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 466 | PRODUCT_BUNDLE_IDENTIFIER = "ar.com.wolox.$(PRODUCT_NAME:rfc1034identifier)"; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | }; 469 | name = Debug; 470 | }; 471 | 9DF1E5551B41E438007A6182 /* Release */ = { 472 | isa = XCBuildConfiguration; 473 | buildSettings = { 474 | FRAMEWORK_SEARCH_PATHS = ( 475 | "$(inherited)", 476 | "$(PROJECT_DIR)/Carthage/Build/iOS", 477 | ); 478 | INFOPLIST_FILE = ReactiveArrayTests/Info.plist; 479 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 480 | PRODUCT_BUNDLE_IDENTIFIER = "ar.com.wolox.$(PRODUCT_NAME:rfc1034identifier)"; 481 | PRODUCT_NAME = "$(TARGET_NAME)"; 482 | }; 483 | name = Release; 484 | }; 485 | /* End XCBuildConfiguration section */ 486 | 487 | /* Begin XCConfigurationList section */ 488 | 9DF1E5341B41E438007A6182 /* Build configuration list for PBXProject "ReactiveArray" */ = { 489 | isa = XCConfigurationList; 490 | buildConfigurations = ( 491 | 9DF1E54E1B41E438007A6182 /* Debug */, 492 | 9DF1E54F1B41E438007A6182 /* Release */, 493 | ); 494 | defaultConfigurationIsVisible = 0; 495 | defaultConfigurationName = Release; 496 | }; 497 | 9DF1E5501B41E438007A6182 /* Build configuration list for PBXNativeTarget "ReactiveArray" */ = { 498 | isa = XCConfigurationList; 499 | buildConfigurations = ( 500 | 9DF1E5511B41E438007A6182 /* Debug */, 501 | 9DF1E5521B41E438007A6182 /* Release */, 502 | ); 503 | defaultConfigurationIsVisible = 0; 504 | defaultConfigurationName = Release; 505 | }; 506 | 9DF1E5531B41E438007A6182 /* Build configuration list for PBXNativeTarget "ReactiveArrayTests" */ = { 507 | isa = XCConfigurationList; 508 | buildConfigurations = ( 509 | 9DF1E5541B41E438007A6182 /* Debug */, 510 | 9DF1E5551B41E438007A6182 /* Release */, 511 | ); 512 | defaultConfigurationIsVisible = 0; 513 | defaultConfigurationName = Release; 514 | }; 515 | /* End XCConfigurationList section */ 516 | }; 517 | rootObject = 9DF1E5311B41E438007A6182 /* Project object */; 518 | } 519 | -------------------------------------------------------------------------------- /ReactiveArray.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ReactiveArray.xcodeproj/xcshareddata/xcschemes/ReactiveArray.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 | -------------------------------------------------------------------------------- /ReactiveArray/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.7.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ReactiveArray/Operation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Operation.swift 3 | // ReactiveArray 4 | // 5 | // Created by Guido Marucci Blas on 7/1/15. 6 | // Copyright (c) 2015 Wolox. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Operation: CustomDebugStringConvertible { 12 | 13 | case Append(value: T) 14 | case Insert(value: T, atIndex: Int) 15 | case Update(value: T, atIndex: Int) 16 | case RemoveElement(atIndex: Int) 17 | 18 | public func map(mapper: T -> U) -> Operation { 19 | let result: Operation 20 | switch self { 21 | case .Append(let value): 22 | result = Operation.Append(value: mapper(value)) 23 | case .Insert(let value, let index): 24 | result = Operation.Insert(value: mapper(value), atIndex: index) 25 | case .Update(let value, let index): 26 | result = Operation.Update(value: mapper(value), atIndex: index) 27 | case .RemoveElement(let index): 28 | result = Operation.RemoveElement(atIndex: index) 29 | } 30 | return result 31 | } 32 | 33 | public var debugDescription: String { 34 | let description: String 35 | switch self { 36 | case .Append(let value): 37 | description = ".Append(value:\(value))" 38 | case .Insert(let value, let index): 39 | description = ".Insert(value: \(value), atIndex:\(index))" 40 | case .Update(let value, let index): 41 | description = ".Update(value: \(value), atIndex:\(index))" 42 | case .RemoveElement(let index): 43 | description = ".RemoveElement(atIndex:\(index))" 44 | } 45 | return description 46 | } 47 | 48 | public var value: T? { 49 | switch self { 50 | case .Append(let value): 51 | return value 52 | case .Insert(let value, _): 53 | return value 54 | case .Update(let value, _): 55 | return value 56 | default: 57 | return Optional.None 58 | } 59 | } 60 | 61 | } 62 | 63 | public func ==(lhs: Operation, rhs: Operation) -> Bool { 64 | switch (lhs, rhs) { 65 | case (.Append(let leftValue), .Append(let rightValue)): 66 | return leftValue == rightValue 67 | case (.Insert(let leftValue, let leftIndex), .Insert(let rightValue, let rightIndex)): 68 | return leftIndex == rightIndex && leftValue == rightValue 69 | case (.Update(let leftValue, let leftIndex), .Update(let rightValue, let rightIndex)): 70 | return leftIndex == rightIndex && leftValue == rightValue 71 | case (.RemoveElement(let leftIndex), .RemoveElement(let rightIndex)): 72 | return leftIndex == rightIndex 73 | default: 74 | return false 75 | } 76 | } 77 | 78 | // WTF!!! Again this is needed because the compiler is super stupid! 79 | public func !=(lhs: Operation, rhs: Operation) -> Bool { 80 | return !(lhs == rhs) 81 | } 82 | 83 | // This is needed because somehow the compiler does not realize 84 | // that when T is equatable it can compare an array of operations. 85 | public func ==(lhs: [Operation], rhs: [Operation]) -> Bool { 86 | let areEqual: () -> Bool = { 87 | for var i = 0; i < lhs.count; i++ { 88 | if lhs[i] != rhs[i] { 89 | return false 90 | } 91 | } 92 | return true 93 | } 94 | return lhs.count == rhs.count && areEqual() 95 | } -------------------------------------------------------------------------------- /ReactiveArray/ReactiveArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // ReactiveArray.h 3 | // ReactiveArray 4 | // 5 | // Created by Guido Marucci Blas on 6/29/15. 6 | // Copyright (c) 2015 Wolox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ReactiveArray. 12 | FOUNDATION_EXPORT double ReactiveArrayVersionNumber; 13 | 14 | //! Project version string for ReactiveArray. 15 | FOUNDATION_EXPORT const unsigned char ReactiveArrayVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /ReactiveArray/ReactiveArray.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReactiveArray.swift 3 | // ReactiveArray 4 | // 5 | // Created by Guido Marucci Blas on 6/29/15. 6 | // Copyright (c) 2015 Wolox. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // 12 | // ReactiveArray.swift 13 | // WLXViewModel 14 | // 15 | // Created by Guido Marucci Blas on 6/15/15. 16 | // Copyright (c) 2015 Wolox. All rights reserved. 17 | // 18 | 19 | import Foundation 20 | import ReactiveCocoa 21 | import Result 22 | 23 | public final class ReactiveArray: CollectionType, MutableCollectionType, CustomDebugStringConvertible { 24 | 25 | public typealias OperationProducer = SignalProducer, NoError> 26 | public typealias OperationSignal = Signal, NoError> 27 | 28 | private var _elements: Array = [] 29 | 30 | private let (_signal, _sink) = OperationSignal.pipe() 31 | public var signal: OperationSignal { 32 | return _signal 33 | } 34 | 35 | public var producer: OperationProducer { 36 | let appendCurrentElements = OperationProducer(values:_elements.map { Operation.Append(value: $0) }) 37 | let forwardOperations = OperationProducer { (observer, dispoable) in self._signal.observe(observer) } 38 | 39 | return appendCurrentElements.concat(forwardOperations) 40 | } 41 | 42 | private let _mutableCount: MutableProperty 43 | public let observableCount:AnyProperty 44 | 45 | public var isEmpty: Bool { 46 | return _elements.isEmpty 47 | } 48 | 49 | public var count: Int { 50 | return _elements.count 51 | } 52 | 53 | public var startIndex: Int { 54 | return _elements.startIndex 55 | } 56 | 57 | public var endIndex: Int { 58 | return _elements.endIndex 59 | } 60 | 61 | public var first: T? { 62 | return _elements.first 63 | } 64 | 65 | public var last: T? { 66 | let value: T? 67 | if _elements.count > 0 { 68 | value = _elements[_elements.count - 1] 69 | } else { 70 | value = Optional.None 71 | } 72 | return value 73 | } 74 | 75 | public var debugDescription: String { 76 | return _elements.debugDescription 77 | } 78 | 79 | public init(elements:[T]) { 80 | _elements = elements 81 | _mutableCount = MutableProperty(elements.count) 82 | observableCount = AnyProperty(_mutableCount) 83 | 84 | _signal.observe { [unowned self](event) in 85 | if case .Next(let operation) = event { 86 | self.updateArray(operation) 87 | } 88 | } 89 | 90 | } 91 | 92 | public convenience init(producer: OperationProducer) { 93 | self.init() 94 | 95 | producer.start(_sink) 96 | } 97 | 98 | public convenience init() { 99 | self.init(elements: []) 100 | } 101 | 102 | public subscript(index: Int) -> T { 103 | get { 104 | return _elements[index] 105 | } 106 | set(newValue) { 107 | update(newValue, atIndex: index) 108 | } 109 | } 110 | 111 | public func append(element: T) { 112 | let operation: Operation = .Append(value: element) 113 | _sink.sendNext(operation) 114 | } 115 | 116 | public func insert(newElement: T, atIndex index : Int) { 117 | let operation: Operation = .Insert(value: newElement, atIndex: index) 118 | _sink.sendNext(operation) 119 | } 120 | 121 | public func update(element: T, atIndex index: Int) { 122 | let operation: Operation = .Update(value: element, atIndex: index) 123 | _sink.sendNext(operation) 124 | } 125 | 126 | public func removeAtIndex(index:Int) { 127 | let operation: Operation = .RemoveElement(atIndex: index) 128 | _sink.sendNext(operation) 129 | } 130 | 131 | public func mirror(transformer: T -> U) -> ReactiveArray { 132 | return ReactiveArray(producer: producer.map { $0.map(transformer) }) 133 | } 134 | 135 | public func toArray() -> Array { 136 | return _elements 137 | } 138 | 139 | private func updateArray(operation: Operation) { 140 | switch operation { 141 | case .Append(let value): 142 | _elements.append(value) 143 | _mutableCount.value = _elements.count 144 | case .Insert(let value, let index): 145 | _elements.insert(value, atIndex: index) 146 | _mutableCount.value = _elements.count 147 | case .Update(let value, let index): 148 | _elements[index] = value 149 | case .RemoveElement(let index): 150 | _elements.removeAtIndex(index) 151 | _mutableCount.value = _elements.count 152 | } 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /ReactiveArrayTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 0.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 0.2.0 23 | 24 | 25 | -------------------------------------------------------------------------------- /ReactiveArrayTests/OperationSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperationSpec.swift 3 | // ReactiveArray 4 | // 5 | // Created by Guido Marucci Blas on 7/2/15. 6 | // Copyright (c) 2015 Wolox. All rights reserved. 7 | // 8 | 9 | import Quick 10 | import Nimble 11 | import ReactiveArray 12 | import ReactiveCocoa 13 | 14 | class OperationSpec: QuickSpec { 15 | 16 | override func spec() { 17 | 18 | var operation: Operation! 19 | 20 | describe("#map") { 21 | 22 | context("when the operation is an Append operation") { 23 | 24 | beforeEach { 25 | operation = Operation.Append(value: 10) 26 | } 27 | 28 | it("maps the value to be appended") { 29 | let mappedOperation = operation.map { $0 * 2 } 30 | 31 | let areEqual = mappedOperation == Operation.Append(value: 20) 32 | expect(areEqual).to(beTrue()) 33 | } 34 | 35 | } 36 | 37 | context("when the operation is an Insert operation") { 38 | 39 | beforeEach { 40 | operation = Operation.Insert(value: 10, atIndex: 5) 41 | } 42 | 43 | it("maps the value to be inserted") { 44 | let mappedOperation = operation.map { $0 * 2 } 45 | 46 | let areEqual = mappedOperation == Operation.Insert(value: 20, atIndex: 5) 47 | expect(areEqual).to(beTrue()) 48 | } 49 | 50 | } 51 | 52 | context("when the operation is an Update operation") { 53 | 54 | beforeEach { 55 | operation = Operation.Update(value: 10, atIndex: 5) 56 | } 57 | 58 | it("maps the value to be updated") { 59 | let mappedOperation = operation.map { $0 * 2 } 60 | 61 | let areEqual = mappedOperation == Operation.Update(value: 20, atIndex: 5) 62 | expect(areEqual).to(beTrue()) 63 | } 64 | 65 | } 66 | 67 | context("when the operation is a Delete operation") { 68 | 69 | beforeEach { 70 | operation = Operation.RemoveElement(atIndex: 5) 71 | } 72 | 73 | it("does nothing") { 74 | let mappedOperation = operation.map { $0 * 2 } 75 | 76 | let areEqual = mappedOperation == operation 77 | expect(areEqual).to(beTrue()) 78 | } 79 | 80 | } 81 | 82 | } 83 | 84 | describe("#value") { 85 | 86 | context("when the operation is an Append operation") { 87 | 88 | beforeEach { 89 | operation = Operation.Append(value: 10) 90 | } 91 | 92 | it("returns the appended value") { 93 | expect(operation.value).to(equal(10)) 94 | } 95 | 96 | } 97 | 98 | context("when the operation is an Insert operation") { 99 | 100 | beforeEach { 101 | operation = Operation.Insert(value: 10, atIndex: 5) 102 | } 103 | 104 | it("returns the inserted value") { 105 | expect(operation.value).to(equal(10)) 106 | } 107 | 108 | } 109 | 110 | context("when the operation is an Update operation") { 111 | 112 | beforeEach { 113 | operation = Operation.Update(value: 10, atIndex: 5) 114 | } 115 | 116 | it("returns the updated value") { 117 | expect(operation.value).to(equal(10)) 118 | } 119 | 120 | } 121 | 122 | context("when the operation is an Remove operation") { 123 | 124 | beforeEach { 125 | operation = Operation.RemoveElement(atIndex: 5) 126 | } 127 | 128 | it("returns .None") { 129 | expect(operation.value).to(beNil()) 130 | } 131 | 132 | } 133 | 134 | } 135 | 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /ReactiveArrayTests/ReactiveArraySpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReactiveArraySpec.swift 3 | // ReactiveArraySpec 4 | // 5 | // Created by Guido Marucci Blas on 6/29/15. 6 | // Copyright (c) 2015 Wolox. All rights reserved. 7 | // 8 | 9 | import Quick 10 | import Nimble 11 | import ReactiveArray 12 | import ReactiveCocoa 13 | 14 | private func waitForOperation(fromProducer producer: SignalProducer, NoError>, 15 | when: () -> (), 16 | onAppend: T -> () = { fail("Invalid operation type: .Append(\($0))") }, 17 | onInsert: (T, Int) -> () = { fail("Invalid operation type: .Insert(\($0), \($1))") }, 18 | onUpdate: (T, Int) -> () = { fail("Invalid operation type: .Update(\($0), \($1))") }, 19 | onDelete: Int -> () = { fail("Invalid operation type: .Delete(\($0))") }) { 20 | 21 | waitUntil(timeout: 10.0) { done in 22 | producer.startWithNext { operation in 23 | switch operation { 24 | case .Append(let value): 25 | onAppend(value) 26 | case .Insert(let value, let index): 27 | onInsert(value, index) 28 | case .Update(let value, let index): 29 | onUpdate(value, index) 30 | case .RemoveElement(let index): 31 | onDelete(index) 32 | } 33 | done() 34 | } 35 | when() 36 | } 37 | 38 | } 39 | 40 | private func waitForOperation(fromSignal signal: Signal, NoError>, 41 | when: () -> (), 42 | onAppend: T -> () = { fail("Invalid operation type: .Append(\($0))") }, 43 | onInsert: (T, Int) -> () = { fail("Invalid operation type: .Insert(\($0), \($1))") }, 44 | onUpdate: (T, Int) -> () = { fail("Invalid operation type: .Update(\($0), \($1))") }, 45 | onDelete: Int -> () = { fail("Invalid operation type: .Delete(\($0))") }) { 46 | 47 | let producer = SignalProducer, NoError> { (observer, disposable) in signal.observe(observer) } 48 | waitForOperation(fromProducer: producer, when: when, onAppend: onAppend, onInsert: onInsert, onUpdate: onUpdate, onDelete: onDelete) 49 | } 50 | 51 | private func waitForOperation(fromArray array: ReactiveArray, 52 | when: () -> (), 53 | onAppend: T -> () = { fail("Invalid operation type: .Append(\($0))") }, 54 | onInsert: (T, Int) -> () = { fail("Invalid operation type: .Insert(\($0), \($1))") }, 55 | onUpdate: (T, Int) -> () = { fail("Invalid operation type: .Update(\($0), \($1))") }, 56 | onDelete: Int -> () = { fail("Invalid operation type: .Delete(\($0))") }) { 57 | 58 | waitForOperation(fromSignal: array.signal, when: when, onAppend: onAppend, onInsert: onInsert, onUpdate: onUpdate, onDelete: onDelete) 59 | } 60 | 61 | class ReactiveArraySpec: QuickSpec { 62 | 63 | override func spec() { 64 | 65 | var array: ReactiveArray! 66 | 67 | beforeEach { 68 | array = ReactiveArray(elements: [1,2,3,4]) 69 | } 70 | 71 | describe("#append") { 72 | 73 | it("inserts the given element at the end of the array") { 74 | array.append(5) 75 | 76 | expect(array[array.count - 1]).to(equal(5)) 77 | } 78 | 79 | it("increments the amount of elements in the array by one") { 80 | let countBeforeAppend = array.count 81 | 82 | array.append(5) 83 | 84 | expect(array.count).to(equal(countBeforeAppend + 1)) 85 | } 86 | 87 | it("signals an append operation") { 88 | waitForOperation( 89 | fromArray: array, 90 | when: { 91 | array.append(5) 92 | }, 93 | onAppend: { value in 94 | expect(value).to(equal(5)) 95 | } 96 | ) 97 | } 98 | 99 | } 100 | 101 | describe("#insert") { 102 | 103 | context("when there is a value at the given position") { 104 | 105 | it("replaces the old value with the new one") { 106 | array.insert(5, atIndex: 1) 107 | 108 | expect(array[1]).to(equal(5)) 109 | expect(array.toArray()).to(equal([1,5,2,3,4])) 110 | } 111 | 112 | it("signals an insert operation") { 113 | waitForOperation( 114 | fromArray: array, 115 | when: { 116 | array.insert(5, atIndex: 1) 117 | }, 118 | onInsert: { (value, index) in 119 | expect(value).to(equal(5)) 120 | expect(index).to(equal(1)) 121 | } 122 | ) 123 | } 124 | 125 | } 126 | 127 | // TODO: Fix this case because this raises an exception that cannot 128 | // be caught 129 | // context("when the index is out of bounds") { 130 | // 131 | // it("raises an exception") { 132 | // expect { 133 | // array.insert(5, atIndex: array.count + 10) 134 | // }.to(raiseException(named: "NSInternalInconsistencyException")) 135 | // } 136 | // 137 | // } 138 | 139 | } 140 | 141 | describe("#removeAtIndex") { 142 | 143 | it("removes the element at the given position") { 144 | array.removeAtIndex(1) 145 | 146 | expect(array.toArray()).to(equal([1,3,4])) 147 | } 148 | 149 | it("signals a delete operation") { 150 | waitForOperation( 151 | fromArray: array, 152 | when: { 153 | array.removeAtIndex(1) 154 | }, 155 | onDelete: { index in 156 | expect(index).to(equal(1)) 157 | } 158 | ) 159 | } 160 | 161 | 162 | } 163 | 164 | describe("#[]") { 165 | 166 | it("returns the element at the given position") { 167 | expect(array[2]).to(equal(3)) 168 | } 169 | 170 | } 171 | 172 | describe("#[]=") { 173 | 174 | context("when there is a value at the given position") { 175 | 176 | it("replaces the old value with the new one") { 177 | array[1] = 5 178 | 179 | expect(array[1]).to(equal(5)) 180 | expect(array.toArray()).to(equal([1,5,3,4])) 181 | } 182 | 183 | it("signals an update operation") { 184 | waitForOperation( 185 | fromArray: array, 186 | when: { 187 | array[1] = 5 188 | }, 189 | onUpdate: { (value, index) in 190 | expect(value).to(equal(5)) 191 | expect(index).to(equal(1)) 192 | } 193 | ) 194 | } 195 | 196 | } 197 | 198 | } 199 | 200 | describe("#mirror") { 201 | 202 | var mirror: ReactiveArray! 203 | 204 | beforeEach { 205 | mirror = array.mirror { $0 + 10 } 206 | } 207 | 208 | it("returns a new reactive array that maps the values of the original array") { 209 | expect(mirror.toArray()).to(equal([11, 12, 13, 14])) 210 | } 211 | 212 | context("when a insert is executed on the original array") { 213 | 214 | it("signals a mapped insert operation") { 215 | waitForOperation( 216 | fromArray: mirror, 217 | when: { 218 | array[1] = 5 219 | }, 220 | onUpdate: { (value, index) in 221 | expect(value).to(equal(15)) 222 | expect(index).to(equal(1)) 223 | } 224 | ) 225 | } 226 | 227 | } 228 | 229 | context("when an append is executed on the original array") { 230 | 231 | it("signals a mapped append operation") { 232 | waitForOperation( 233 | fromArray: mirror, 234 | when: { 235 | array.append(5) 236 | }, 237 | onAppend: { value in 238 | expect(value).to(equal(15)) 239 | } 240 | ) 241 | } 242 | 243 | } 244 | 245 | context("when a delete is executed on the original array") { 246 | 247 | it("signals a mapped delete operation") { 248 | waitForOperation( 249 | fromArray: mirror, 250 | when: { 251 | array.removeAtIndex(1) 252 | }, 253 | onDelete: { index in 254 | expect(index).to(equal(1)) 255 | } 256 | ) 257 | } 258 | 259 | } 260 | 261 | } 262 | 263 | describe("#producer") { 264 | 265 | context("when the array has elements") { 266 | 267 | it("signals an append operation for each stored element") { 268 | waitUntil { done in 269 | array.producer 270 | .take(array.count) 271 | .collect() 272 | .startWithNext { operations in 273 | let expectedOperations: [Operation] = array.map { Operation.Append(value: $0) } 274 | let result = operations == expectedOperations 275 | expect(result).to(beTrue()) 276 | done() 277 | } 278 | } 279 | } 280 | 281 | } 282 | 283 | context("when an append operation is executed in the original array") { 284 | 285 | it("forwards the operation") { 286 | let a = ReactiveArray() 287 | 288 | waitForOperation( 289 | fromProducer: a.producer, 290 | when: { 291 | a.append(5) 292 | }, 293 | onAppend: { value in 294 | expect(value).to(equal(5)) 295 | } 296 | ) 297 | } 298 | 299 | } 300 | 301 | context("when an insert operation is executed in the original array") { 302 | 303 | it("forwards the operation") { 304 | let a = ReactiveArray(elements: [1]) 305 | 306 | waitForOperation( 307 | fromProducer: a.producer.skip(1), // Skips the operation triggered due to the array not being empty 308 | when: { 309 | a.insert(5, atIndex: 0) 310 | }, 311 | onInsert: { (value, index) in 312 | expect(value).to(equal(5)) 313 | expect(index).to(equal(0)) 314 | } 315 | ) 316 | } 317 | 318 | } 319 | 320 | context("when a delete operation is executed in the original array") { 321 | 322 | it("forwards the operation") { 323 | let a = ReactiveArray(elements: [1]) 324 | 325 | waitForOperation( 326 | fromProducer: a.producer.skip(1), // Skips the operation triggered due to the array not being empty 327 | when: { 328 | a.removeAtIndex(0) 329 | }, 330 | onDelete: { index in 331 | expect(index).to(equal(0)) 332 | } 333 | ) 334 | } 335 | 336 | } 337 | 338 | } 339 | 340 | describe("#signal") { 341 | 342 | context("when an insert operation is executed") { 343 | 344 | it("signals the operations") { 345 | waitForOperation( 346 | fromSignal: array.signal, 347 | when: { 348 | array.insert(5, atIndex: 1) 349 | }, 350 | onInsert: { (value, index) in 351 | expect(value).to(equal(5)) 352 | expect(index).to(equal(1)) 353 | } 354 | ) 355 | } 356 | 357 | } 358 | 359 | context("when an append operation is executed") { 360 | 361 | it("signals the operations") { 362 | waitForOperation( 363 | fromSignal: array.signal, 364 | when: { 365 | array.append(5) 366 | }, 367 | onAppend: { value in 368 | expect(value).to(equal(5)) 369 | } 370 | ) 371 | } 372 | 373 | } 374 | 375 | context("when an update operation is executed") { 376 | 377 | it("signals the operations") { 378 | waitForOperation( 379 | fromSignal: array.signal, 380 | when: { 381 | array[1] = 5 382 | }, 383 | onUpdate: { (value, index) in 384 | expect(value).to(equal(5)) 385 | expect(index).to(equal(1)) 386 | } 387 | ) 388 | } 389 | 390 | } 391 | 392 | context("when a delete operation is executed") { 393 | 394 | it("signals the operations") { 395 | waitForOperation( 396 | fromSignal: array.signal, 397 | when: { 398 | array.removeAtIndex(1) 399 | }, 400 | onDelete: { index in 401 | expect(index).to(equal(1)) 402 | } 403 | ) 404 | } 405 | 406 | } 407 | 408 | } 409 | 410 | describe("observableCount") { 411 | 412 | var countBeforeOperation: Int! 413 | var producer: SignalProducer! 414 | 415 | beforeEach { 416 | countBeforeOperation = array.count 417 | producer = array.observableCount.producer 418 | } 419 | 420 | it("returns the initial amount of elements in the array") { 421 | producer.startWithNext { count in 422 | expect(count).to(equal(countBeforeOperation)) 423 | } 424 | } 425 | 426 | context("when an insert operation is executed") { 427 | 428 | beforeEach { 429 | producer = producer.skip(1) 430 | } 431 | 432 | it("updates the count") { 433 | waitUntil { done in 434 | producer 435 | .take(2) 436 | .collect() 437 | .startWithNext { counts in 438 | expect(counts).to(equal([countBeforeOperation + 1, countBeforeOperation + 2])) 439 | done() 440 | } 441 | 442 | array.insert(657, atIndex: 1) 443 | array.append(656) 444 | } 445 | } 446 | 447 | } 448 | 449 | context("when an update operation is executed") { 450 | 451 | beforeEach { 452 | producer = producer.skip(1) 453 | } 454 | 455 | it("does not update the count") { 456 | waitUntil { done in 457 | array[1] = 656 458 | expect(array.count).to(equal(countBeforeOperation)) 459 | done() 460 | } 461 | } 462 | 463 | } 464 | 465 | context("when an append operation is executed") { 466 | 467 | beforeEach { 468 | producer = producer.skip(1) 469 | } 470 | 471 | it("updates the count") { 472 | waitUntil { done in 473 | producer.startWithNext { count in 474 | expect(count).to(equal(countBeforeOperation + 1)) 475 | done() 476 | } 477 | 478 | array.append(656) 479 | } 480 | } 481 | 482 | } 483 | 484 | context("when a delete operation is executed") { 485 | 486 | beforeEach { 487 | producer = producer.skip(1) 488 | } 489 | 490 | it("updates the count") { 491 | waitUntil { done in 492 | producer.startWithNext { count in 493 | expect(count).to(equal(countBeforeOperation - 1)) 494 | done() 495 | } 496 | 497 | array.removeAtIndex(1) 498 | } 499 | } 500 | 501 | } 502 | 503 | } 504 | 505 | describe("isEmpty") { 506 | 507 | context("when the array is empty") { 508 | 509 | it("returns true") { 510 | expect(ReactiveArray().isEmpty).to(beTrue()) 511 | } 512 | 513 | } 514 | 515 | context("when the array is not empty") { 516 | 517 | it("returns false") { 518 | expect(array.isEmpty).to(beFalse()) 519 | } 520 | 521 | } 522 | 523 | } 524 | 525 | describe("count") { 526 | 527 | it("returns the amount of elements in the array") { 528 | expect(array.count).to(equal(4)) 529 | } 530 | 531 | } 532 | 533 | describe("startIndex") { 534 | 535 | context("when the array is not empty") { 536 | 537 | it("returns the index of the first element") { 538 | expect(array.startIndex).to(equal(0)) 539 | } 540 | 541 | } 542 | 543 | context("when the array is empty") { 544 | 545 | beforeEach { 546 | array = ReactiveArray() 547 | } 548 | 549 | it("returns the index of the first element") { 550 | expect(array.startIndex).to(equal(0)) 551 | } 552 | 553 | } 554 | 555 | } 556 | 557 | describe("endIndex") { 558 | 559 | context("when the array is not empty") { 560 | 561 | it("returns the index of the last element plus one") { 562 | expect(array.endIndex).to(equal(array.count)) 563 | } 564 | 565 | } 566 | 567 | context("when the array is empty") { 568 | 569 | beforeEach { 570 | array = ReactiveArray() 571 | } 572 | 573 | it("returns zero") { 574 | expect(array.startIndex).to(equal(0)) 575 | } 576 | 577 | } 578 | 579 | } 580 | 581 | describe("first") { 582 | 583 | it("returns the first element in the array") { 584 | expect(array.first).to(equal(1)) 585 | } 586 | 587 | } 588 | 589 | describe("last") { 590 | 591 | it("returns the last element in the array") { 592 | expect(array.last).to(equal(4)) 593 | } 594 | 595 | context("when the array is empty") { 596 | 597 | beforeEach { 598 | array = ReactiveArray() 599 | } 600 | 601 | it("returns .None") { 602 | expect(array.last).to(beNil()) 603 | } 604 | 605 | } 606 | 607 | } 608 | 609 | } 610 | 611 | } 612 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | xcode: 3 | version: "6.3.1" 4 | test: 5 | override: 6 | - script/cibuild 7 | -------------------------------------------------------------------------------- /coverage/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wolox/ReactiveArray/9150465a31a48b65ba1981f7f308aa1273c371e9/coverage/.gitkeep -------------------------------------------------------------------------------- /script/.env: -------------------------------------------------------------------------------- 1 | REQUIRED_CARTHAGE_VERSION=0.9.4 2 | CARTHAGE_NO_USE_BINARIES=\${CARTHAGE_NO_USE_BINARIES:-"false"} 3 | CARTHAGE_BUILD_PLATFORM=${CARTHAGE_BUILD_PLATFORM:-"iOS"} 4 | PROJECT_NAME=ReactiveArray 5 | XCODE_WORKSPACE= 6 | XCODE_PROJECT=ReactiveArray.xcodeproj 7 | IOS_DESTINATION_VERSION=${IOS_DESTINATION_VERSION:-"latest"} 8 | IOS_DESTINATION_SIMULATOR_NAME=${IOS_DESTINATION_SIMULATOR_NAME:-"iPhone 6"} 9 | OSX_DESTINATION_ARCH=${OSX_DESTINATION_ARCH:-""} 10 | INSTALL_GITHOOKS=true 11 | -------------------------------------------------------------------------------- /script/.env.template: -------------------------------------------------------------------------------- 1 | PROJECT_NAME= 2 | 3 | # xcodebuild settings 4 | XCODE_SCHEME= 5 | XCODE_PROJECT= 6 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | install_homebrew () 6 | { 7 | if [ -z $GITHUB_ACCESS_TOKEN ] 8 | then 9 | export HOMEBREW_GITHUB_API_TOKEN=$GITHUB_ACCESS_TOKEN 10 | fi 11 | 12 | if type brew > /dev/null 13 | then 14 | echo " ✔ brew is already installed" 15 | 16 | if [ -z "$SKIP_BREW_FORMULAS_UPDATE" ] 17 | then 18 | echo "" 19 | echo " → Updating homebrew formulas" 20 | brew update > /dev/null || brew update > /dev/null 21 | echo " ✔ formulas updated" 22 | fi 23 | else 24 | command -v ruby >/dev/null 2>&1 || { echo >&2 "Error: Some ruby of version is required to install homebrew. Aborting"; exit 1; } 25 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 26 | fi 27 | } 28 | 29 | # param $1 formula name 30 | # param $2 [optional] tap path 31 | brew_install () 32 | { 33 | formula_version=`brew list --versions $1` 34 | if [ -z "$formula_version" ] 35 | then 36 | if [ -z $2 ] 37 | then 38 | formula_name=$1 39 | else 40 | formula_name="$2/$1" 41 | fi 42 | echo "" 43 | echo " → Installing brew formula $formula_name" 44 | brew install -v $formula_name > /dev/null 2>&1 45 | 46 | # Extract version 47 | regexp="^.*([0-9]\.[0-9]\.[0-9]).*$" 48 | installed_version="" 49 | eval "output=\"$(brew info $1)\"" 50 | if [[ $output =~ $regexp ]] 51 | then 52 | installed_version=${BASH_REMATCH[1]} 53 | fi 54 | 55 | echo " ✔ $formula_name $installed_version has been installed" 56 | else 57 | echo " ✔ $1 is already installed" 58 | fi 59 | } 60 | 61 | print_gem_install_cmd () 62 | { 63 | regexp="gem ['\"]([a-zA-Z0-9_-]+)['\"](,.*)?" 64 | gems="" 65 | while read -r line 66 | do 67 | if [[ $line =~ $regexp ]] 68 | then 69 | gems="$gems ${BASH_REMATCH[1]}" 70 | fi 71 | done < Gemfile 72 | 73 | echo "" 74 | echo " $> 'sudo gem install$gems'" 75 | echo "" 76 | } 77 | 78 | bundle_install () 79 | { 80 | echo "" 81 | echo " → Installing gems" 82 | echo "" 83 | if type bundle > /dev/null 84 | then 85 | bundle install 86 | else 87 | # TODO ask user if he/she wants the script to try to install 88 | # rbenv, ruby and bundler. 89 | printf "\033[1;33m⚠ WARNING: Ruby gems in Gemfile could not be installed because 'bundler' is not available.\n" \ 90 | "You should install rbenv or rvm and bundler" \ 91 | "or try to install the gems globally by running the following command:" 92 | print_gem_install_cmd 93 | printf "\033[0m" 94 | exit 1 95 | fi 96 | } 97 | 98 | install_git_hooks () 99 | { 100 | if [ ! -z "$INSTALL_GITHOOKS" ] 101 | then 102 | echo "" 103 | echo " → Installing git hooks" 104 | echo "" 105 | for hook in script/git_hooks/* 106 | do 107 | cp $hook .git/hooks 108 | echo " ✔ $hook successfully installed" 109 | done 110 | echo "" 111 | fi 112 | } 113 | 114 | bootstrap_carthage () 115 | { 116 | echo "" 117 | echo " → Bootstrapping Carthage" 118 | echo "" 119 | carthage_cmd="carthage bootstrap --platform $CARTHAGE_BUILD_PLATFORM" 120 | 121 | if [ "$USE_SSH" == "true" ] 122 | then 123 | carthage_cmd="$carthage_cmd --use-ssh" 124 | fi 125 | 126 | if [ "$USE_SUBMODULES" == "true" ] 127 | then 128 | carthage_cmd="$carthage_cmd --use-submodules --no-build" 129 | fi 130 | 131 | if [ "$CARTHAGE_NO_USE_BINARIES" == "true" ] 132 | then 133 | carthage_cmd="$carthage_cmd --no-use-binaries" 134 | fi 135 | 136 | if [ -z $DISABLE_CARTHAGE_CACHE ] && type bundle > /dev/null && bundle show carthage_cache > /dev/null && ([ ! -z $DISABLE_CARTHAGE_CACHE_CONFIG ] || [ -f .carthage_cache.yml ]) 137 | then 138 | carthage_cache_cmd="carthage_cache" 139 | if [ ! -z $CARTHAGE_CACHE_BUCKET_NAME ] 140 | then 141 | carthage_cache_cmd="$carthage_cache_cmd -b $CARTHAGE_CACHE_BUCKET_NAME" 142 | fi 143 | carthage_cmd="(bundle exec $carthage_cache_cmd install || $carthage_cmd) && bundle exec $carthage_cache_cmd publish" 144 | fi 145 | 146 | carthage_cmd="time $carthage_cmd" 147 | if [ ! -z "$VERBOSE" ] 148 | then 149 | echo $carthage_cmd 150 | fi 151 | 152 | eval $carthage_cmd 153 | } 154 | 155 | bootstrap_cocoapods () 156 | { 157 | echo "" 158 | echo " → Bootstrapping Cocoapods" 159 | echo "" 160 | if type bundle > /dev/null && bundle show pod > /dev/null 161 | then 162 | bundle exec pod install 163 | else 164 | pod install 165 | fi 166 | } 167 | 168 | echo_submodule_name () 169 | { 170 | echo " ✔ $name successfully initialized" 171 | } 172 | 173 | init_submodules () 174 | { 175 | echo "" 176 | echo " → Initializing submodules ..." 177 | echo "" 178 | git submodule update --quiet --init --recursive > /dev/null 179 | git submodule foreach --quiet echo_submodule_name 180 | } 181 | 182 | install_dependencies () 183 | { 184 | echo "" 185 | echo " → Installing dependencies" 186 | echo "" 187 | install_homebrew 188 | brew_install "xcode-coveralls" "macmade/tap" 189 | 190 | if [ -f script/script_hooks/bootstrap ] && [ -z $DISABLE_BOOTSTRAP_HOOKS ] 191 | then 192 | script/script_hooks/bootstrap 193 | fi 194 | } 195 | 196 | install_carthage () 197 | { 198 | source script/common/carthage 199 | 200 | if type carthage > /dev/null 201 | then 202 | echo "" 203 | echo " → Checking installed version of carthage" 204 | echo "" 205 | check_carthage_version 206 | else 207 | force_install_carthage 208 | fi 209 | 210 | if [ -z "$DISABLE_CARTHAGE_CACHE_CONFIG" ] && type bundle > /dev/null && bundle show carthage_cache > /dev/null && [ ! -f .carthage_cache.yml ] 211 | then 212 | bundle exec carthage_cache config 213 | fi 214 | } 215 | 216 | main () 217 | { 218 | source script/.env 219 | 220 | echo "" 221 | echo " Bootstrapping $PROJECT_NAME" 222 | echo "" 223 | 224 | install_git_hooks 225 | install_dependencies 226 | 227 | if [ -f Gemfile ] 228 | then 229 | bundle_install 230 | fi 231 | 232 | if [ -f Cartfile.resolved ] 233 | then 234 | install_carthage 235 | bootstrap_carthage 236 | fi 237 | 238 | if [ -f Podfile ] 239 | then 240 | bootstrap_cocoapods 241 | fi 242 | 243 | if [ -f .gitmodules ] 244 | then 245 | init_submodules 246 | fi 247 | 248 | open_file_name="" 249 | if [ -z "$XCODE_WORKSPACE" ] 250 | then 251 | open_file_name=$XCODE_PROJECT 252 | else 253 | open_file_name=$XCODE_WORKSPACE 254 | fi 255 | 256 | echo "" 257 | echo " $PROJECT_NAME successfully bootstrapped" 258 | echo "" 259 | echo " Usefull scripts:" 260 | echo "" 261 | echo " * 'script/test' to run tests." 262 | echo " * 'script/build' to build the project." 263 | echo " * 'script/update' to update project's dependencies." 264 | echo "" 265 | echo " You can start hacking by executing:" 266 | echo "" 267 | echo " open $open_file_name" 268 | echo "" 269 | } 270 | 271 | export -f init_submodules 272 | export -f echo_submodule_name 273 | export -f brew_install 274 | 275 | main 276 | -------------------------------------------------------------------------------- /script/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source script/common/carthage 6 | 7 | build_using_carthage () 8 | { 9 | check_carthage_version 10 | carthage_cmd="carthage build --no-skip-current --platform $CARTHAGE_BUILD_PLATFORM" 11 | if [ "$USE_SSH" == "true" ] 12 | then 13 | carthage_cmd="$carthage_cmd --use-ssh" 14 | fi 15 | if [ ! -z "$VERBOSE" ] 16 | then 17 | echo $carthage_cmd 18 | fi 19 | eval $carthage_cmd 20 | } 21 | 22 | build_using_xcodebuild () 23 | { 24 | build_command="set -o pipefail && xcodebuild -scheme $1" 25 | if [ -z "$XCODE_WORKSPACE" ] 26 | then 27 | build_command="$build_command -project $XCODE_PROJECT" 28 | else 29 | build_command="$build_command -workspace $XCODE_WORKSPACE" 30 | fi 31 | build_command="$build_command -sdk iphonesimulator build -configuration Debug" 32 | 33 | if type bundle > /dev/null && bundle show xcpretty > /dev/null 34 | then 35 | build_command="$build_command | xcpretty -c" 36 | fi 37 | 38 | echo "" 39 | echo " → Building scheme '$1'" 40 | echo "" 41 | if [ ! -z "$VERBOSE" ] 42 | then 43 | echo $build_command 44 | fi 45 | eval $build_command 46 | } 47 | 48 | if [ ! -f $XCODE_WORKSPACE ] && [ -f Cartfile.resolved ] && type carthage > /dev/null 49 | then 50 | build_using_carthage 51 | else 52 | source script/.env 53 | source script/script_hooks/schemes 54 | 55 | current_schemes=$(schemes) 56 | if [ -z "$current_schemes" ] 57 | then 58 | echo "" 59 | echo "ERROR: There are no schemes. Probably you forgot to share your schemes" 60 | exit 1 61 | fi 62 | 63 | for scheme in $current_schemes 64 | do 65 | build_using_xcodebuild $scheme 66 | done 67 | fi 68 | -------------------------------------------------------------------------------- /script/certificates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wolox/ReactiveArray/9150465a31a48b65ba1981f7f308aa1273c371e9/script/certificates/.gitkeep -------------------------------------------------------------------------------- /script/certificates/cibot.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wolox/ReactiveArray/9150465a31a48b65ba1981f7f308aa1273c371e9/script/certificates/cibot.p12 -------------------------------------------------------------------------------- /script/cibuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SCRIPT_DIR=$(dirname "$0") 4 | 5 | ## 6 | ## Configuration Variables 7 | ## 8 | 9 | # The name of the keychain to create for iOS code signing. 10 | KEYCHAIN=ios-build.keychain 11 | 12 | ## 13 | ## Build Process 14 | ## 15 | 16 | main () 17 | { 18 | if [ -f Cartfile.resolved ] && [ -f "$SCRIPT_DIR/certificates/cibot.p12" ] 19 | then 20 | echo "" 21 | echo "####### Importing Developer Certificates #######" 22 | echo "" 23 | import_certs 24 | fi 25 | 26 | echo "" 27 | echo "####### Bootstrap Phase #######" 28 | echo "" 29 | DISABLE_CARTHAGE_CACHE_CONFIG=true NO_CARTHAGE_UPDATE=true script/bootstrap 30 | local status=$? 31 | 32 | if [ $status -eq 0 ] 33 | then 34 | echo "" 35 | echo "" 36 | echo "####### Build & Test Phase #######" 37 | echo "" 38 | set -o pipefail && script/test 2>&1 | tee /tmp/build.test-output.txt 39 | status=$? 40 | if [ ! $status -eq 0 ] 41 | then 42 | log_file_path=`cat /tmp/build.test-output.txt | tail -n 100 | perl -l -ne '/(\/var\/folders.*\/com\.apple\.dt\.XCTest-status.*)\)/ && print $1'` 43 | if [ ! -z "$log_file_path" ] 44 | then 45 | echo "" 46 | echo " → The tests have failed. Printing output of log file '$log_file_path'." 47 | cat $log_file_path 48 | echo "" 49 | fi 50 | fi 51 | fi 52 | 53 | if [ -f Cartfile.resolved ] && [ -f "$SCRIPT_DIR/certificates/cibot.p12" ] 54 | then 55 | delete_keychain 56 | fi 57 | exit $status 58 | } 59 | 60 | import_certs () 61 | { 62 | # If this environment variable is missing, we must not be running on Travis. 63 | if [ -z "$KEY_PASSWORD" ] 64 | then 65 | return 0 66 | fi 67 | 68 | echo " → Setting up code signing..." 69 | local password=cibuild 70 | 71 | # Create a temporary keychain for code signing. 72 | security create-keychain -p "$password" "$KEYCHAIN" 73 | security default-keychain -s "$KEYCHAIN" 74 | security unlock-keychain -p "$password" "$KEYCHAIN" 75 | security set-keychain-settings -t 3600 -l "$KEYCHAIN" 76 | 77 | # Download the certificate for the Apple Worldwide Developer Relations 78 | # Certificate Authority. 79 | local certpath="$SCRIPT_DIR/apple_wwdr.cer" 80 | curl -s 'https://developer.apple.com/certificationauthority/AppleWWDRCA.cer' > "$certpath" 81 | security import "$certpath" -k "$KEYCHAIN" -T /usr/bin/codesign 82 | 83 | # Import our development certificate. 84 | security import "$SCRIPT_DIR/certificates/cibot.p12" -k "$KEYCHAIN" -P "$KEY_PASSWORD" -T /usr/bin/codesign 85 | } 86 | 87 | delete_keychain () 88 | { 89 | if [ -z "$KEY_PASSWORD" ] 90 | then 91 | return 0 92 | fi 93 | 94 | echo " → Removing temporary keychain" 95 | security delete-keychain "$KEYCHAIN" 96 | echo " ✔ Temporary keychain successfully removed." 97 | } 98 | 99 | export -f import_certs 100 | export -f delete_keychain 101 | 102 | main 103 | -------------------------------------------------------------------------------- /script/common/carthage: -------------------------------------------------------------------------------- 1 | force_install_carthage () 2 | { 3 | echo "" 4 | echo " → Installing carthage '$REQUIRED_CARTHAGE_VERSION'" 5 | echo "" 6 | curl -s -L -O https://github.com/Carthage/Carthage/releases/download/$REQUIRED_CARTHAGE_VERSION/Carthage.pkg > /dev/null 7 | sudo installer -pkg Carthage.pkg -target / > /dev/null 8 | rm Carthage.pkg 9 | echo " ✔ carthage '$REQUIRED_CARTHAGE_VERSION' successfully installed" 10 | echo "" 11 | } 12 | 13 | uninstall_carthage () 14 | { 15 | echo "" 16 | echo " → Uninstalling carthage" 17 | echo "" 18 | local carthage_installed_version=`carthage version` 19 | if type brew > /dev/null && [ ! -z "$(brew list --versions carthage)" ] 20 | then 21 | brew uninstall carthage > /dev/null 22 | else 23 | sudo rm -frd /Library/Frameworks/CarthageKit.framework 24 | sudo rm /usr/local/bin/carthage 25 | fi 26 | echo " ✔ carthage '$carthage_installed_version' successfully uninstalled" 27 | } 28 | 29 | check_carthage_version () 30 | { 31 | local carthage_installed_version=`carthage version` 32 | local patch_number=`echo $REQUIRED_CARTHAGE_VERSION | cut -d "." -f 3` 33 | if [ -z $patch_number ] 34 | then 35 | REQUIRED_CARTHAGE_VERSION="$REQUIRED_CARTHAGE_VERSION.0" 36 | fi 37 | if [ "$carthage_installed_version" != "$REQUIRED_CARTHAGE_VERSION" ] 38 | then 39 | printf "\033[1;31mError: carthage version '$carthage_installed_version' is not equal to '$REQUIRED_CARTHAGE_VERSION'" 40 | printf "\033[0m" 41 | if [ ! -z "$NO_CARTHAGE_UPDATE" ] 42 | then 43 | exit 1 44 | else 45 | echo "" 46 | echo "" 47 | echo "Would you like to update carthage to version '$REQUIRED_CARTHAGE_VERSION'? [N/y]" 48 | read update_carthage 49 | if [ "$update_carthage" == "y" ] 50 | then 51 | uninstall_carthage 52 | force_install_carthage 53 | else 54 | exit 1 55 | fi 56 | fi 57 | fi 58 | } 59 | -------------------------------------------------------------------------------- /script/common/install_carthage: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source script/common/carthage 3 | 4 | if [ -z $REQUIRED_CARTHAGE_VERSION ] 5 | then 6 | echo "Enter the version of Carthage you want to install:" 7 | read REQUIRED_CARTHAGE_VERSION 8 | fi 9 | force_install_carthage 10 | -------------------------------------------------------------------------------- /script/coverage: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source script/.env 6 | 7 | if type xcode-coveralls > /dev/null 8 | then 9 | if [ ! -f script/xcenv.sh ] 10 | then 11 | # Running the test generates the xcenv.sh 12 | script/test 13 | fi 14 | 15 | if [ -f script/xcenv.sh ] 16 | then 17 | source script/xcenv.sh 18 | declare -r DIR_BUILD="${OBJECT_FILE_DIR_normal}/${CURRENT_ARCH}/" 19 | xcode-coveralls --include $SRCROOT --exclude "$SRCROOT""Tests" --exclude Carthage --exclude Pods --token $COVERALLS_TOKEN "${DIR_BUILD}" 20 | else 21 | # TODO print instruction of how to add the generation of xcenv.sh 22 | echo "" 23 | echo " Error: script/xcenv.sh was not generated after running 'script/test'." 24 | echo "" 25 | exit 1 26 | fi 27 | fi 28 | -------------------------------------------------------------------------------- /script/git_hooks/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # An example hook script to verify what is about to be pushed. Called by "git 4 | # push" after it has checked the remote status, but before anything has been 5 | # pushed. If this script exits with a non-zero status nothing will be pushed. 6 | # 7 | # This hook is called with the following parameters: 8 | # 9 | # $1 -- Name of the remote to which the push is being done 10 | # $2 -- URL to which the push is being done 11 | # 12 | # If pushing without using a named remote those arguments will be equal. 13 | # 14 | # Information about the commits which are being pushed is supplied as lines to 15 | # the standard input in the form: 16 | # 17 | # 18 | # 19 | # This sample shows how to prevent push of commits where the log message starts 20 | # with "WIP" (work in progress). 21 | 22 | set -e 23 | 24 | remote="$1" 25 | url="$2" 26 | 27 | script/test 28 | -------------------------------------------------------------------------------- /script/script_hooks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wolox/ReactiveArray/9150465a31a48b65ba1981f7f308aa1273c371e9/script/script_hooks/.gitkeep -------------------------------------------------------------------------------- /script/script_hooks/destinations: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | IOS_DESTINATION="'platform=iOS Simulator,name=$IOS_DESTINATION_SIMULATOR_NAME,OS=$IOS_DESTINATION_VERSION'" 4 | 5 | if [ -z "$OSX_DESTINATION_ARCH" ] 6 | then 7 | OSX_DESTINATION="'platform=OS X'" 8 | else 9 | OSX_DESTINATION="'platform=OS X,arch=$OSX_DESTINATION_ARCH'" 10 | fi 11 | 12 | scheme_destination () 13 | { 14 | shopt -s nocasematch 15 | 16 | case "$1" in 17 | *iOS) 18 | destination=$IOS_DESTINATION 19 | ;; 20 | *OSX) 21 | destination=$OSX_DESTINATION 22 | ;; 23 | *) 24 | destination=$IOS_DESTINATION 25 | ;; 26 | esac 27 | 28 | echo $destination 29 | } -------------------------------------------------------------------------------- /script/script_hooks/schemes: -------------------------------------------------------------------------------- 1 | schemes () 2 | { 3 | if [ -z "$SCHEME" ] 4 | then 5 | xcodebuild -list | awk '{if(found) print} /Schemes/{found=1}' | awk '{$1=$1};1' 6 | else 7 | echo $SCHEME 8 | fi 9 | } 10 | -------------------------------------------------------------------------------- /script/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source script/.env 6 | source script/script_hooks/schemes 7 | source script/script_hooks/destinations 8 | source script/common/carthage 9 | 10 | run_tests () 11 | { 12 | test_command="set -o pipefail && xcodebuild -scheme $1" 13 | 14 | if [ -z "$XCODE_WORKSPACE" ] 15 | then 16 | test_command="$test_command -project $XCODE_PROJECT" 17 | else 18 | test_command="$test_command -workspace $XCODE_WORKSPACE" 19 | fi 20 | 21 | destination=$(scheme_destination $1) 22 | test_command="$test_command -destination $destination clean build test -configuration Debug" 23 | 24 | if [ ! -z "$CIRCLE_ARTIFACTS" ] 25 | then 26 | test_command="$test_command | tee $CIRCLE_ARTIFACTS/xcode_raw.log" 27 | fi 28 | if type bundle > /dev/null && bundle show xcpretty > /dev/null 29 | then 30 | test_command="$test_command | bundle exec xcpretty -c" 31 | if [ ! -z "$CIRCLE_TEST_REPORTS" ] 32 | then 33 | test_command="$test_command --report junit --output $CIRCLE_TEST_REPORTS/xcode/results.xml" 34 | fi 35 | fi 36 | 37 | echo "" 38 | echo " → Running tests for scheme '$1'" 39 | echo "" 40 | if [ ! -z "$VERBOSE" ] 41 | then 42 | echo $test_command 43 | fi 44 | eval $test_command 45 | } 46 | 47 | if [ -f Cartfile.resolved ] 48 | then 49 | check_carthage_version 50 | fi 51 | 52 | current_schemes=$(schemes) 53 | if [ -z "$current_schemes" ] 54 | then 55 | echo "" 56 | echo "ERROR: There are no schemes. Probably you forgot to share your schemes" 57 | exit 1 58 | fi 59 | 60 | for scheme in $current_schemes 61 | do 62 | run_tests $scheme 63 | done 64 | 65 | if [ -f "$PROJECT_NAME.podspec" ] 66 | then 67 | echo "" 68 | echo " → Linting $PROJECT_NAME.podspec" 69 | echo "" 70 | if type bundle > /dev/null && bundle show pod > /dev/null 71 | then 72 | bundle exec pod lib lint 73 | elif type pod > /dev/null 74 | then 75 | pod lib lint 76 | fi 77 | fi 78 | -------------------------------------------------------------------------------- /script/update: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source script/.env 6 | source script/common/carthage 7 | 8 | if [ -f Cartfile.resolved ] && type carthage > /dev/null 9 | then 10 | check_carthage_version 11 | carthage_cmd="carthage update --platform $CARTHAGE_BUILD_PLATFORM" 12 | if [ "$USE_SSH" == "true" ] 13 | then 14 | carthage_cmd="$carthage_cmd --use-ssh" 15 | fi 16 | if [ "$USE_SUBMODULES" == "true" ] 17 | then 18 | carthage_cmd="$carthage_cmd --use-submodules --no-build" 19 | fi 20 | if [ "$CARTHAGE_NO_USE_BINARIES" == "true" ] 21 | then 22 | carthage_cmd="$carthage_cmd --no-use-binaries" 23 | fi 24 | if [ -z $DISABLE_CARTHAGE_CACHE ] && type bundle > /dev/null && bundle show carthage_cache > /dev/null && [ -f .carthage_cache.yml ] 25 | then 26 | carthage_cmd="$carthage_cmd && bundle exec carthage_cache publish" 27 | fi 28 | if [ ! -z "$VERBOSE" ] 29 | then 30 | echo $carthage_cmd 31 | fi 32 | eval $carthage_cmd 33 | elif [ -f Podfile ] 34 | then 35 | if type bundle > /dev/null && bundle show pod > /dev/null 36 | then 37 | bundle exec pod update 38 | else 39 | pod update 40 | fi 41 | fi 42 | --------------------------------------------------------------------------------