├── .gitignore ├── .gitmodules ├── README.md ├── ReactiveSwift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── yusef.xcuserdatad │ └── xcschemes │ └── ReactiveSwift.xcscheme ├── ReactiveSwift ├── AppDelegate.swift ├── Base.lproj │ └── Main.storyboard ├── DummySignInService.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── RAC.swift ├── ReactiveSwift-Bridging-Header.h ├── ViewController.swift └── kitten.jpg └── ReactiveSwiftTests ├── Info.plist └── ReactiveSwiftTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | ######################### 2 | # .gitignore file for Xcode4 / OS X Source projects 3 | # 4 | # Version 2.0 5 | # For latest version, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects 6 | # 7 | # 2013 updates: 8 | # - fixed the broken "save personal Schemes" 9 | # 10 | # NB: if you are storing "built" products, this WILL NOT WORK, 11 | # and you should use a different .gitignore (or none at all) 12 | # This file is for SOURCE projects, where there are many extra 13 | # files that we want to exclude 14 | # 15 | ######################### 16 | 17 | ##### 18 | # OS X temporary files that should never be committed 19 | 20 | .DS_Store 21 | *.swp 22 | *.lock 23 | profile 24 | 25 | 26 | #### 27 | # Xcode temporary files that should never be committed 28 | # 29 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this... 30 | 31 | *~.nib 32 | 33 | 34 | #### 35 | # Xcode build files - 36 | # 37 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData" 38 | 39 | DerivedData/ 40 | 41 | # NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build" 42 | 43 | build/ 44 | 45 | 46 | ##### 47 | # Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups) 48 | # 49 | # This is complicated: 50 | # 51 | # SOMETIMES you need to put this file in version control. 52 | # Apple designed it poorly - if you use "custom executables", they are 53 | # saved in this file. 54 | # 99% of projects do NOT use those, so they do NOT want to version control this file. 55 | # ..but if you're in the 1%, comment out the line "*.pbxuser" 56 | 57 | *.pbxuser 58 | *.mode1v3 59 | *.mode2v3 60 | *.perspectivev3 61 | # NB: also, whitelist the default ones, some projects need to use these 62 | !default.pbxuser 63 | !default.mode1v3 64 | !default.mode2v3 65 | !default.perspectivev3 66 | 67 | 68 | #### 69 | # Xcode 4 - semi-personal settings 70 | # 71 | # 72 | # OPTION 1: --------------------------------- 73 | # throw away ALL personal settings (including custom schemes! 74 | # - unless they are "shared") 75 | # 76 | # NB: this is exclusive with OPTION 2 below 77 | xcuserdata 78 | 79 | # OPTION 2: --------------------------------- 80 | # get rid of ALL personal settings, but KEEP SOME OF THEM 81 | # - NB: you must manually uncomment the bits you want to keep 82 | # 83 | # NB: this is exclusive with OPTION 1 above 84 | # 85 | #xcuserdata/**/* 86 | 87 | # (requires option 2 above): Personal Schemes 88 | # 89 | #!xcuserdata/**/xcschemes/* 90 | 91 | #### 92 | # XCode 4 workspaces - more detailed 93 | # 94 | # Workspaces are important! They are a core feature of Xcode - don't exclude them :) 95 | # 96 | # Workspace layout is quite spammy. For reference: 97 | # 98 | # /(root)/ 99 | # /(project-name).xcodeproj/ 100 | # project.pbxproj 101 | # /project.xcworkspace/ 102 | # contents.xcworkspacedata 103 | # /xcuserdata/ 104 | # /(your name)/xcuserdatad/ 105 | # UserInterfaceState.xcuserstate 106 | # /xcsshareddata/ 107 | # /xcschemes/ 108 | # (shared scheme name).xcscheme 109 | # /xcuserdata/ 110 | # /(your name)/xcuserdatad/ 111 | # (private scheme).xcscheme 112 | # xcschememanagement.plist 113 | # 114 | # 115 | 116 | #### 117 | # Xcode 4 - Deprecated classes 118 | # 119 | # Allegedly, if you manually "deprecate" your classes, they get moved here. 120 | # 121 | # We're using source-control, so this is a "feature" that we do not want! 122 | 123 | *.moved-aside 124 | 125 | 126 | #### 127 | # UNKNOWN: recommended by others, but I can't discover what these files are 128 | # 129 | # ...none. Everything is now explained. -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ReactiveCocoa"] 2 | path = ReactiveCocoa 3 | url = https://github.com/yusefnapora/ReactiveCocoa.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reactive Swift 2 | 3 | 4 | An implementation of Colin Eberhardt's [raywenderlich.com ReactiveCocoa tutorial][wenderlich] in Swift. 5 | 6 | I've been playing around trying to get [ReactiveCocoa][reactivecocoa_github] to play nice with [Swift][swift_lang]. 7 | 8 | For more information, see my [blog post][blogpost] which goes into detail about the interop issues and workarounds. 9 | 10 | ## Installation 11 | 12 | After cloning, you'll want to initialize the `ReactiveCocoa` submodule by doing: 13 | 14 | ``` 15 | git submodule init 16 | git submodule update 17 | Run script/bootstrap from within the ReactiveCocoa folder 18 | ``` 19 | 20 | This will check out [my fork][rc_fork] of ReactiveCocoa, which has a workaround for a Swift compiler error. 21 | 22 | [reactivecocoa_github]: https://github.com/ReactiveCocoa/ReactiveCocoa 23 | [wenderlich]: http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1 24 | [swift_lang]: https://developer.apple.com/swift/ 25 | [blogpost]: http://napora.org/a-swift-reaction 26 | [rc_fork]: https://github.com/yusefnapora/ReactiveCocoa -------------------------------------------------------------------------------- /ReactiveSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 80362714194784A800785CE1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80362713194784A800785CE1 /* AppDelegate.swift */; }; 11 | 80362716194784A800785CE1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80362715194784A800785CE1 /* ViewController.swift */; }; 12 | 80362719194784A800785CE1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 80362717194784A800785CE1 /* Main.storyboard */; }; 13 | 8036271B194784A800785CE1 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8036271A194784A800785CE1 /* Images.xcassets */; }; 14 | 80362727194784A800785CE1 /* ReactiveSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80362726194784A800785CE1 /* ReactiveSwiftTests.swift */; }; 15 | 803627481947860F00785CE1 /* libReactiveCocoa-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8036273D194785F100785CE1 /* libReactiveCocoa-iOS.a */; }; 16 | 8036274F19478C6D00785CE1 /* RAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8036274E19478C6D00785CE1 /* RAC.swift */; }; 17 | 803756081948CA5C00724166 /* DummySignInService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 803756071948CA5C00724166 /* DummySignInService.swift */; }; 18 | 8037560A1948D01600724166 /* kitten.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 803756091948D01600724166 /* kitten.jpg */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 80362721194784A800785CE1 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 80362706194784A800785CE1 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 8036270D194784A800785CE1; 27 | remoteInfo = ReactiveSwift; 28 | }; 29 | 8036273A194785F100785CE1 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */; 32 | proxyType = 2; 33 | remoteGlobalIDString = 88037F8315056328001A5B19; 34 | remoteInfo = ReactiveCocoa; 35 | }; 36 | 8036273C194785F100785CE1 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */; 39 | proxyType = 2; 40 | remoteGlobalIDString = 88F440AB153DAC820097B4C3; 41 | remoteInfo = "ReactiveCocoa-iOS"; 42 | }; 43 | 8036273E194785F100785CE1 /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */; 46 | proxyType = 2; 47 | remoteGlobalIDString = 88CDF7DC15000FCF00163A9F; 48 | remoteInfo = ReactiveCocoaTests; 49 | }; 50 | 80362740194785F100785CE1 /* PBXContainerItemProxy */ = { 51 | isa = PBXContainerItemProxy; 52 | containerPortal = 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */; 53 | proxyType = 2; 54 | remoteGlobalIDString = 5FAF5223174D4C2000CAC810; 55 | remoteInfo = "ReactiveCocoaTests-iOS"; 56 | }; 57 | 80362742194785F100785CE1 /* PBXContainerItemProxy */ = { 58 | isa = PBXContainerItemProxy; 59 | containerPortal = 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */; 60 | proxyType = 2; 61 | remoteGlobalIDString = 1860F412177C91B500C7B3C9; 62 | remoteInfo = "ReactiveCocoa-iOS-UIKitTestHost"; 63 | }; 64 | 80362744194785F100785CE1 /* PBXContainerItemProxy */ = { 65 | isa = PBXContainerItemProxy; 66 | containerPortal = 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */; 67 | proxyType = 2; 68 | remoteGlobalIDString = 1860F430177C91B500C7B3C9; 69 | remoteInfo = "ReactiveCocoa-iOS-UIKitTestHostTests"; 70 | }; 71 | 80362746194785F100785CE1 /* PBXContainerItemProxy */ = { 72 | isa = PBXContainerItemProxy; 73 | containerPortal = 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */; 74 | proxyType = 2; 75 | remoteGlobalIDString = D05AD39417F2D56F0080895B; 76 | remoteInfo = "ReactiveCocoa-Mac"; 77 | }; 78 | 803627491947866700785CE1 /* PBXContainerItemProxy */ = { 79 | isa = PBXContainerItemProxy; 80 | containerPortal = 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */; 81 | proxyType = 1; 82 | remoteGlobalIDString = 88F440AA153DAC820097B4C3; 83 | remoteInfo = "ReactiveCocoa-iOS"; 84 | }; 85 | /* End PBXContainerItemProxy section */ 86 | 87 | /* Begin PBXFileReference section */ 88 | 8036270E194784A800785CE1 /* ReactiveSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReactiveSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; 89 | 80362712194784A800785CE1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 90 | 80362713194784A800785CE1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 91 | 80362715194784A800785CE1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 92 | 80362718194784A800785CE1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 93 | 8036271A194784A800785CE1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 94 | 80362720194784A800785CE1 /* ReactiveSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 95 | 80362725194784A800785CE1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 96 | 80362726194784A800785CE1 /* ReactiveSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactiveSwiftTests.swift; sourceTree = ""; }; 97 | 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactiveCocoa.xcodeproj; path = ReactiveCocoa/ReactiveCocoaFramework/ReactiveCocoa.xcodeproj; sourceTree = ""; }; 98 | 8036274D194786EE00785CE1 /* ReactiveSwift-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactiveSwift-Bridging-Header.h"; sourceTree = ""; }; 99 | 8036274E19478C6D00785CE1 /* RAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RAC.swift; sourceTree = ""; }; 100 | 803756071948CA5C00724166 /* DummySignInService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DummySignInService.swift; sourceTree = ""; }; 101 | 803756091948D01600724166 /* kitten.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = kitten.jpg; sourceTree = ""; }; 102 | /* End PBXFileReference section */ 103 | 104 | /* Begin PBXFrameworksBuildPhase section */ 105 | 8036270B194784A800785CE1 /* Frameworks */ = { 106 | isa = PBXFrameworksBuildPhase; 107 | buildActionMask = 2147483647; 108 | files = ( 109 | 803627481947860F00785CE1 /* libReactiveCocoa-iOS.a in Frameworks */, 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | 8036271D194784A800785CE1 /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | ); 118 | runOnlyForDeploymentPostprocessing = 0; 119 | }; 120 | /* End PBXFrameworksBuildPhase section */ 121 | 122 | /* Begin PBXGroup section */ 123 | 80362705194784A800785CE1 = { 124 | isa = PBXGroup; 125 | children = ( 126 | 80362710194784A800785CE1 /* ReactiveSwift */, 127 | 80362723194784A800785CE1 /* ReactiveSwiftTests */, 128 | 8036270F194784A800785CE1 /* Products */, 129 | 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */, 130 | ); 131 | sourceTree = ""; 132 | }; 133 | 8036270F194784A800785CE1 /* Products */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 8036270E194784A800785CE1 /* ReactiveSwift.app */, 137 | 80362720194784A800785CE1 /* ReactiveSwiftTests.xctest */, 138 | ); 139 | name = Products; 140 | sourceTree = ""; 141 | }; 142 | 80362710194784A800785CE1 /* ReactiveSwift */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 803756091948D01600724166 /* kitten.jpg */, 146 | 80362713194784A800785CE1 /* AppDelegate.swift */, 147 | 80362715194784A800785CE1 /* ViewController.swift */, 148 | 80362717194784A800785CE1 /* Main.storyboard */, 149 | 8036271A194784A800785CE1 /* Images.xcassets */, 150 | 80362711194784A800785CE1 /* Supporting Files */, 151 | 8036274D194786EE00785CE1 /* ReactiveSwift-Bridging-Header.h */, 152 | 8036274E19478C6D00785CE1 /* RAC.swift */, 153 | 803756071948CA5C00724166 /* DummySignInService.swift */, 154 | ); 155 | path = ReactiveSwift; 156 | sourceTree = ""; 157 | }; 158 | 80362711194784A800785CE1 /* Supporting Files */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 80362712194784A800785CE1 /* Info.plist */, 162 | ); 163 | name = "Supporting Files"; 164 | sourceTree = ""; 165 | }; 166 | 80362723194784A800785CE1 /* ReactiveSwiftTests */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 80362726194784A800785CE1 /* ReactiveSwiftTests.swift */, 170 | 80362724194784A800785CE1 /* Supporting Files */, 171 | ); 172 | path = ReactiveSwiftTests; 173 | sourceTree = ""; 174 | }; 175 | 80362724194784A800785CE1 /* Supporting Files */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | 80362725194784A800785CE1 /* Info.plist */, 179 | ); 180 | name = "Supporting Files"; 181 | sourceTree = ""; 182 | }; 183 | 80362731194785F100785CE1 /* Products */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 8036273B194785F100785CE1 /* ReactiveCocoa.framework */, 187 | 8036273D194785F100785CE1 /* libReactiveCocoa-iOS.a */, 188 | 8036273F194785F100785CE1 /* ReactiveCocoaTests.octest */, 189 | 80362741194785F100785CE1 /* ReactiveCocoaTests-iOS.octest */, 190 | 80362743194785F100785CE1 /* ReactiveCocoa-iOS-UIKitTestHost.app */, 191 | 80362745194785F100785CE1 /* ReactiveCocoa-iOS-UIKitTestHostTests.octest */, 192 | 80362747194785F100785CE1 /* libReactiveCocoa-Mac.a */, 193 | ); 194 | name = Products; 195 | sourceTree = ""; 196 | }; 197 | /* End PBXGroup section */ 198 | 199 | /* Begin PBXNativeTarget section */ 200 | 8036270D194784A800785CE1 /* ReactiveSwift */ = { 201 | isa = PBXNativeTarget; 202 | buildConfigurationList = 8036272A194784A800785CE1 /* Build configuration list for PBXNativeTarget "ReactiveSwift" */; 203 | buildPhases = ( 204 | 8036270A194784A800785CE1 /* Sources */, 205 | 8036270B194784A800785CE1 /* Frameworks */, 206 | 8036270C194784A800785CE1 /* Resources */, 207 | ); 208 | buildRules = ( 209 | ); 210 | dependencies = ( 211 | 8036274A1947866700785CE1 /* PBXTargetDependency */, 212 | ); 213 | name = ReactiveSwift; 214 | productName = ReactiveSwift; 215 | productReference = 8036270E194784A800785CE1 /* ReactiveSwift.app */; 216 | productType = "com.apple.product-type.application"; 217 | }; 218 | 8036271F194784A800785CE1 /* ReactiveSwiftTests */ = { 219 | isa = PBXNativeTarget; 220 | buildConfigurationList = 8036272D194784A800785CE1 /* Build configuration list for PBXNativeTarget "ReactiveSwiftTests" */; 221 | buildPhases = ( 222 | 8036271C194784A800785CE1 /* Sources */, 223 | 8036271D194784A800785CE1 /* Frameworks */, 224 | 8036271E194784A800785CE1 /* Resources */, 225 | ); 226 | buildRules = ( 227 | ); 228 | dependencies = ( 229 | 80362722194784A800785CE1 /* PBXTargetDependency */, 230 | ); 231 | name = ReactiveSwiftTests; 232 | productName = ReactiveSwiftTests; 233 | productReference = 80362720194784A800785CE1 /* ReactiveSwiftTests.xctest */; 234 | productType = "com.apple.product-type.bundle.unit-test"; 235 | }; 236 | /* End PBXNativeTarget section */ 237 | 238 | /* Begin PBXProject section */ 239 | 80362706194784A800785CE1 /* Project object */ = { 240 | isa = PBXProject; 241 | attributes = { 242 | LastUpgradeCheck = 0600; 243 | ORGANIZATIONNAME = "Yusef Napora"; 244 | TargetAttributes = { 245 | 8036270D194784A800785CE1 = { 246 | CreatedOnToolsVersion = 6.0; 247 | }; 248 | 8036271F194784A800785CE1 = { 249 | CreatedOnToolsVersion = 6.0; 250 | TestTargetID = 8036270D194784A800785CE1; 251 | }; 252 | }; 253 | }; 254 | buildConfigurationList = 80362709194784A800785CE1 /* Build configuration list for PBXProject "ReactiveSwift" */; 255 | compatibilityVersion = "Xcode 3.2"; 256 | developmentRegion = English; 257 | hasScannedForEncodings = 0; 258 | knownRegions = ( 259 | en, 260 | Base, 261 | ); 262 | mainGroup = 80362705194784A800785CE1; 263 | productRefGroup = 8036270F194784A800785CE1 /* Products */; 264 | projectDirPath = ""; 265 | projectReferences = ( 266 | { 267 | ProductGroup = 80362731194785F100785CE1 /* Products */; 268 | ProjectRef = 80362730194785F100785CE1 /* ReactiveCocoa.xcodeproj */; 269 | }, 270 | ); 271 | projectRoot = ""; 272 | targets = ( 273 | 8036270D194784A800785CE1 /* ReactiveSwift */, 274 | 8036271F194784A800785CE1 /* ReactiveSwiftTests */, 275 | ); 276 | }; 277 | /* End PBXProject section */ 278 | 279 | /* Begin PBXReferenceProxy section */ 280 | 8036273B194785F100785CE1 /* ReactiveCocoa.framework */ = { 281 | isa = PBXReferenceProxy; 282 | fileType = wrapper.framework; 283 | path = ReactiveCocoa.framework; 284 | remoteRef = 8036273A194785F100785CE1 /* PBXContainerItemProxy */; 285 | sourceTree = BUILT_PRODUCTS_DIR; 286 | }; 287 | 8036273D194785F100785CE1 /* libReactiveCocoa-iOS.a */ = { 288 | isa = PBXReferenceProxy; 289 | fileType = archive.ar; 290 | path = "libReactiveCocoa-iOS.a"; 291 | remoteRef = 8036273C194785F100785CE1 /* PBXContainerItemProxy */; 292 | sourceTree = BUILT_PRODUCTS_DIR; 293 | }; 294 | 8036273F194785F100785CE1 /* ReactiveCocoaTests.octest */ = { 295 | isa = PBXReferenceProxy; 296 | fileType = wrapper.cfbundle; 297 | path = ReactiveCocoaTests.octest; 298 | remoteRef = 8036273E194785F100785CE1 /* PBXContainerItemProxy */; 299 | sourceTree = BUILT_PRODUCTS_DIR; 300 | }; 301 | 80362741194785F100785CE1 /* ReactiveCocoaTests-iOS.octest */ = { 302 | isa = PBXReferenceProxy; 303 | fileType = wrapper.cfbundle; 304 | path = "ReactiveCocoaTests-iOS.octest"; 305 | remoteRef = 80362740194785F100785CE1 /* PBXContainerItemProxy */; 306 | sourceTree = BUILT_PRODUCTS_DIR; 307 | }; 308 | 80362743194785F100785CE1 /* ReactiveCocoa-iOS-UIKitTestHost.app */ = { 309 | isa = PBXReferenceProxy; 310 | fileType = wrapper.application; 311 | path = "ReactiveCocoa-iOS-UIKitTestHost.app"; 312 | remoteRef = 80362742194785F100785CE1 /* PBXContainerItemProxy */; 313 | sourceTree = BUILT_PRODUCTS_DIR; 314 | }; 315 | 80362745194785F100785CE1 /* ReactiveCocoa-iOS-UIKitTestHostTests.octest */ = { 316 | isa = PBXReferenceProxy; 317 | fileType = wrapper.cfbundle; 318 | path = "ReactiveCocoa-iOS-UIKitTestHostTests.octest"; 319 | remoteRef = 80362744194785F100785CE1 /* PBXContainerItemProxy */; 320 | sourceTree = BUILT_PRODUCTS_DIR; 321 | }; 322 | 80362747194785F100785CE1 /* libReactiveCocoa-Mac.a */ = { 323 | isa = PBXReferenceProxy; 324 | fileType = archive.ar; 325 | path = "libReactiveCocoa-Mac.a"; 326 | remoteRef = 80362746194785F100785CE1 /* PBXContainerItemProxy */; 327 | sourceTree = BUILT_PRODUCTS_DIR; 328 | }; 329 | /* End PBXReferenceProxy section */ 330 | 331 | /* Begin PBXResourcesBuildPhase section */ 332 | 8036270C194784A800785CE1 /* Resources */ = { 333 | isa = PBXResourcesBuildPhase; 334 | buildActionMask = 2147483647; 335 | files = ( 336 | 80362719194784A800785CE1 /* Main.storyboard in Resources */, 337 | 8037560A1948D01600724166 /* kitten.jpg in Resources */, 338 | 8036271B194784A800785CE1 /* Images.xcassets in Resources */, 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | }; 342 | 8036271E194784A800785CE1 /* Resources */ = { 343 | isa = PBXResourcesBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | ); 347 | runOnlyForDeploymentPostprocessing = 0; 348 | }; 349 | /* End PBXResourcesBuildPhase section */ 350 | 351 | /* Begin PBXSourcesBuildPhase section */ 352 | 8036270A194784A800785CE1 /* Sources */ = { 353 | isa = PBXSourcesBuildPhase; 354 | buildActionMask = 2147483647; 355 | files = ( 356 | 803756081948CA5C00724166 /* DummySignInService.swift in Sources */, 357 | 8036274F19478C6D00785CE1 /* RAC.swift in Sources */, 358 | 80362716194784A800785CE1 /* ViewController.swift in Sources */, 359 | 80362714194784A800785CE1 /* AppDelegate.swift in Sources */, 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | }; 363 | 8036271C194784A800785CE1 /* Sources */ = { 364 | isa = PBXSourcesBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | 80362727194784A800785CE1 /* ReactiveSwiftTests.swift in Sources */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | /* End PBXSourcesBuildPhase section */ 372 | 373 | /* Begin PBXTargetDependency section */ 374 | 80362722194784A800785CE1 /* PBXTargetDependency */ = { 375 | isa = PBXTargetDependency; 376 | target = 8036270D194784A800785CE1 /* ReactiveSwift */; 377 | targetProxy = 80362721194784A800785CE1 /* PBXContainerItemProxy */; 378 | }; 379 | 8036274A1947866700785CE1 /* PBXTargetDependency */ = { 380 | isa = PBXTargetDependency; 381 | name = "ReactiveCocoa-iOS"; 382 | targetProxy = 803627491947866700785CE1 /* PBXContainerItemProxy */; 383 | }; 384 | /* End PBXTargetDependency section */ 385 | 386 | /* Begin PBXVariantGroup section */ 387 | 80362717194784A800785CE1 /* Main.storyboard */ = { 388 | isa = PBXVariantGroup; 389 | children = ( 390 | 80362718194784A800785CE1 /* Base */, 391 | ); 392 | name = Main.storyboard; 393 | sourceTree = ""; 394 | }; 395 | /* End PBXVariantGroup section */ 396 | 397 | /* Begin XCBuildConfiguration section */ 398 | 80362728194784A800785CE1 /* Debug */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ALWAYS_SEARCH_USER_PATHS = NO; 402 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 403 | CLANG_CXX_LIBRARY = "libc++"; 404 | CLANG_ENABLE_MODULES = YES; 405 | CLANG_ENABLE_OBJC_ARC = YES; 406 | CLANG_WARN_BOOL_CONVERSION = YES; 407 | CLANG_WARN_CONSTANT_CONVERSION = YES; 408 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 409 | CLANG_WARN_EMPTY_BODY = YES; 410 | CLANG_WARN_ENUM_CONVERSION = YES; 411 | CLANG_WARN_INT_CONVERSION = YES; 412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | ENABLE_STRICT_OBJC_MSGSEND = YES; 418 | GCC_C_LANGUAGE_STANDARD = gnu99; 419 | GCC_DYNAMIC_NO_PIC = NO; 420 | GCC_OPTIMIZATION_LEVEL = 0; 421 | GCC_PREPROCESSOR_DEFINITIONS = ( 422 | "DEBUG=1", 423 | "$(inherited)", 424 | ); 425 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 426 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 427 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 428 | GCC_WARN_UNDECLARED_SELECTOR = YES; 429 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 430 | GCC_WARN_UNUSED_FUNCTION = YES; 431 | GCC_WARN_UNUSED_VARIABLE = YES; 432 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 433 | METAL_ENABLE_DEBUG_INFO = YES; 434 | ONLY_ACTIVE_ARCH = YES; 435 | SDKROOT = iphoneos; 436 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 437 | TARGETED_DEVICE_FAMILY = 2; 438 | }; 439 | name = Debug; 440 | }; 441 | 80362729194784A800785CE1 /* Release */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | ALWAYS_SEARCH_USER_PATHS = NO; 445 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 446 | CLANG_CXX_LIBRARY = "libc++"; 447 | CLANG_ENABLE_MODULES = YES; 448 | CLANG_ENABLE_OBJC_ARC = YES; 449 | CLANG_WARN_BOOL_CONVERSION = YES; 450 | CLANG_WARN_CONSTANT_CONVERSION = YES; 451 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 452 | CLANG_WARN_EMPTY_BODY = YES; 453 | CLANG_WARN_ENUM_CONVERSION = YES; 454 | CLANG_WARN_INT_CONVERSION = YES; 455 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 456 | CLANG_WARN_UNREACHABLE_CODE = YES; 457 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 458 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 459 | COPY_PHASE_STRIP = YES; 460 | ENABLE_NS_ASSERTIONS = NO; 461 | ENABLE_STRICT_OBJC_MSGSEND = YES; 462 | GCC_C_LANGUAGE_STANDARD = gnu99; 463 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 464 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 465 | GCC_WARN_UNDECLARED_SELECTOR = YES; 466 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 467 | GCC_WARN_UNUSED_FUNCTION = YES; 468 | GCC_WARN_UNUSED_VARIABLE = YES; 469 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 470 | METAL_ENABLE_DEBUG_INFO = NO; 471 | SDKROOT = iphoneos; 472 | TARGETED_DEVICE_FAMILY = 2; 473 | VALIDATE_PRODUCT = YES; 474 | }; 475 | name = Release; 476 | }; 477 | 8036272B194784A800785CE1 /* Debug */ = { 478 | isa = XCBuildConfiguration; 479 | buildSettings = { 480 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 481 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 482 | HEADER_SEARCH_PATHS = ( 483 | "$(inherited)", 484 | "/Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", 485 | "\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/include\"", 486 | "$(inherited)", 487 | ); 488 | INFOPLIST_FILE = ReactiveSwift/Info.plist; 489 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 490 | OTHER_LDFLAGS = "-ObjC"; 491 | PRODUCT_NAME = "$(TARGET_NAME)"; 492 | SWIFT_OBJC_BRIDGING_HEADER = "ReactiveSwift/ReactiveSwift-Bridging-Header.h"; 493 | }; 494 | name = Debug; 495 | }; 496 | 8036272C194784A800785CE1 /* Release */ = { 497 | isa = XCBuildConfiguration; 498 | buildSettings = { 499 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 500 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 501 | HEADER_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "/Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", 504 | "\"$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/include\"", 505 | "$(inherited)", 506 | ); 507 | INFOPLIST_FILE = ReactiveSwift/Info.plist; 508 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 509 | OTHER_LDFLAGS = "-ObjC"; 510 | PRODUCT_NAME = "$(TARGET_NAME)"; 511 | SWIFT_OBJC_BRIDGING_HEADER = "ReactiveSwift/ReactiveSwift-Bridging-Header.h"; 512 | }; 513 | name = Release; 514 | }; 515 | 8036272E194784A800785CE1 /* Debug */ = { 516 | isa = XCBuildConfiguration; 517 | buildSettings = { 518 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ReactiveSwift.app/ReactiveSwift"; 519 | FRAMEWORK_SEARCH_PATHS = ( 520 | "$(SDKROOT)/Developer/Library/Frameworks", 521 | "$(inherited)", 522 | ); 523 | GCC_PREPROCESSOR_DEFINITIONS = ( 524 | "DEBUG=1", 525 | "$(inherited)", 526 | ); 527 | INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; 528 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 529 | METAL_ENABLE_DEBUG_INFO = YES; 530 | PRODUCT_NAME = "$(TARGET_NAME)"; 531 | TEST_HOST = "$(BUNDLE_LOADER)"; 532 | }; 533 | name = Debug; 534 | }; 535 | 8036272F194784A800785CE1 /* Release */ = { 536 | isa = XCBuildConfiguration; 537 | buildSettings = { 538 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ReactiveSwift.app/ReactiveSwift"; 539 | FRAMEWORK_SEARCH_PATHS = ( 540 | "$(SDKROOT)/Developer/Library/Frameworks", 541 | "$(inherited)", 542 | ); 543 | INFOPLIST_FILE = ReactiveSwiftTests/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 545 | METAL_ENABLE_DEBUG_INFO = NO; 546 | PRODUCT_NAME = "$(TARGET_NAME)"; 547 | TEST_HOST = "$(BUNDLE_LOADER)"; 548 | }; 549 | name = Release; 550 | }; 551 | /* End XCBuildConfiguration section */ 552 | 553 | /* Begin XCConfigurationList section */ 554 | 80362709194784A800785CE1 /* Build configuration list for PBXProject "ReactiveSwift" */ = { 555 | isa = XCConfigurationList; 556 | buildConfigurations = ( 557 | 80362728194784A800785CE1 /* Debug */, 558 | 80362729194784A800785CE1 /* Release */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | defaultConfigurationName = Release; 562 | }; 563 | 8036272A194784A800785CE1 /* Build configuration list for PBXNativeTarget "ReactiveSwift" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 8036272B194784A800785CE1 /* Debug */, 567 | 8036272C194784A800785CE1 /* Release */, 568 | ); 569 | defaultConfigurationIsVisible = 0; 570 | defaultConfigurationName = Release; 571 | }; 572 | 8036272D194784A800785CE1 /* Build configuration list for PBXNativeTarget "ReactiveSwiftTests" */ = { 573 | isa = XCConfigurationList; 574 | buildConfigurations = ( 575 | 8036272E194784A800785CE1 /* Debug */, 576 | 8036272F194784A800785CE1 /* Release */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | defaultConfigurationName = Release; 580 | }; 581 | /* End XCConfigurationList section */ 582 | }; 583 | rootObject = 80362706194784A800785CE1 /* Project object */; 584 | } 585 | -------------------------------------------------------------------------------- /ReactiveSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ReactiveSwift.xcodeproj/xcuserdata/yusef.xcuserdatad/xcschemes/ReactiveSwift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /ReactiveSwift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ReactiveSwift 4 | // 5 | // Created by Yusef Napora on 6/10/14. 6 | // Copyright (c) 2014 Yusef Napora. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ReactiveSwift/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 38 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /ReactiveSwift/DummySignInService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DummySignInService.swift 3 | // ReactiveSwift 4 | // 5 | // Created by Yusef Napora on 6/11/14. 6 | // Copyright (c) 2014 Yusef Napora. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class DummySignInService { 12 | func signIn(username: String, password: String, complete: (Bool) -> Void) { 13 | let success = (username == "username" && password == "password") 14 | complete(success) 15 | } 16 | } -------------------------------------------------------------------------------- /ReactiveSwift/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "ipad", 5 | "size" : "29x29", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "ipad", 10 | "size" : "29x29", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "ipad", 15 | "size" : "40x40", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "40x40", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "76x76", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "76x76", 31 | "scale" : "2x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ReactiveSwift/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "extent" : "full-screen", 14 | "minimum-system-version" : "7.0", 15 | "subtype" : "retina4", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | }, 46 | { 47 | "orientation" : "portrait", 48 | "idiom" : "iphone", 49 | "extent" : "full-screen", 50 | "scale" : "1x" 51 | }, 52 | { 53 | "orientation" : "portrait", 54 | "idiom" : "iphone", 55 | "extent" : "full-screen", 56 | "scale" : "2x" 57 | }, 58 | { 59 | "orientation" : "portrait", 60 | "idiom" : "iphone", 61 | "extent" : "full-screen", 62 | "subtype" : "retina4", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "orientation" : "portrait", 67 | "idiom" : "ipad", 68 | "extent" : "to-status-bar", 69 | "scale" : "1x" 70 | }, 71 | { 72 | "orientation" : "portrait", 73 | "idiom" : "ipad", 74 | "extent" : "full-screen", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "orientation" : "landscape", 79 | "idiom" : "ipad", 80 | "extent" : "to-status-bar", 81 | "scale" : "1x" 82 | }, 83 | { 84 | "orientation" : "landscape", 85 | "idiom" : "ipad", 86 | "extent" : "full-screen", 87 | "scale" : "1x" 88 | }, 89 | { 90 | "orientation" : "portrait", 91 | "idiom" : "ipad", 92 | "extent" : "to-status-bar", 93 | "scale" : "2x" 94 | }, 95 | { 96 | "orientation" : "portrait", 97 | "idiom" : "ipad", 98 | "extent" : "full-screen", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "orientation" : "landscape", 103 | "idiom" : "ipad", 104 | "extent" : "to-status-bar", 105 | "scale" : "2x" 106 | }, 107 | { 108 | "orientation" : "landscape", 109 | "idiom" : "ipad", 110 | "extent" : "full-screen", 111 | "scale" : "2x" 112 | } 113 | ], 114 | "info" : { 115 | "version" : 1, 116 | "author" : "xcode" 117 | } 118 | } -------------------------------------------------------------------------------- /ReactiveSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.napora.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ReactiveSwift/RAC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RAC.swift 3 | // ReactiveSwift 4 | // 5 | // Created by Yusef Napora on 6/10/14. 6 | // Copyright (c) 2014 Yusef Napora. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | struct RAC { 13 | var target : NSObject! 14 | var keyPath : String! 15 | var nilValue : AnyObject! 16 | 17 | init(_ target: NSObject!, _ keyPath: String, nilValue: AnyObject? = nil) { 18 | self.target = target 19 | self.keyPath = keyPath 20 | self.nilValue = nilValue 21 | } 22 | 23 | func assignSignal(signal : RACSignal) { 24 | signal.setKeyPath(self.keyPath, onObject: self.target, nilValue: self.nilValue) 25 | } 26 | } 27 | 28 | extension NSObject { 29 | func RACObserve(target: NSObject!,_ keyPath: String) -> RACSignal{ 30 | return target.rac_valuesForKeyPath(keyPath, observer: self) 31 | } 32 | } 33 | 34 | 35 | infix operator <~ {} 36 | func <~ (rac: RAC, signal: RACSignal) { 37 | rac.assignSignal(signal) 38 | } 39 | 40 | infix operator ~> {} 41 | func ~> (signal: RACSignal, rac: RAC) { 42 | rac.assignSignal(signal) 43 | } 44 | 45 | -------------------------------------------------------------------------------- /ReactiveSwift/ReactiveSwift-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // ReactiveSwift-Bridging-Header.h 3 | // ReactiveSwift 4 | // 5 | // Created by Yusef Napora on 6/10/14. 6 | // Copyright (c) 2014 Yusef Napora. All rights reserved. 7 | // 8 | 9 | #import -------------------------------------------------------------------------------- /ReactiveSwift/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ReactiveSwift 4 | // 5 | // Created by Yusef Napora on 6/10/14. 6 | // Copyright (c) 2014 Yusef Napora. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet var usernameField : UITextField! 14 | @IBOutlet var passwordField : UITextField! 15 | @IBOutlet var loginButton : UIButton! 16 | @IBOutlet var signInFailureText : UILabel! 17 | 18 | var signInService = DummySignInService() 19 | 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | // we can map signals using the trailing closure syntax 25 | var validUsernameSignal = 26 | usernameField 27 | .rac_textSignal() 28 | .map { self.isValidUsername($0) } 29 | 30 | // or by passing the name of a function. 31 | // note that isValidPassword has to accept AnyObject! and return an AnyObject compatible type 32 | var validPasswordSignal = 33 | passwordField 34 | .rac_textSignal() 35 | .map(isValidPassword) 36 | 37 | // Here we're using our swift version of the 'RAC' macro. (see RAC.swift) 38 | // 39 | // Using the custom operators '<~' and '~>', we can have the signal 40 | // on either side of the RAC construct. Both of the following statements 41 | // wire up a signal to the 'backgroundColor' property of a UITextField 42 | 43 | 44 | RAC(self.usernameField, "backgroundColor") <~ validUsernameSignal.map(colorForValidity) 45 | validPasswordSignal.map(colorForValidity) ~> RAC(self.passwordField, "backgroundColor") 46 | 47 | // RACSignal prodvides a combineLatest:reduce: function that lets you provide a reducer block to 48 | // "sum up" multiple values. However, it's implemented using a tricky feature of Objective C blocks 49 | // that is incompatibile with swift. Namely, if you define an Objective C block type with an empty argument 50 | // list, it can accept any number of arguments. 51 | // For example a block defined like this: id (^blockname)() 52 | // could be assigned a block with any number of parameters, so long as it returns id 53 | // 54 | // ReactiveCocoa exploits this to provide a limited form of dynamic blocks. If a signal's value is an RACTuple, 55 | // it will invoke the block with the number of arguments corresponding to the number of values in the tuple 56 | // (up to a maximum of fifteen). 57 | // 58 | // Swift is much more strict, and won't let you pass in a closure with arguments to a method expecting 59 | // a block with zero arguments. 60 | // 61 | // However, swift does provide a reduce function on the Array type, and we can easily get an Array 62 | // representation of an RACTuple's values. 63 | // Here we're mapping the RACTuple returned by RACSignal.combineLatest() to a single Bool, by first 64 | // casting tuple.allObjects() to a [Bool] and then reducing it using a very simple reducer that just 65 | // returns the logical AND of the two arguments. 66 | // 67 | 68 | let signupActiveSignal = RACSignal.combineLatest([validUsernameSignal, validPasswordSignal]).map { 69 | let tuple = $0 as RACTuple 70 | let bools: [Bool] = tuple.allObjects() as [Bool] 71 | let result: Bool = bools.reduce(true) {$0 && $1} 72 | return result 73 | } 74 | signupActiveSignal ~> RAC(self.loginButton, "enabled") 75 | 76 | #if DONT_COMPILE_ME_BRO 77 | // If your signals contain values of different types, you'll need to unpack the RACTuple yourself 78 | // Note that this won't compile if you remove the #if fence, since we're using undeclared identifiers 79 | // 'thisSignalHasABool' and 'thisOneHasAString' 80 | RACSignal.combineLatest([thisSignalHasABool, thisOneHasAString]).map { 81 | let tuple = $0 as RACTuple 82 | let boolVal = tuple.first() as Bool 83 | let strVal = tuple.second() as String 84 | 85 | // do something with boolVal and strVal 86 | } 87 | #endif 88 | 89 | #if EASY_WAY 90 | // For this simple case where we just want the logical AND of a set of NSNumber containing signals, 91 | // we can just use the built-in AND method on RACSignal instead of a reducer. 92 | // Still, I spent a good couple hours trying to get my head around the reduce block thing, so 93 | // I wanted to keep the demonstration of reducing in Swift above. Here's how to use the AND() method. 94 | // 95 | // Also, with our RAC construct now able to live on the right-hand side of the signal, we can add it to 96 | // the end of a chain, like this: 97 | RACSignal.combineLatest([validUsernameSignal, validPasswordSignal]) 98 | .AND() 99 | ~> RAC(self.loginButton, "enabled") 100 | #endif 101 | 102 | 103 | loginButton.rac_signalForControlEvents(UIControlEvents.TouchUpInside) 104 | .doNext { 105 | _ in 106 | self.loginButton.enabled = false 107 | self.signInFailureText.hidden = true 108 | } 109 | .flattenMap(signInSignal) 110 | .subscribeNext { 111 | let success = $0 as Bool 112 | self.loginButton.enabled = true 113 | self.signInFailureText.hidden = success 114 | println("Sign in result: \(success)") 115 | if success { 116 | self.performSegueWithIdentifier("signInSuccess", sender: self) 117 | } 118 | } 119 | } 120 | 121 | func isValidUsername(name : AnyObject!) -> NSNumber! { 122 | return (name as NSString).length > 3; 123 | } 124 | 125 | // since we're passing this directly to a method expecting a block of type id (^)(id), 126 | // we need to accept and return AnyObject compatible types 127 | func isValidPassword(pass : AnyObject!) -> NSNumber! { 128 | return (pass as NSString).length > 3; 129 | } 130 | 131 | func colorForValidity(valid : AnyObject!) -> UIColor! { 132 | return (valid as Bool) ? UIColor.clearColor() : UIColor.yellowColor() 133 | } 134 | 135 | func signInSignal(_ : AnyObject!) -> RACSignal { 136 | return RACSignal.createSignal { 137 | subscriber in 138 | self.signInService.signIn(self.usernameField.text, password: self.passwordField.text) { 139 | subscriber.sendNext($0) 140 | subscriber.sendCompleted() 141 | } 142 | return nil 143 | } 144 | } 145 | 146 | 147 | override func didReceiveMemoryWarning() { 148 | super.didReceiveMemoryWarning() 149 | // Dispose of any resources that can be recreated. 150 | } 151 | 152 | 153 | } 154 | 155 | -------------------------------------------------------------------------------- /ReactiveSwift/kitten.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yusefnapora/ReactiveSwift/99f40af2c35894ce931f5ec958e4267952da1237/ReactiveSwift/kitten.jpg -------------------------------------------------------------------------------- /ReactiveSwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.napora.${PRODUCT_NAME:rfc1034identifier} 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 | -------------------------------------------------------------------------------- /ReactiveSwiftTests/ReactiveSwiftTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReactiveSwiftTests.swift 3 | // ReactiveSwiftTests 4 | // 5 | // Created by Yusef Napora on 6/10/14. 6 | // Copyright (c) 2014 Yusef Napora. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class ReactiveSwiftTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | XCTAssert(true, "Pass") 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock() { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | --------------------------------------------------------------------------------