├── .gitignore ├── Podfile ├── Podfile.lock ├── README.md ├── ReactiveCocoaDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ ├── dajing.xcuserdatad │ └── xcschemes │ │ ├── Documentation.xcscheme │ │ ├── ReactiveCocoaDemo.xcscheme │ │ └── xcschememanagement.plist │ ├── ddaajing.xcuserdatad │ └── xcschemes │ │ ├── Documentation.xcscheme │ │ ├── ReactiveCocoaDemo.xcscheme │ │ └── xcschememanagement.plist │ └── djmacin.xcuserdatad │ └── xcschemes │ ├── Documentation.xcscheme │ ├── ReactiveCocoaDemo.xcscheme │ └── xcschememanagement.plist ├── ReactiveCocoaDemo.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── ReactiveCocoaDemo.xccheckout ├── ReactiveCocoaDemo ├── AddCityViewController.h ├── AddCityViewController.m ├── AddCityViewModel.h ├── AddCityViewModel.m ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── Main.storyboard ├── City.h ├── City.m ├── GeoCityViewController.h ├── GeoCityViewController.m ├── GeoCityViewModel.h ├── GeoCityViewModel.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Launch Screen.storyboard ├── MyTableViewCell.h ├── MyTableViewCell.m ├── ReactiveCocoaDemo-Info.plist ├── ReactiveCocoaDemo-Prefix.pch ├── ViewController.h ├── ViewController.m ├── en.lproj │ └── InfoPlist.strings └── main.m └── ReactiveCocoaDemoTests ├── AddCitySpec.m ├── CityListSpec.m ├── ReactiveCocoaDemoTests-Info.plist └── en.lproj └── InfoPlist.strings /.gitignore: -------------------------------------------------------------------------------- 1 | /Pods/* 2 | xcuserdata/ 3 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | inhibit_all_warnings! 4 | 5 | pod 'ReactiveCocoa','2.5' 6 | pod 'AFNetworking-RACExtensions' 7 | pod 'Mantle' 8 | 9 | target 'ReactiveCocoaDemoTests', :exclusive=>true do 10 | pod 'Kiwi' 11 | end 12 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AFNetworking (2.5.4): 3 | - AFNetworking/NSURLConnection (= 2.5.4) 4 | - AFNetworking/NSURLSession (= 2.5.4) 5 | - AFNetworking/Reachability (= 2.5.4) 6 | - AFNetworking/Security (= 2.5.4) 7 | - AFNetworking/Serialization (= 2.5.4) 8 | - AFNetworking/UIKit (= 2.5.4) 9 | - AFNetworking-RACExtensions (0.1.8): 10 | - AFNetworking (~> 2.5.3) 11 | - ReactiveCocoa (~> 2.0) 12 | - AFNetworking/NSURLConnection (2.5.4): 13 | - AFNetworking/Reachability 14 | - AFNetworking/Security 15 | - AFNetworking/Serialization 16 | - AFNetworking/NSURLSession (2.5.4): 17 | - AFNetworking/Reachability 18 | - AFNetworking/Security 19 | - AFNetworking/Serialization 20 | - AFNetworking/Reachability (2.5.4) 21 | - AFNetworking/Security (2.5.4) 22 | - AFNetworking/Serialization (2.5.4) 23 | - AFNetworking/UIKit (2.5.4): 24 | - AFNetworking/NSURLConnection 25 | - AFNetworking/NSURLSession 26 | - Kiwi (2.4.0) 27 | - Mantle (2.0.6): 28 | - Mantle/extobjc (= 2.0.6) 29 | - Mantle/extobjc (2.0.6) 30 | - ReactiveCocoa (2.5): 31 | - ReactiveCocoa/UI (= 2.5) 32 | - ReactiveCocoa/Core (2.5): 33 | - ReactiveCocoa/no-arc 34 | - ReactiveCocoa/no-arc (2.5) 35 | - ReactiveCocoa/UI (2.5): 36 | - ReactiveCocoa/Core 37 | 38 | DEPENDENCIES: 39 | - AFNetworking-RACExtensions 40 | - Kiwi 41 | - Mantle 42 | - ReactiveCocoa (= 2.5) 43 | 44 | SPEC CHECKSUMS: 45 | AFNetworking: 05edc0ac4c4c8cf57bcf4b84be5b0744b6d8e71e 46 | AFNetworking-RACExtensions: a043c6b492fa892395ceb9d20bc4c94e96e08a57 47 | Kiwi: f49c9d54b28917df5928fe44968a39ed198cb8a8 48 | Mantle: 299966b00759634931699f69cb6a30b9239b944d 49 | ReactiveCocoa: e2db045570aa97c695e7aa97c2bcab222ae51f4a 50 | 51 | COCOAPODS: 0.39.0 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ***ReactiveCocoaDemo*** 2 | ====== 3 | 4 | **Demo 概述** 5 | 6 | 7 | 该Demo主要用来测验MVVM模式的分层,使APP更方便维护,测试 8 | 以及练习ReactiveCocoa相关API 9 | 10 | 首页 - 点击‘Load’,请求web service,加载数据,渲染表格 11 | 12 | 列表页 - 点击‘Add’,填写表单,验证,添加数据 13 | 14 | 15 | **涉及概念:** 16 | 17 | 关于ReactiveCocoa,有时并不会减少你的代码量,相反,会使代码 18 | 相对难以理解(习惯就好了),而且也难以调试(可以给每个Signal 设置setNameWithFormat来方便调试)。 19 | 20 | 它可以减少代码的复杂度(复杂项目比较适用),结合MVVM模式使你的APP更好的分层,让每个VC的VM可以进行单元测试,因为VC里只有页面逻辑,VM里包含所有业务逻辑,VM也可以直接当成MODEL,VM是testable的。 21 | 22 | - FRP 23 | - MVVM 24 | - RACCommand 25 | - RACSignal 26 | - RAC绑定宏 27 | - flattenMap 28 | - map 29 | - filter 30 | - rac_signalForSelector 31 | - RACDelegateProxy 32 | 33 | **参考资料:** 34 | 35 | [ReactiveCocoa on Github 36 | ](https://github.com/ReactiveCocoa/ReactiveCocoa) 37 | 38 | [Ray's blog](http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2) 39 | 40 | [Using RACCommand](http://codeblog.shape.dk/blog/2013/12/05/reactivecocoa-essentials-understanding-and-using-raccommand/) 41 | 42 | `PS:` 43 | 用xcode6 bete测试kiwi会crash,改用xcode5 44 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | FF6E5F36197394DC00E4797B /* Documentation */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = FF6E5F39197394DC00E4797B /* Build configuration list for PBXAggregateTarget "Documentation" */; 13 | buildPhases = ( 14 | FF6E5F3A197394EC00E4797B /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | ); 18 | name = Documentation; 19 | productName = Documentation; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 4993923F1C7863590087185C /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4993923E1C7863590087185C /* Launch Screen.storyboard */; }; 25 | 5FE44B00E7F24821978E20C2 /* libPods-ReactiveCocoaDemoTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AAF3A9A3C35248DEBB0BB9B3 /* libPods-ReactiveCocoaDemoTests.a */; }; 26 | A68248665C8E44EDAEDE36E2 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F459F0A87D3B40B581E342B9 /* libPods.a */; }; 27 | FF3283CB194981B20003CEE2 /* City.m in Sources */ = {isa = PBXBuildFile; fileRef = FF3283CA194981B20003CEE2 /* City.m */; }; 28 | FF3C57F11947128C00858CFF /* AddCityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FF3C57F01947128C00858CFF /* AddCityViewController.m */; }; 29 | FF3C57F4194712AF00858CFF /* AddCityViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = FF3C57F3194712AF00858CFF /* AddCityViewModel.m */; }; 30 | FF610676193D705E00D15169 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF610675193D705E00D15169 /* Foundation.framework */; }; 31 | FF610678193D705E00D15169 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF610677193D705E00D15169 /* CoreGraphics.framework */; }; 32 | FF61067A193D705E00D15169 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF610679193D705E00D15169 /* UIKit.framework */; }; 33 | FF610680193D705E00D15169 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FF61067E193D705E00D15169 /* InfoPlist.strings */; }; 34 | FF610682193D705E00D15169 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FF610681193D705E00D15169 /* main.m */; }; 35 | FF610686193D705E00D15169 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FF610685193D705E00D15169 /* AppDelegate.m */; }; 36 | FF610689193D705E00D15169 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FF610687193D705E00D15169 /* Main.storyboard */; }; 37 | FF61068C193D705E00D15169 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FF61068B193D705E00D15169 /* ViewController.m */; }; 38 | FF61068E193D705E00D15169 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FF61068D193D705E00D15169 /* Images.xcassets */; }; 39 | FF610695193D705F00D15169 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF610694193D705F00D15169 /* XCTest.framework */; }; 40 | FF610696193D705F00D15169 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF610675193D705E00D15169 /* Foundation.framework */; }; 41 | FF610697193D705F00D15169 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF610679193D705E00D15169 /* UIKit.framework */; }; 42 | FF61069F193D705F00D15169 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FF61069D193D705F00D15169 /* InfoPlist.strings */; }; 43 | FF697A1619407B2C007BDBD3 /* MyTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FF697A1519407B2C007BDBD3 /* MyTableViewCell.m */; }; 44 | FF81A19519597DC800DB965E /* AddCitySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = FF81A19419597DC800DB965E /* AddCitySpec.m */; }; 45 | FF839E50194AE5FB00D4194B /* CityListSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = FF839E4F194AE5FB00D4194B /* CityListSpec.m */; }; 46 | FFB99D8C1940085E0063C266 /* GeoCityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFB99D8B1940085E0063C266 /* GeoCityViewController.m */; }; 47 | FFB99D8F19400D160063C266 /* GeoCityViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = FFB99D8E19400D160063C266 /* GeoCityViewModel.m */; }; 48 | /* End PBXBuildFile section */ 49 | 50 | /* Begin PBXContainerItemProxy section */ 51 | FF610698193D705F00D15169 /* PBXContainerItemProxy */ = { 52 | isa = PBXContainerItemProxy; 53 | containerPortal = FF61066A193D705E00D15169 /* Project object */; 54 | proxyType = 1; 55 | remoteGlobalIDString = FF610671193D705E00D15169; 56 | remoteInfo = ReactiveCocoaDemo; 57 | }; 58 | FF839E45194AE25400D4194B /* PBXContainerItemProxy */ = { 59 | isa = PBXContainerItemProxy; 60 | containerPortal = FF61066A193D705E00D15169 /* Project object */; 61 | proxyType = 1; 62 | remoteGlobalIDString = FF610671193D705E00D15169; 63 | remoteInfo = ReactiveCocoaDemo; 64 | }; 65 | FF839E47194AE25400D4194B /* PBXContainerItemProxy */ = { 66 | isa = PBXContainerItemProxy; 67 | containerPortal = FF61066A193D705E00D15169 /* Project object */; 68 | proxyType = 1; 69 | remoteGlobalIDString = FF610671193D705E00D15169; 70 | remoteInfo = ReactiveCocoaDemo; 71 | }; 72 | FF839E49194AE27300D4194B /* PBXContainerItemProxy */ = { 73 | isa = PBXContainerItemProxy; 74 | containerPortal = FF61066A193D705E00D15169 /* Project object */; 75 | proxyType = 1; 76 | remoteGlobalIDString = FF610671193D705E00D15169; 77 | remoteInfo = ReactiveCocoaDemo; 78 | }; 79 | /* End PBXContainerItemProxy section */ 80 | 81 | /* Begin PBXFileReference section */ 82 | 08D355651F2B1B29710A57D8 /* Pods-ReactiveCocoaDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactiveCocoaDemoTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ReactiveCocoaDemoTests/Pods-ReactiveCocoaDemoTests.debug.xcconfig"; sourceTree = ""; }; 83 | 4993923E1C7863590087185C /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; 84 | 520F4780D079D1AB09FBBA0C /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; 85 | 65301E41A818C6DE7E4A3CF5 /* Pods-ReactiveCocoaDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactiveCocoaDemoTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ReactiveCocoaDemoTests/Pods-ReactiveCocoaDemoTests.release.xcconfig"; sourceTree = ""; }; 86 | 97CD1FE0BD151E3784468D4C /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; 87 | AAF3A9A3C35248DEBB0BB9B3 /* libPods-ReactiveCocoaDemoTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactiveCocoaDemoTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 88 | F459F0A87D3B40B581E342B9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 89 | FF3283C9194981B20003CEE2 /* City.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = City.h; sourceTree = ""; }; 90 | FF3283CA194981B20003CEE2 /* City.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = City.m; sourceTree = ""; }; 91 | FF3C57EF1947128C00858CFF /* AddCityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddCityViewController.h; sourceTree = ""; }; 92 | FF3C57F01947128C00858CFF /* AddCityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddCityViewController.m; sourceTree = ""; }; 93 | FF3C57F2194712AF00858CFF /* AddCityViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddCityViewModel.h; sourceTree = ""; }; 94 | FF3C57F3194712AF00858CFF /* AddCityViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddCityViewModel.m; sourceTree = ""; }; 95 | FF610672193D705E00D15169 /* ReactiveCocoaDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReactiveCocoaDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 96 | FF610675193D705E00D15169 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 97 | FF610677193D705E00D15169 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 98 | FF610679193D705E00D15169 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 99 | FF61067D193D705E00D15169 /* ReactiveCocoaDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ReactiveCocoaDemo-Info.plist"; sourceTree = ""; }; 100 | FF61067F193D705E00D15169 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 101 | FF610681193D705E00D15169 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 102 | FF610683193D705E00D15169 /* ReactiveCocoaDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactiveCocoaDemo-Prefix.pch"; sourceTree = ""; }; 103 | FF610684193D705E00D15169 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 104 | FF610685193D705E00D15169 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 105 | FF610688193D705E00D15169 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 106 | FF61068A193D705E00D15169 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 107 | FF61068B193D705E00D15169 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 108 | FF61068D193D705E00D15169 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 109 | FF610693193D705F00D15169 /* ReactiveCocoaDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactiveCocoaDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 110 | FF610694193D705F00D15169 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 111 | FF61069C193D705F00D15169 /* ReactiveCocoaDemoTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ReactiveCocoaDemoTests-Info.plist"; sourceTree = ""; }; 112 | FF61069E193D705F00D15169 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 113 | FF697A1419407B2C007BDBD3 /* MyTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyTableViewCell.h; sourceTree = ""; }; 114 | FF697A1519407B2C007BDBD3 /* MyTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyTableViewCell.m; sourceTree = ""; }; 115 | FF81A19419597DC800DB965E /* AddCitySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddCitySpec.m; sourceTree = ""; }; 116 | FF839E4F194AE5FB00D4194B /* CityListSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CityListSpec.m; sourceTree = ""; }; 117 | FFB99D8A1940085E0063C266 /* GeoCityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeoCityViewController.h; sourceTree = ""; }; 118 | FFB99D8B1940085E0063C266 /* GeoCityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeoCityViewController.m; sourceTree = ""; }; 119 | FFB99D8D19400D160063C266 /* GeoCityViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeoCityViewModel.h; sourceTree = ""; }; 120 | FFB99D8E19400D160063C266 /* GeoCityViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeoCityViewModel.m; sourceTree = ""; }; 121 | /* End PBXFileReference section */ 122 | 123 | /* Begin PBXFrameworksBuildPhase section */ 124 | FF61066F193D705E00D15169 /* Frameworks */ = { 125 | isa = PBXFrameworksBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | FF610678193D705E00D15169 /* CoreGraphics.framework in Frameworks */, 129 | FF61067A193D705E00D15169 /* UIKit.framework in Frameworks */, 130 | FF610676193D705E00D15169 /* Foundation.framework in Frameworks */, 131 | A68248665C8E44EDAEDE36E2 /* libPods.a in Frameworks */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | FF610690193D705F00D15169 /* Frameworks */ = { 136 | isa = PBXFrameworksBuildPhase; 137 | buildActionMask = 2147483647; 138 | files = ( 139 | FF610695193D705F00D15169 /* XCTest.framework in Frameworks */, 140 | FF610697193D705F00D15169 /* UIKit.framework in Frameworks */, 141 | FF610696193D705F00D15169 /* Foundation.framework in Frameworks */, 142 | 5FE44B00E7F24821978E20C2 /* libPods-ReactiveCocoaDemoTests.a in Frameworks */, 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | /* End PBXFrameworksBuildPhase section */ 147 | 148 | /* Begin PBXGroup section */ 149 | DFD13B03E44561F3CF4120A9 /* Pods */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 520F4780D079D1AB09FBBA0C /* Pods.debug.xcconfig */, 153 | 97CD1FE0BD151E3784468D4C /* Pods.release.xcconfig */, 154 | 08D355651F2B1B29710A57D8 /* Pods-ReactiveCocoaDemoTests.debug.xcconfig */, 155 | 65301E41A818C6DE7E4A3CF5 /* Pods-ReactiveCocoaDemoTests.release.xcconfig */, 156 | ); 157 | name = Pods; 158 | sourceTree = ""; 159 | }; 160 | FF3283C419485E970003CEE2 /* CityList */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | FFB99D8A1940085E0063C266 /* GeoCityViewController.h */, 164 | FFB99D8B1940085E0063C266 /* GeoCityViewController.m */, 165 | FF697A1419407B2C007BDBD3 /* MyTableViewCell.h */, 166 | FF697A1519407B2C007BDBD3 /* MyTableViewCell.m */, 167 | ); 168 | name = CityList; 169 | sourceTree = ""; 170 | }; 171 | FF3283C519485EAC0003CEE2 /* AddCity */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | FF3C57EF1947128C00858CFF /* AddCityViewController.h */, 175 | FF3C57F01947128C00858CFF /* AddCityViewController.m */, 176 | ); 177 | name = AddCity; 178 | sourceTree = ""; 179 | }; 180 | FF3283C619485EB90003CEE2 /* CityList */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | FFB99D8D19400D160063C266 /* GeoCityViewModel.h */, 184 | FFB99D8E19400D160063C266 /* GeoCityViewModel.m */, 185 | ); 186 | name = CityList; 187 | sourceTree = ""; 188 | }; 189 | FF3283C719485EC50003CEE2 /* AddCity */ = { 190 | isa = PBXGroup; 191 | children = ( 192 | FF3C57F2194712AF00858CFF /* AddCityViewModel.h */, 193 | FF3C57F3194712AF00858CFF /* AddCityViewModel.m */, 194 | ); 195 | name = AddCity; 196 | sourceTree = ""; 197 | }; 198 | FF3283C81949819C0003CEE2 /* Models */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | FF3283C9194981B20003CEE2 /* City.h */, 202 | FF3283CA194981B20003CEE2 /* City.m */, 203 | ); 204 | name = Models; 205 | sourceTree = ""; 206 | }; 207 | FF3C57ED1946AA3200858CFF /* Views */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | FF3283C519485EAC0003CEE2 /* AddCity */, 211 | FF3283C419485E970003CEE2 /* CityList */, 212 | ); 213 | name = Views; 214 | sourceTree = ""; 215 | }; 216 | FF3C57EE1946AA3A00858CFF /* ViewModels */ = { 217 | isa = PBXGroup; 218 | children = ( 219 | FF3283C719485EC50003CEE2 /* AddCity */, 220 | FF3283C619485EB90003CEE2 /* CityList */, 221 | ); 222 | name = ViewModels; 223 | sourceTree = ""; 224 | }; 225 | FF610669193D705E00D15169 = { 226 | isa = PBXGroup; 227 | children = ( 228 | FF61067B193D705E00D15169 /* ReactiveCocoaDemo */, 229 | FF61069A193D705F00D15169 /* ReactiveCocoaDemoTests */, 230 | FF610674193D705E00D15169 /* Frameworks */, 231 | FF610673193D705E00D15169 /* Products */, 232 | DFD13B03E44561F3CF4120A9 /* Pods */, 233 | ); 234 | sourceTree = ""; 235 | }; 236 | FF610673193D705E00D15169 /* Products */ = { 237 | isa = PBXGroup; 238 | children = ( 239 | FF610672193D705E00D15169 /* ReactiveCocoaDemo.app */, 240 | FF610693193D705F00D15169 /* ReactiveCocoaDemoTests.xctest */, 241 | ); 242 | name = Products; 243 | sourceTree = ""; 244 | }; 245 | FF610674193D705E00D15169 /* Frameworks */ = { 246 | isa = PBXGroup; 247 | children = ( 248 | FF610675193D705E00D15169 /* Foundation.framework */, 249 | FF610677193D705E00D15169 /* CoreGraphics.framework */, 250 | FF610679193D705E00D15169 /* UIKit.framework */, 251 | FF610694193D705F00D15169 /* XCTest.framework */, 252 | F459F0A87D3B40B581E342B9 /* libPods.a */, 253 | AAF3A9A3C35248DEBB0BB9B3 /* libPods-ReactiveCocoaDemoTests.a */, 254 | ); 255 | name = Frameworks; 256 | sourceTree = ""; 257 | }; 258 | FF61067B193D705E00D15169 /* ReactiveCocoaDemo */ = { 259 | isa = PBXGroup; 260 | children = ( 261 | FF3283C81949819C0003CEE2 /* Models */, 262 | FF3C57EE1946AA3A00858CFF /* ViewModels */, 263 | FF3C57ED1946AA3200858CFF /* Views */, 264 | FF610684193D705E00D15169 /* AppDelegate.h */, 265 | FF610685193D705E00D15169 /* AppDelegate.m */, 266 | FF610687193D705E00D15169 /* Main.storyboard */, 267 | FF61068A193D705E00D15169 /* ViewController.h */, 268 | FF61068B193D705E00D15169 /* ViewController.m */, 269 | 4993923E1C7863590087185C /* Launch Screen.storyboard */, 270 | FF61068D193D705E00D15169 /* Images.xcassets */, 271 | FF61067C193D705E00D15169 /* Supporting Files */, 272 | ); 273 | path = ReactiveCocoaDemo; 274 | sourceTree = ""; 275 | }; 276 | FF61067C193D705E00D15169 /* Supporting Files */ = { 277 | isa = PBXGroup; 278 | children = ( 279 | FF61067D193D705E00D15169 /* ReactiveCocoaDemo-Info.plist */, 280 | FF61067E193D705E00D15169 /* InfoPlist.strings */, 281 | FF610681193D705E00D15169 /* main.m */, 282 | FF610683193D705E00D15169 /* ReactiveCocoaDemo-Prefix.pch */, 283 | ); 284 | name = "Supporting Files"; 285 | sourceTree = ""; 286 | }; 287 | FF61069A193D705F00D15169 /* ReactiveCocoaDemoTests */ = { 288 | isa = PBXGroup; 289 | children = ( 290 | FF61069B193D705F00D15169 /* Supporting Files */, 291 | FF839E4F194AE5FB00D4194B /* CityListSpec.m */, 292 | FF81A19419597DC800DB965E /* AddCitySpec.m */, 293 | ); 294 | path = ReactiveCocoaDemoTests; 295 | sourceTree = ""; 296 | }; 297 | FF61069B193D705F00D15169 /* Supporting Files */ = { 298 | isa = PBXGroup; 299 | children = ( 300 | FF61069C193D705F00D15169 /* ReactiveCocoaDemoTests-Info.plist */, 301 | FF61069D193D705F00D15169 /* InfoPlist.strings */, 302 | ); 303 | name = "Supporting Files"; 304 | sourceTree = ""; 305 | }; 306 | /* End PBXGroup section */ 307 | 308 | /* Begin PBXNativeTarget section */ 309 | FF610671193D705E00D15169 /* ReactiveCocoaDemo */ = { 310 | isa = PBXNativeTarget; 311 | buildConfigurationList = FF6106A4193D705F00D15169 /* Build configuration list for PBXNativeTarget "ReactiveCocoaDemo" */; 312 | buildPhases = ( 313 | C623C7F936154503B7776551 /* Check Pods Manifest.lock */, 314 | FF61066E193D705E00D15169 /* Sources */, 315 | FF61066F193D705E00D15169 /* Frameworks */, 316 | FF610670193D705E00D15169 /* Resources */, 317 | 943D2FA8E1244697AB4F7BC6 /* Copy Pods Resources */, 318 | 0AB65B267AAAAAF4FB32658B /* Embed Pods Frameworks */, 319 | ); 320 | buildRules = ( 321 | ); 322 | dependencies = ( 323 | ); 324 | name = ReactiveCocoaDemo; 325 | productName = ReactiveCocoaDemo; 326 | productReference = FF610672193D705E00D15169 /* ReactiveCocoaDemo.app */; 327 | productType = "com.apple.product-type.application"; 328 | }; 329 | FF610692193D705F00D15169 /* ReactiveCocoaDemoTests */ = { 330 | isa = PBXNativeTarget; 331 | buildConfigurationList = FF6106A7193D705F00D15169 /* Build configuration list for PBXNativeTarget "ReactiveCocoaDemoTests" */; 332 | buildPhases = ( 333 | 89504A1DC98C4210B60336E7 /* Check Pods Manifest.lock */, 334 | FF61068F193D705F00D15169 /* Sources */, 335 | FF610690193D705F00D15169 /* Frameworks */, 336 | FF610691193D705F00D15169 /* Resources */, 337 | 5A15E7C872064D77B1B4EFB4 /* Copy Pods Resources */, 338 | 2CA1572BDDDB2436E65617C3 /* Embed Pods Frameworks */, 339 | ); 340 | buildRules = ( 341 | ); 342 | dependencies = ( 343 | FF610699193D705F00D15169 /* PBXTargetDependency */, 344 | FF839E46194AE25400D4194B /* PBXTargetDependency */, 345 | FF839E48194AE25400D4194B /* PBXTargetDependency */, 346 | FF839E4A194AE27300D4194B /* PBXTargetDependency */, 347 | ); 348 | name = ReactiveCocoaDemoTests; 349 | productName = ReactiveCocoaDemoTests; 350 | productReference = FF610693193D705F00D15169 /* ReactiveCocoaDemoTests.xctest */; 351 | productType = "com.apple.product-type.bundle.unit-test"; 352 | }; 353 | /* End PBXNativeTarget section */ 354 | 355 | /* Begin PBXProject section */ 356 | FF61066A193D705E00D15169 /* Project object */ = { 357 | isa = PBXProject; 358 | attributes = { 359 | LastUpgradeCheck = 0720; 360 | ORGANIZATIONNAME = Anjuke; 361 | TargetAttributes = { 362 | FF610692193D705F00D15169 = { 363 | TestTargetID = FF610671193D705E00D15169; 364 | }; 365 | }; 366 | }; 367 | buildConfigurationList = FF61066D193D705E00D15169 /* Build configuration list for PBXProject "ReactiveCocoaDemo" */; 368 | compatibilityVersion = "Xcode 3.2"; 369 | developmentRegion = English; 370 | hasScannedForEncodings = 0; 371 | knownRegions = ( 372 | en, 373 | Base, 374 | ); 375 | mainGroup = FF610669193D705E00D15169; 376 | productRefGroup = FF610673193D705E00D15169 /* Products */; 377 | projectDirPath = ""; 378 | projectRoot = ""; 379 | targets = ( 380 | FF610671193D705E00D15169 /* ReactiveCocoaDemo */, 381 | FF610692193D705F00D15169 /* ReactiveCocoaDemoTests */, 382 | FF6E5F36197394DC00E4797B /* Documentation */, 383 | ); 384 | }; 385 | /* End PBXProject section */ 386 | 387 | /* Begin PBXResourcesBuildPhase section */ 388 | FF610670193D705E00D15169 /* Resources */ = { 389 | isa = PBXResourcesBuildPhase; 390 | buildActionMask = 2147483647; 391 | files = ( 392 | FF61068E193D705E00D15169 /* Images.xcassets in Resources */, 393 | FF610680193D705E00D15169 /* InfoPlist.strings in Resources */, 394 | 4993923F1C7863590087185C /* Launch Screen.storyboard in Resources */, 395 | FF610689193D705E00D15169 /* Main.storyboard in Resources */, 396 | ); 397 | runOnlyForDeploymentPostprocessing = 0; 398 | }; 399 | FF610691193D705F00D15169 /* Resources */ = { 400 | isa = PBXResourcesBuildPhase; 401 | buildActionMask = 2147483647; 402 | files = ( 403 | FF61069F193D705F00D15169 /* InfoPlist.strings in Resources */, 404 | ); 405 | runOnlyForDeploymentPostprocessing = 0; 406 | }; 407 | /* End PBXResourcesBuildPhase section */ 408 | 409 | /* Begin PBXShellScriptBuildPhase section */ 410 | 0AB65B267AAAAAF4FB32658B /* Embed Pods Frameworks */ = { 411 | isa = PBXShellScriptBuildPhase; 412 | buildActionMask = 2147483647; 413 | files = ( 414 | ); 415 | inputPaths = ( 416 | ); 417 | name = "Embed Pods Frameworks"; 418 | outputPaths = ( 419 | ); 420 | runOnlyForDeploymentPostprocessing = 0; 421 | shellPath = /bin/sh; 422 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; 423 | showEnvVarsInLog = 0; 424 | }; 425 | 2CA1572BDDDB2436E65617C3 /* Embed Pods Frameworks */ = { 426 | isa = PBXShellScriptBuildPhase; 427 | buildActionMask = 2147483647; 428 | files = ( 429 | ); 430 | inputPaths = ( 431 | ); 432 | name = "Embed Pods Frameworks"; 433 | outputPaths = ( 434 | ); 435 | runOnlyForDeploymentPostprocessing = 0; 436 | shellPath = /bin/sh; 437 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ReactiveCocoaDemoTests/Pods-ReactiveCocoaDemoTests-frameworks.sh\"\n"; 438 | showEnvVarsInLog = 0; 439 | }; 440 | 5A15E7C872064D77B1B4EFB4 /* Copy Pods Resources */ = { 441 | isa = PBXShellScriptBuildPhase; 442 | buildActionMask = 2147483647; 443 | files = ( 444 | ); 445 | inputPaths = ( 446 | ); 447 | name = "Copy Pods Resources"; 448 | outputPaths = ( 449 | ); 450 | runOnlyForDeploymentPostprocessing = 0; 451 | shellPath = /bin/sh; 452 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ReactiveCocoaDemoTests/Pods-ReactiveCocoaDemoTests-resources.sh\"\n"; 453 | showEnvVarsInLog = 0; 454 | }; 455 | 89504A1DC98C4210B60336E7 /* Check Pods Manifest.lock */ = { 456 | isa = PBXShellScriptBuildPhase; 457 | buildActionMask = 2147483647; 458 | files = ( 459 | ); 460 | inputPaths = ( 461 | ); 462 | name = "Check Pods Manifest.lock"; 463 | outputPaths = ( 464 | ); 465 | runOnlyForDeploymentPostprocessing = 0; 466 | shellPath = /bin/sh; 467 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 468 | showEnvVarsInLog = 0; 469 | }; 470 | 943D2FA8E1244697AB4F7BC6 /* Copy Pods Resources */ = { 471 | isa = PBXShellScriptBuildPhase; 472 | buildActionMask = 2147483647; 473 | files = ( 474 | ); 475 | inputPaths = ( 476 | ); 477 | name = "Copy Pods Resources"; 478 | outputPaths = ( 479 | ); 480 | runOnlyForDeploymentPostprocessing = 0; 481 | shellPath = /bin/sh; 482 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; 483 | showEnvVarsInLog = 0; 484 | }; 485 | C623C7F936154503B7776551 /* Check Pods Manifest.lock */ = { 486 | isa = PBXShellScriptBuildPhase; 487 | buildActionMask = 2147483647; 488 | files = ( 489 | ); 490 | inputPaths = ( 491 | ); 492 | name = "Check Pods Manifest.lock"; 493 | outputPaths = ( 494 | ); 495 | runOnlyForDeploymentPostprocessing = 0; 496 | shellPath = /bin/sh; 497 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 498 | showEnvVarsInLog = 0; 499 | }; 500 | FF6E5F3A197394EC00E4797B /* ShellScript */ = { 501 | isa = PBXShellScriptBuildPhase; 502 | buildActionMask = 2147483647; 503 | files = ( 504 | ); 505 | inputPaths = ( 506 | ); 507 | outputPaths = ( 508 | ); 509 | runOnlyForDeploymentPostprocessing = 0; 510 | shellPath = /bin/sh; 511 | shellScript = "#appledoc Xcode script\n# Start constants\ncompany=\"ACME\";\ncompanyID=\"com.ACME\";\ncompanyURL=\"http://ACME.com\";\ntarget=\"iphoneos\";\n#target=\"macosx\";\noutputPath=\"~/help\";\n# End constants\n/usr/local/bin/appledoc \\\n--project-name \"${PROJECT_NAME}\" \\\n--project-company \"${company}\" \\\n--company-id \"${companyID}\" \\\n--docset-atom-filename \"${company}.atom\" \\\n--docset-feed-url \"${companyURL}/${company}/%DOCSETATOMFILENAME\" \\\n--docset-package-url \"${companyURL}/${company}/%DOCSETPACKAGEFILENAME\" \\\n--docset-fallback-url \"${companyURL}/${company}\" \\\n--output \"/Users/dajing/anjuke\" \\\n--publish-docset \\\n--docset-platform-family \"${target}\" \\\n--logformat xcode \\\n--keep-intermediate-files \\\n--no-repeat-first-par \\\n--no-warn-invalid-crossref \\\n--exit-threshold 2 \\\n\"${PROJECT_DIR}\"\n"; 512 | }; 513 | /* End PBXShellScriptBuildPhase section */ 514 | 515 | /* Begin PBXSourcesBuildPhase section */ 516 | FF61066E193D705E00D15169 /* Sources */ = { 517 | isa = PBXSourcesBuildPhase; 518 | buildActionMask = 2147483647; 519 | files = ( 520 | FF61068C193D705E00D15169 /* ViewController.m in Sources */, 521 | FF697A1619407B2C007BDBD3 /* MyTableViewCell.m in Sources */, 522 | FFB99D8F19400D160063C266 /* GeoCityViewModel.m in Sources */, 523 | FF3C57F4194712AF00858CFF /* AddCityViewModel.m in Sources */, 524 | FF3C57F11947128C00858CFF /* AddCityViewController.m in Sources */, 525 | FFB99D8C1940085E0063C266 /* GeoCityViewController.m in Sources */, 526 | FF610686193D705E00D15169 /* AppDelegate.m in Sources */, 527 | FF610682193D705E00D15169 /* main.m in Sources */, 528 | FF3283CB194981B20003CEE2 /* City.m in Sources */, 529 | ); 530 | runOnlyForDeploymentPostprocessing = 0; 531 | }; 532 | FF61068F193D705F00D15169 /* Sources */ = { 533 | isa = PBXSourcesBuildPhase; 534 | buildActionMask = 2147483647; 535 | files = ( 536 | FF839E50194AE5FB00D4194B /* CityListSpec.m in Sources */, 537 | FF81A19519597DC800DB965E /* AddCitySpec.m in Sources */, 538 | ); 539 | runOnlyForDeploymentPostprocessing = 0; 540 | }; 541 | /* End PBXSourcesBuildPhase section */ 542 | 543 | /* Begin PBXTargetDependency section */ 544 | FF610699193D705F00D15169 /* PBXTargetDependency */ = { 545 | isa = PBXTargetDependency; 546 | target = FF610671193D705E00D15169 /* ReactiveCocoaDemo */; 547 | targetProxy = FF610698193D705F00D15169 /* PBXContainerItemProxy */; 548 | }; 549 | FF839E46194AE25400D4194B /* PBXTargetDependency */ = { 550 | isa = PBXTargetDependency; 551 | target = FF610671193D705E00D15169 /* ReactiveCocoaDemo */; 552 | targetProxy = FF839E45194AE25400D4194B /* PBXContainerItemProxy */; 553 | }; 554 | FF839E48194AE25400D4194B /* PBXTargetDependency */ = { 555 | isa = PBXTargetDependency; 556 | target = FF610671193D705E00D15169 /* ReactiveCocoaDemo */; 557 | targetProxy = FF839E47194AE25400D4194B /* PBXContainerItemProxy */; 558 | }; 559 | FF839E4A194AE27300D4194B /* PBXTargetDependency */ = { 560 | isa = PBXTargetDependency; 561 | target = FF610671193D705E00D15169 /* ReactiveCocoaDemo */; 562 | targetProxy = FF839E49194AE27300D4194B /* PBXContainerItemProxy */; 563 | }; 564 | /* End PBXTargetDependency section */ 565 | 566 | /* Begin PBXVariantGroup section */ 567 | FF61067E193D705E00D15169 /* InfoPlist.strings */ = { 568 | isa = PBXVariantGroup; 569 | children = ( 570 | FF61067F193D705E00D15169 /* en */, 571 | ); 572 | name = InfoPlist.strings; 573 | sourceTree = ""; 574 | }; 575 | FF610687193D705E00D15169 /* Main.storyboard */ = { 576 | isa = PBXVariantGroup; 577 | children = ( 578 | FF610688193D705E00D15169 /* Base */, 579 | ); 580 | name = Main.storyboard; 581 | sourceTree = ""; 582 | }; 583 | FF61069D193D705F00D15169 /* InfoPlist.strings */ = { 584 | isa = PBXVariantGroup; 585 | children = ( 586 | FF61069E193D705F00D15169 /* en */, 587 | ); 588 | name = InfoPlist.strings; 589 | sourceTree = ""; 590 | }; 591 | /* End PBXVariantGroup section */ 592 | 593 | /* Begin XCBuildConfiguration section */ 594 | FF6106A2193D705F00D15169 /* Debug */ = { 595 | isa = XCBuildConfiguration; 596 | buildSettings = { 597 | ALWAYS_SEARCH_USER_PATHS = NO; 598 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 599 | CLANG_CXX_LIBRARY = "libc++"; 600 | CLANG_ENABLE_MODULES = YES; 601 | CLANG_ENABLE_OBJC_ARC = YES; 602 | CLANG_WARN_BOOL_CONVERSION = YES; 603 | CLANG_WARN_CONSTANT_CONVERSION = YES; 604 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 605 | CLANG_WARN_EMPTY_BODY = YES; 606 | CLANG_WARN_ENUM_CONVERSION = YES; 607 | CLANG_WARN_INT_CONVERSION = YES; 608 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 609 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 610 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 611 | COPY_PHASE_STRIP = NO; 612 | ENABLE_TESTABILITY = YES; 613 | GCC_C_LANGUAGE_STANDARD = gnu99; 614 | GCC_DYNAMIC_NO_PIC = NO; 615 | GCC_OPTIMIZATION_LEVEL = 0; 616 | GCC_PREPROCESSOR_DEFINITIONS = ( 617 | "DEBUG=1", 618 | "$(inherited)", 619 | ); 620 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 621 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 622 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 623 | GCC_WARN_UNDECLARED_SELECTOR = YES; 624 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 625 | GCC_WARN_UNUSED_FUNCTION = YES; 626 | GCC_WARN_UNUSED_VARIABLE = YES; 627 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 628 | ONLY_ACTIVE_ARCH = YES; 629 | SDKROOT = iphoneos; 630 | }; 631 | name = Debug; 632 | }; 633 | FF6106A3193D705F00D15169 /* Release */ = { 634 | isa = XCBuildConfiguration; 635 | buildSettings = { 636 | ALWAYS_SEARCH_USER_PATHS = NO; 637 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 638 | CLANG_CXX_LIBRARY = "libc++"; 639 | CLANG_ENABLE_MODULES = YES; 640 | CLANG_ENABLE_OBJC_ARC = YES; 641 | CLANG_WARN_BOOL_CONVERSION = YES; 642 | CLANG_WARN_CONSTANT_CONVERSION = YES; 643 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 644 | CLANG_WARN_EMPTY_BODY = YES; 645 | CLANG_WARN_ENUM_CONVERSION = YES; 646 | CLANG_WARN_INT_CONVERSION = YES; 647 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 648 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 649 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 650 | COPY_PHASE_STRIP = YES; 651 | ENABLE_NS_ASSERTIONS = NO; 652 | GCC_C_LANGUAGE_STANDARD = gnu99; 653 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 654 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 655 | GCC_WARN_UNDECLARED_SELECTOR = YES; 656 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 657 | GCC_WARN_UNUSED_FUNCTION = YES; 658 | GCC_WARN_UNUSED_VARIABLE = YES; 659 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 660 | SDKROOT = iphoneos; 661 | VALIDATE_PRODUCT = YES; 662 | }; 663 | name = Release; 664 | }; 665 | FF6106A5193D705F00D15169 /* Debug */ = { 666 | isa = XCBuildConfiguration; 667 | baseConfigurationReference = 520F4780D079D1AB09FBBA0C /* Pods.debug.xcconfig */; 668 | buildSettings = { 669 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 670 | CODE_SIGN_IDENTITY = "iPhone Developer"; 671 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 672 | GCC_PREFIX_HEADER = "ReactiveCocoaDemo/ReactiveCocoaDemo-Prefix.pch"; 673 | INFOPLIST_FILE = "ReactiveCocoaDemo/ReactiveCocoaDemo-Info.plist"; 674 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 675 | PRODUCT_BUNDLE_IDENTIFIER = net.meeno.com.Anjuke.ReactiveCocoaDemo; 676 | PRODUCT_NAME = "$(TARGET_NAME)"; 677 | PROVISIONING_PROFILE = "b0f20621-4f07-4f38-b4c0-c441a18dff88"; 678 | WRAPPER_EXTENSION = app; 679 | }; 680 | name = Debug; 681 | }; 682 | FF6106A6193D705F00D15169 /* Release */ = { 683 | isa = XCBuildConfiguration; 684 | baseConfigurationReference = 97CD1FE0BD151E3784468D4C /* Pods.release.xcconfig */; 685 | buildSettings = { 686 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 687 | CODE_SIGN_IDENTITY = "iPhone Developer"; 688 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 689 | GCC_PREFIX_HEADER = "ReactiveCocoaDemo/ReactiveCocoaDemo-Prefix.pch"; 690 | INFOPLIST_FILE = "ReactiveCocoaDemo/ReactiveCocoaDemo-Info.plist"; 691 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 692 | PRODUCT_BUNDLE_IDENTIFIER = net.meeno.com.Anjuke.ReactiveCocoaDemo; 693 | PRODUCT_NAME = "$(TARGET_NAME)"; 694 | PROVISIONING_PROFILE = "b0f20621-4f07-4f38-b4c0-c441a18dff88"; 695 | WRAPPER_EXTENSION = app; 696 | }; 697 | name = Release; 698 | }; 699 | FF6106A8193D705F00D15169 /* Debug */ = { 700 | isa = XCBuildConfiguration; 701 | baseConfigurationReference = 08D355651F2B1B29710A57D8 /* Pods-ReactiveCocoaDemoTests.debug.xcconfig */; 702 | buildSettings = { 703 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ReactiveCocoaDemo.app/ReactiveCocoaDemo"; 704 | FRAMEWORK_SEARCH_PATHS = ( 705 | "$(SDKROOT)/Developer/Library/Frameworks", 706 | "$(inherited)", 707 | "$(DEVELOPER_FRAMEWORKS_DIR)", 708 | ); 709 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 710 | GCC_PREFIX_HEADER = "ReactiveCocoaDemo/ReactiveCocoaDemo-Prefix.pch"; 711 | GCC_PREPROCESSOR_DEFINITIONS = ( 712 | "DEBUG=1", 713 | "$(inherited)", 714 | ); 715 | INFOPLIST_FILE = "ReactiveCocoaDemoTests/ReactiveCocoaDemoTests-Info.plist"; 716 | PRODUCT_BUNDLE_IDENTIFIER = "com.Anjuke.${PRODUCT_NAME:rfc1034identifier}"; 717 | PRODUCT_NAME = "$(TARGET_NAME)"; 718 | TEST_HOST = "$(BUNDLE_LOADER)"; 719 | WRAPPER_EXTENSION = xctest; 720 | }; 721 | name = Debug; 722 | }; 723 | FF6106A9193D705F00D15169 /* Release */ = { 724 | isa = XCBuildConfiguration; 725 | baseConfigurationReference = 65301E41A818C6DE7E4A3CF5 /* Pods-ReactiveCocoaDemoTests.release.xcconfig */; 726 | buildSettings = { 727 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ReactiveCocoaDemo.app/ReactiveCocoaDemo"; 728 | FRAMEWORK_SEARCH_PATHS = ( 729 | "$(SDKROOT)/Developer/Library/Frameworks", 730 | "$(inherited)", 731 | "$(DEVELOPER_FRAMEWORKS_DIR)", 732 | ); 733 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 734 | GCC_PREFIX_HEADER = "ReactiveCocoaDemo/ReactiveCocoaDemo-Prefix.pch"; 735 | INFOPLIST_FILE = "ReactiveCocoaDemoTests/ReactiveCocoaDemoTests-Info.plist"; 736 | PRODUCT_BUNDLE_IDENTIFIER = "com.Anjuke.${PRODUCT_NAME:rfc1034identifier}"; 737 | PRODUCT_NAME = "$(TARGET_NAME)"; 738 | TEST_HOST = "$(BUNDLE_LOADER)"; 739 | WRAPPER_EXTENSION = xctest; 740 | }; 741 | name = Release; 742 | }; 743 | FF6E5F37197394DC00E4797B /* Debug */ = { 744 | isa = XCBuildConfiguration; 745 | buildSettings = { 746 | PRODUCT_NAME = "$(TARGET_NAME)"; 747 | }; 748 | name = Debug; 749 | }; 750 | FF6E5F38197394DC00E4797B /* Release */ = { 751 | isa = XCBuildConfiguration; 752 | buildSettings = { 753 | PRODUCT_NAME = "$(TARGET_NAME)"; 754 | }; 755 | name = Release; 756 | }; 757 | /* End XCBuildConfiguration section */ 758 | 759 | /* Begin XCConfigurationList section */ 760 | FF61066D193D705E00D15169 /* Build configuration list for PBXProject "ReactiveCocoaDemo" */ = { 761 | isa = XCConfigurationList; 762 | buildConfigurations = ( 763 | FF6106A2193D705F00D15169 /* Debug */, 764 | FF6106A3193D705F00D15169 /* Release */, 765 | ); 766 | defaultConfigurationIsVisible = 0; 767 | defaultConfigurationName = Release; 768 | }; 769 | FF6106A4193D705F00D15169 /* Build configuration list for PBXNativeTarget "ReactiveCocoaDemo" */ = { 770 | isa = XCConfigurationList; 771 | buildConfigurations = ( 772 | FF6106A5193D705F00D15169 /* Debug */, 773 | FF6106A6193D705F00D15169 /* Release */, 774 | ); 775 | defaultConfigurationIsVisible = 0; 776 | defaultConfigurationName = Release; 777 | }; 778 | FF6106A7193D705F00D15169 /* Build configuration list for PBXNativeTarget "ReactiveCocoaDemoTests" */ = { 779 | isa = XCConfigurationList; 780 | buildConfigurations = ( 781 | FF6106A8193D705F00D15169 /* Debug */, 782 | FF6106A9193D705F00D15169 /* Release */, 783 | ); 784 | defaultConfigurationIsVisible = 0; 785 | defaultConfigurationName = Release; 786 | }; 787 | FF6E5F39197394DC00E4797B /* Build configuration list for PBXAggregateTarget "Documentation" */ = { 788 | isa = XCConfigurationList; 789 | buildConfigurations = ( 790 | FF6E5F37197394DC00E4797B /* Debug */, 791 | FF6E5F38197394DC00E4797B /* Release */, 792 | ); 793 | defaultConfigurationIsVisible = 0; 794 | defaultConfigurationName = Release; 795 | }; 796 | /* End XCConfigurationList section */ 797 | }; 798 | rootObject = FF61066A193D705E00D15169 /* Project object */; 799 | } 800 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/xcuserdata/dajing.xcuserdatad/xcschemes/Documentation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/xcuserdata/dajing.xcuserdatad/xcschemes/ReactiveCocoaDemo.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 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/xcuserdata/dajing.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Documentation.xcscheme 8 | 9 | orderHint 10 | 9 11 | 12 | ReactiveCocoaDemo.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | FF610671193D705E00D15169 21 | 22 | primary 23 | 24 | 25 | FF610692193D705F00D15169 26 | 27 | primary 28 | 29 | 30 | FF6E5F36197394DC00E4797B 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/xcuserdata/ddaajing.xcuserdatad/xcschemes/Documentation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/xcuserdata/ddaajing.xcuserdatad/xcschemes/ReactiveCocoaDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/xcuserdata/ddaajing.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Documentation.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | ReactiveCocoaDemo.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | FF610671193D705E00D15169 21 | 22 | primary 23 | 24 | 25 | FF610692193D705F00D15169 26 | 27 | primary 28 | 29 | 30 | FF6E5F36197394DC00E4797B 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/xcuserdata/djmacin.xcuserdatad/xcschemes/Documentation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/xcuserdata/djmacin.xcuserdatad/xcschemes/ReactiveCocoaDemo.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 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcodeproj/xcuserdata/djmacin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Documentation.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | ReactiveCocoaDemo.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | FF610671193D705E00D15169 21 | 22 | primary 23 | 24 | 25 | FF610692193D705F00D15169 26 | 27 | primary 28 | 29 | 30 | FF6E5F36197394DC00E4797B 31 | 32 | primary 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo.xcworkspace/xcshareddata/ReactiveCocoaDemo.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 21E50B04-3285-43ED-9AB5-82EAEB933BA3 9 | IDESourceControlProjectName 10 | ReactiveCocoaDemo 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 8FB7A489-BBBF-47EC-9428-A37DE5CA9E6A 14 | ssh://git.corp.anjuke.com/jingda/ReactiveCocoaDemo 15 | 16 | IDESourceControlProjectPath 17 | ReactiveCocoaDemo.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 8FB7A489-BBBF-47EC-9428-A37DE5CA9E6A 21 | .. 22 | 23 | IDESourceControlProjectURL 24 | ssh://git.corp.anjuke.com/jingda/ReactiveCocoaDemo 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | 8FB7A489-BBBF-47EC-9428-A37DE5CA9E6A 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 8FB7A489-BBBF-47EC-9428-A37DE5CA9E6A 36 | IDESourceControlWCCName 37 | ReactiveCocoaDemo 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/AddCityViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AddCityViewController.h 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-10. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "City.h" 11 | 12 | // 新建城市的回调 13 | @protocol SaveDataCallBack 14 | 15 | @optional 16 | -(void)didSaveDataCallback:(City *)city; 17 | 18 | @end 19 | 20 | @interface AddCityViewController : UIViewController 21 | 22 | @property (nonatomic, weak) id delegate; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/AddCityViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AddCityViewController.m 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-10. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import "AddCityViewController.h" 10 | #import "AddCityViewModel.h" 11 | 12 | @interface AddCityViewController () 13 | @property (weak, nonatomic) IBOutlet UITextField *tfCityName; 14 | @property (weak, nonatomic) IBOutlet UILabel *lblCityName; 15 | @property (nonatomic, strong) AddCityViewModel *viewModel; 16 | @property (weak, nonatomic) IBOutlet UISlider *sliderPop; 17 | @property (nonatomic, strong) UIButton *btnSave; 18 | @property (weak, nonatomic) IBOutlet UIView *barPopu; 19 | @end 20 | 21 | @implementation AddCityViewController 22 | 23 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 24 | { 25 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 26 | if (self) { 27 | } 28 | return self; 29 | } 30 | 31 | // 初始化UI元素 32 | -(void)initUI { 33 | self.btnSave = [UIButton buttonWithType:UIButtonTypeContactAdd]; 34 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.btnSave]; 35 | 36 | self.tfCityName.delegate = self; 37 | 38 | // set slider vertical 39 | CGAffineTransform trans = CGAffineTransformMakeRotation(M_PI * 3 / 2); 40 | self.sliderPop.transform = trans; 41 | } 42 | 43 | - (void)viewDidLoad { 44 | [super viewDidLoad]; 45 | 46 | [self initUI]; 47 | 48 | [self bindViewModel]; 49 | } 50 | 51 | - (void)didReceiveMemoryWarning { 52 | [super didReceiveMemoryWarning]; 53 | } 54 | 55 | - (BOOL)textFieldShouldReturn:(UITextField *)textField { 56 | [textField resignFirstResponder]; 57 | return YES; 58 | } 59 | 60 | // 绑定viewModel 61 | -(void)bindViewModel { 62 | 63 | // init viewModel 64 | self.viewModel = [AddCityViewModel new]; 65 | 66 | // set validation once user input 67 | RAC(self.viewModel, cityName) = [self.tfCityName.rac_textSignal distinctUntilChanged]; 68 | 69 | self.btnSave.rac_command = self.viewModel.saveCommand; 70 | 71 | [[self.btnSave.rac_command.executionSignals switchToLatest] subscribeNext:^(id x) { 72 | [self.delegate didSaveDataCallback:x]; 73 | [self.navigationController popViewControllerAnimated:YES]; 74 | }]; 75 | 76 | // set validation delay until button click 77 | // [[self.btnSave rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { 78 | // self.viewModel.cityName = self.tfCityName.text; 79 | // }]; 80 | 81 | // bind label textColor property to viewModel's cityNameValidatorSignal 82 | RAC(self.lblCityName, textColor) = [self.viewModel.cityNameValidatorSignal map:^id(NSNumber *valid) { 83 | BOOL isValid = [valid boolValue]; 84 | return isValid?[UIColor blackColor]:[UIColor redColor]; 85 | }]; 86 | 87 | [[self.sliderPop rac_newValueChannelWithNilValue:[NSNumber numberWithFloat:0.]] subscribeNext:^(NSNumber *popu) { 88 | self.viewModel.population = [popu floatValue]; 89 | }]; 90 | 91 | RAC(self.barPopu, backgroundColor) = RACObserve(self.viewModel, popColor); 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/AddCityViewModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // AddCityViewModel.h 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-10. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AddCityViewModel : NSObject 12 | @property (nonatomic, strong) NSString *cityName; 13 | @property (nonatomic, assign) float population; 14 | @property (nonatomic, strong) UIColor *popColor; 15 | @property (nonatomic ,strong) RACSignal *cityNameValidatorSignal; 16 | @property (nonatomic, strong) RACCommand *saveCommand; 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/AddCityViewModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // AddCityViewModel.m 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-10. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import "AddCityViewModel.h" 10 | #import "City.h" 11 | 12 | @implementation AddCityViewModel 13 | 14 | -(id)init { 15 | self = [super init]; 16 | if(!self) return nil; 17 | 18 | [self bindPopulationColor]; 19 | 20 | return self; 21 | } 22 | 23 | /** 24 | * 色彩属性绑定,根据UI上Slider的值 25 | */ 26 | -(void)bindPopulationColor { 27 | RAC(self, popColor) = [RACObserve(self, population) map:^id(NSNumber *popu) { 28 | return [UIColor colorWithRed:[popu floatValue] / 255. green:59 /255. blue:93/255. alpha:1.]; 29 | }]; 30 | } 31 | 32 | /** 33 | * 城市名称验证器信号 34 | * 35 | * @return 验证器的信号 36 | */ 37 | -(RACSignal *)cityNameValidatorSignal { 38 | if (_cityNameValidatorSignal == nil ) { 39 | _cityNameValidatorSignal = [RACObserve(self,cityName) map:^id(NSString *newName) { 40 | BOOL isValid = true; 41 | 42 | if ([newName isEqualToString:@""]) { 43 | isValid = false; 44 | } 45 | 46 | return [NSNumber numberWithBool:isValid]; 47 | }]; 48 | } 49 | return _cityNameValidatorSignal; 50 | } 51 | 52 | /** 53 | * 保存命令 54 | * 55 | * @return 保存新建城市的命令 56 | */ 57 | -(RACCommand *)saveCommand { 58 | if (_saveCommand == nil) { 59 | _saveCommand = [[RACCommand alloc] initWithEnabled:self.cityNameValidatorSignal signalBlock: 60 | ^RACSignal *(id input) { 61 | return [RACSignal createSignal:^RACDisposable *(id subscriber) { 62 | NSDictionary *JSONDictionary = @{@"toponymName": self.cityName}; 63 | 64 | // 用Mantle来组装对象 65 | City *newCity = [MTLJSONAdapter modelOfClass:City.class fromJSONDictionary:JSONDictionary error:nil]; 66 | [self downloadImageForCity:newCity]; 67 | 68 | // 发送信号 69 | [subscriber sendNext:newCity]; 70 | [subscriber sendCompleted]; 71 | return nil; 72 | }]; 73 | }]; 74 | 75 | } 76 | return _saveCommand; 77 | } 78 | 79 | - (void)downloadImageForCity:(City *)city { 80 | RAC(city, thumbnailData) = [self download:city.cityImage]; 81 | } 82 | 83 | - (RACSignal *)download:(NSString *)urlString { 84 | NSAssert(urlString, @"URL must not be nil"); 85 | 86 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]; 87 | 88 | return [[[NSURLConnection rac_sendAsynchronousRequest:request] 89 | reduceEach:^id(NSURLResponse *response, NSData *data){ 90 | return data; 91 | }] deliverOn:[RACScheduler mainThreadScheduler]]; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-3. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-3. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // 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. 22 | // 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. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 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 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 141 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/City.h: -------------------------------------------------------------------------------- 1 | // 2 | // City.h 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-12. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface City : MTLModel 13 | 14 | /** 15 | * 城市名称 16 | */ 17 | @property(nonatomic, copy) NSString *cityName; 18 | 19 | /** 20 | * 城市封面 21 | */ 22 | @property(nonatomic, copy) NSString *cityImage; 23 | 24 | @property (nonatomic, strong) NSData *thumbnailData; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/City.m: -------------------------------------------------------------------------------- 1 | // 2 | // City.m 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-12. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import "City.h" 10 | 11 | @implementation City 12 | 13 | // Mantle定制的value parse path 14 | + (NSDictionary *)JSONKeyPathsByPropertyKey { 15 | return @{ 16 | @"cityName": @"toponymName", 17 | @"cityImage": @"toponymName" 18 | }; 19 | } 20 | 21 | // 城市封面属性转换器 22 | + (NSValueTransformer *)cityImageJSONTransformer { 23 | return [MTLValueTransformer reversibleTransformerWithForwardBlock:^id(NSString *cityName) { 24 | if ([cityName isEqualToString:@"Beijing"]) 25 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/9/95/BeijingWatchTower.jpg/220px-BeijingWatchTower.jpg"; 26 | else if([cityName isEqualToString:@"Seoul"]) 27 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/6/69/Gyeongbok-gung_palace-05_%28xndr%29.jpg/220px-Gyeongbok-gung_palace-05_%28xndr%29.jpg"; 28 | else if([cityName isEqualToString:@"Tokyo"]) 29 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/1/12/TokyoMetropolitanGovernmentOffice.jpg/220px-TokyoMetropolitanGovernmentOffice.jpg"; 30 | else if([cityName isEqualToString:@"Mexico City"]) 31 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/8/88/Old_architecture_Mexico_City.jpg/220px-Old_architecture_Mexico_City.jpg"; 32 | else if([cityName isEqualToString:@"Manila"]) 33 | return @"http://upload.wikimedia.org/wikipedia/en/thumb/7/72/Intramuros_002.JPG/220px-Intramuros_002.JPG"; 34 | else if([cityName isEqualToString:@"Dhaka"]) 35 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/1/1e/Dhaka_Lalbagh_Fort_5.JPG/220px-Dhaka_Lalbagh_Fort_5.JPG"; 36 | else if([cityName isEqualToString:@"Jakarta"]) 37 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Andries_Beeckman_-_The_Castle_of_Batavia.jpg/220px-Andries_Beeckman_-_The_Castle_of_Batavia.jpg"; 38 | else if([cityName isEqualToString:@"Taipei"]) 39 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Chiang_Kai-shek_memorial_amk.jpg/220px-Chiang_Kai-shek_memorial_amk.jpg"; 40 | else if([cityName isEqualToString:@"Hong Kong"]) 41 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/6/6c/HK_Central_Statue_Square_Legislative_Council_Building_n_Neoclassicism_n_Lippo_Centre.JPG/220px-HK_Central_Statue_Square_Legislative_Council_Building_n_Neoclassicism_n_Lippo_Centre.JPG"; 42 | else if([cityName isEqualToString:@"Bogotá"]) 43 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/c/c6/Old_timer_structural_worker2.jpg/220px-Old_timer_structural_worker2.jpg" ; 44 | else 45 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Ngaye_%28Naraka%29_in_Burmese_art.jpg/220px-Ngaye_%28Naraka%29_in_Burmese_art.jpg"; 46 | } reverseBlock:^id(NSString *imgName) { 47 | return @"http://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Ngaye_%28Naraka%29_in_Burmese_art.jpg/220px-Ngaye_%28Naraka%29_in_Burmese_art.jpg"; 48 | }]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/GeoCityViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GeoCityViewController 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-5. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GeoCityViewController : UIViewController 12 | 13 | /** 14 | * 城市列表 15 | */ 16 | @property (weak, nonatomic) IBOutlet UITableView *geoTbl; 17 | 18 | /** 19 | * 用户ID 20 | */ 21 | @property (nonatomic, strong) NSString *uid; 22 | 23 | /** 24 | * 右上角的添加Button 25 | */ 26 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *btnAdd; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/GeoCityViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GeoCityViewController 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-5. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import "GeoCityViewController.h" 10 | #import "AddCityViewController.h" 11 | 12 | #import "GeoCityViewModel.h" 13 | 14 | #import "MyTableViewCell.h" 15 | 16 | #import "City.h" 17 | 18 | #import 19 | 20 | static NSString *identifier = @"identifierCell"; 21 | 22 | @interface GeoCityViewController () 23 | 24 | @property (nonatomic, strong) GeoCityViewModel *viewModel; 25 | 26 | @property (nonatomic, assign) BOOL isLoading; 27 | 28 | @end 29 | 30 | @implementation GeoCityViewController 31 | 32 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 33 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 34 | if (self) { 35 | } 36 | return self; 37 | } 38 | 39 | - (void)viewDidLoad { 40 | [super viewDidLoad]; 41 | 42 | // setup lots of bindings 43 | [self bindViewModel]; 44 | } 45 | 46 | - (void)didReceiveMemoryWarning { 47 | [super didReceiveMemoryWarning]; 48 | } 49 | 50 | -(void)bindViewModel { 51 | @weakify(self); 52 | 53 | [self.geoTbl registerClass:[MyTableViewCell class] forCellReuseIdentifier:identifier]; 54 | self.geoTbl.dataSource = self; 55 | 56 | // init viewModel 57 | self.viewModel = [GeoCityViewModel new]; 58 | self.isLoading = YES; 59 | self.uid = @"124242"; 60 | 61 | RAC(self.viewModel, uid) = RACObserve(self, uid); 62 | 63 | // network loading flag subscription 64 | [RACObserve(self, isLoading) subscribeNext:^(id x) { 65 | UIApplication.sharedApplication.networkActivityIndicatorVisible = [x boolValue]; 66 | self.btnAdd.enabled = ! [x boolValue]; 67 | }]; 68 | 69 | // network success binding 70 | [[[RACObserve(self.viewModel, cities) ignore:nil] doNext:^(id x) { 71 | self.isLoading = YES; 72 | }] subscribeNext:^(id x) { 73 | @strongify(self); 74 | [self.geoTbl reloadData]; 75 | self.isLoading = NO; 76 | }]; 77 | 78 | // network fail binding 79 | [[RACObserve(self.viewModel, statusMessage) filter:^BOOL(id value) { 80 | return value != nil; 81 | }] 82 | subscribeNext:^(NSString *msg) { 83 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:msg message:msg preferredStyle: UIAlertControllerStyleActionSheet]; 84 | 85 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"error" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { 86 | }]; 87 | 88 | [alertController addAction:cancelAction]; 89 | [self presentViewController:alertController animated:YES completion:nil]; 90 | }]; 91 | } 92 | 93 | #pragma mark - UITableViewDataSource methods 94 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 95 | return self.viewModel.cities.count; 96 | } 97 | 98 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 99 | MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath]; 100 | 101 | cell.city = self.viewModel.cities[indexPath.row]; 102 | 103 | return cell; 104 | } 105 | 106 | -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 107 | // 回调的处理 108 | [[self rac_signalForSelector:@selector(didSaveDataCallback:) fromProtocol:@protocol(SaveDataCallBack)] subscribeNext:^(RACTuple *tuple) { 109 | City *newCity = tuple.first; 110 | [self.viewModel.cities insertObject:newCity atIndex:0]; 111 | [self.geoTbl reloadData]; 112 | }]; 113 | 114 | // 再传递viewDelegate给新页面 115 | AddCityViewController *addController = (AddCityViewController *)[segue destinationViewController]; 116 | addController.delegate = self; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/GeoCityViewModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // GeoCityViewModel 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-5. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GeoCityViewModel : NSObject 12 | 13 | /** 14 | * 城市数组 15 | */ 16 | @property (nonatomic, strong) NSMutableArray *cities; 17 | 18 | /** 19 | * 用户ID 20 | */ 21 | @property (nonatomic, strong) NSString *uid; 22 | 23 | /** 24 | * 服务的状态消息 25 | */ 26 | @property (nonatomic, strong) NSString *statusMessage; 27 | 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/GeoCityViewModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // GeoCityViewModel 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-5. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import "GeoCityViewModel.h" 10 | #import "AFHTTPRequestOperationManager+RACSupport.h" 11 | #import "City.h" 12 | 13 | // mock up data request URL 14 | static NSString *const kSubscribeURL = @"http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=anjuke"; 15 | 16 | @interface GeoCityViewModel() 17 | 18 | // 查询信号 19 | @property (nonatomic, strong) RACSignal *searchSignal; 20 | 21 | @end 22 | 23 | @implementation GeoCityViewModel 24 | 25 | -(id)init { 26 | self = [super init]; 27 | if(!self) return nil; 28 | 29 | [self initSearchSubscrition]; 30 | 31 | return self; 32 | } 33 | 34 | - (void)initSearchSubscrition { 35 | [[self.searchSignal deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(RACTuple *jsonSearchResult) { 36 | NSDictionary *response; 37 | if (jsonSearchResult.count) { 38 | response = jsonSearchResult.first; 39 | } 40 | NSArray *rawArray = response[@"geonames"]; 41 | 42 | self.cities = [[[rawArray.rac_sequence 43 | map:^id(NSDictionary *rawDic) { 44 | NSMutableDictionary *dic = [(NSDictionary *)rawDic mutableCopy]; 45 | City *city = [MTLJSONAdapter modelOfClass:City.class fromJSONDictionary:dic error:nil]; 46 | [self downloadImageForCity:city]; 47 | return city; 48 | }] array] mutableCopy]; 49 | }]; 50 | 51 | [self.searchSignal subscribeError:^(NSError *error) { 52 | self.statusMessage = @"offline"; 53 | }]; 54 | } 55 | 56 | -(RACSignal *)searchSignal { 57 | if (_searchSignal == nil) { 58 | AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 59 | manager.requestSerializer = [AFJSONRequestSerializer new]; 60 | NSDictionary *params = @{@"user_id": @"88"}; 61 | _searchSignal = [manager rac_GET:kSubscribeURL parameters:params]; 62 | } 63 | return _searchSignal; 64 | } 65 | 66 | - (void)downloadImageForCity:(City *)city { 67 | RAC(city, thumbnailData) = [self download:city.cityImage]; 68 | } 69 | 70 | - (RACSignal *)download:(NSString *)urlString { 71 | NSAssert(urlString, @"URL must not be nil"); 72 | 73 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]; 74 | 75 | return [[[NSURLConnection rac_sendAsynchronousRequest:request] 76 | reduceEach:^id(NSURLResponse *response, NSData *data){ 77 | return data; 78 | }] deliverOn:[RACScheduler mainThreadScheduler]]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ReactiveCocoaDemo/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ReactiveCocoaDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "8.0", 8 | "subtype" : "736h", 9 | "scale" : "3x" 10 | }, 11 | { 12 | "orientation" : "landscape", 13 | "idiom" : "iphone", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "8.0", 16 | "subtype" : "736h", 17 | "scale" : "3x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "extent" : "full-screen", 23 | "minimum-system-version" : "8.0", 24 | "subtype" : "667h", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "orientation" : "portrait", 29 | "idiom" : "iphone", 30 | "extent" : "full-screen", 31 | "minimum-system-version" : "7.0", 32 | "scale" : "2x" 33 | }, 34 | { 35 | "orientation" : "portrait", 36 | "idiom" : "iphone", 37 | "extent" : "full-screen", 38 | "minimum-system-version" : "7.0", 39 | "subtype" : "retina4", 40 | "scale" : "2x" 41 | }, 42 | { 43 | "orientation" : "portrait", 44 | "idiom" : "iphone", 45 | "extent" : "full-screen", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "orientation" : "portrait", 50 | "idiom" : "iphone", 51 | "extent" : "full-screen", 52 | "scale" : "2x" 53 | }, 54 | { 55 | "orientation" : "portrait", 56 | "idiom" : "iphone", 57 | "extent" : "full-screen", 58 | "subtype" : "retina4", 59 | "scale" : "2x" 60 | } 61 | ], 62 | "info" : { 63 | "version" : 1, 64 | "author" : "xcode" 65 | } 66 | } -------------------------------------------------------------------------------- /ReactiveCocoaDemo/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/MyTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MyTableViewCell.h 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-5. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class City; 12 | 13 | @interface MyTableViewCell : UITableViewCell 14 | 15 | @property (nonatomic, strong) City *city; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/MyTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // MyTableViewCell.m 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-5. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import "MyTableViewCell.h" 10 | #import "City.h" 11 | 12 | @interface MyTableViewCell() 13 | 14 | @property(nonatomic, strong) UIImageView *imgView; 15 | 16 | @end 17 | 18 | @implementation MyTableViewCell 19 | 20 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 21 | { 22 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 23 | if (self) { 24 | self.imgView = [[UIImageView alloc] initWithFrame:CGRectMake(200, 4, 80, 80)]; 25 | [self.contentView addSubview:self.imgView]; 26 | 27 | RAC(self.textLabel, text) = RACObserve(self, city.cityName); 28 | 29 | RAC(self.imgView, image) = [[[RACObserve(self, city.thumbnailData) 30 | ignore:nil] 31 | map:^id(id value) { 32 | return [[[RACSignal createSignal:^RACDisposable *(id subscriber) { 33 | UIImage *image = [UIImage imageWithData:value]; 34 | [subscriber sendNext:image]; 35 | [subscriber sendCompleted]; 36 | return nil; 37 | }] 38 | subscribeOn:[RACScheduler scheduler]] 39 | deliverOn:[RACScheduler mainThreadScheduler]]; 40 | }] 41 | switchToLatest]; 42 | } 43 | 44 | return self; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/ReactiveCocoaDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | UILaunchStoryboardName 33 | Launch Screen 34 | UIMainStoryboardFile 35 | Main 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/ReactiveCocoaDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-3. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | @end 13 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-3. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | // __block int missilesToLaunch = 0; 22 | // 23 | // RACSignal *processedSignal = [[RACSignal 24 | // return:@"missiles"] 25 | // map:^(id x) { 26 | // missilesToLaunch++; 27 | // return [NSString stringWithFormat:@"will launch %d %@", missilesToLaunch, x]; 28 | // }]; 29 | // 30 | // // This will print "First will launch 1 missiles" 31 | // [processedSignal subscribeNext:^(id x) { 32 | // NSLog(@"First %@", x); 33 | // }]; 34 | // 35 | // // This will print "Second will launch 2 missiles" 36 | // [processedSignal subscribeNext:^(id x) { 37 | // NSLog(@"Second %@", x); 38 | // }]; 39 | // 40 | // RACMulticastConnection *connection = [processedSignal multicast:[RACReplaySubject subject]]; 41 | // [connection connect]; 42 | // 43 | // [connection.signal subscribeNext:^(id response) { 44 | // NSLog(@"subscriber one: %@", response); 45 | // }]; 46 | // 47 | // [connection.signal subscribeNext:^(id response) { 48 | // NSLog(@"subscriber two: %@", response); 49 | // }]; 50 | } 51 | 52 | - (void)didReceiveMemoryWarning { 53 | [super didReceiveMemoryWarning]; 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /ReactiveCocoaDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ReactiveCocoaDemo 4 | // 5 | // Created by dajing on 14-6-3. 6 | // Copyright (c) 2014年 Anjuke. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ReactiveCocoaDemoTests/AddCitySpec.m: -------------------------------------------------------------------------------- 1 | #import "Kiwi.h" 2 | #import 3 | #import "AddCityViewModel.h" 4 | 5 | SPEC_BEGIN(AddCitySpec) 6 | 7 | __block AddCityViewModel *viewModel; 8 | 9 | describe(@"GeoCityViewModel", ^{ 10 | 11 | beforeEach(^{ 12 | viewModel = [[AddCityViewModel alloc] init]; 13 | }); 14 | 15 | context(@"After loading", ^{ 16 | it(@"City name should be disable", ^{ 17 | __block BOOL cityNameValid = NO; 18 | 19 | [viewModel.cityNameValidatorSignal subscribeNext:^(id value) { 20 | cityNameValid = [value boolValue]; 21 | }]; 22 | 23 | viewModel.cityName = @""; 24 | 25 | [[theValue(cityNameValid) should] beNo]; 26 | }); 27 | }); 28 | 29 | context(@"After typing cityname ", ^{ 30 | it(@"City name should be ok", ^{ 31 | __block BOOL cityNameValid = NO; 32 | 33 | [viewModel.cityNameValidatorSignal subscribeNext:^(id value) { 34 | cityNameValid = [value boolValue]; 35 | }]; 36 | viewModel.cityName = @"non empty"; 37 | 38 | [[theValue(cityNameValid) should] beYes]; 39 | }); 40 | }); 41 | 42 | }); 43 | 44 | SPEC_END 45 | -------------------------------------------------------------------------------- /ReactiveCocoaDemoTests/CityListSpec.m: -------------------------------------------------------------------------------- 1 | #import "Kiwi.h" 2 | #import 3 | #import "GeoCityViewModel.h" 4 | 5 | SPEC_BEGIN(CityListSpec) 6 | 7 | __block GeoCityViewModel *viewModel; 8 | 9 | describe(@"GeoCityViewModel", ^{ 10 | 11 | beforeEach(^{ 12 | viewModel = [[GeoCityViewModel alloc] init]; 13 | }); 14 | 15 | context(@"After loading", ^{ 16 | it(@"should have 10 cities", ^{ 17 | viewModel.uid = @"11"; 18 | [[expectFutureValue(viewModel.cities) shouldEventually] haveCountOf: 10]; 19 | }); 20 | }); 21 | }); 22 | 23 | SPEC_END 24 | -------------------------------------------------------------------------------- /ReactiveCocoaDemoTests/ReactiveCocoaDemoTests-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 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ReactiveCocoaDemoTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | --------------------------------------------------------------------------------