├── .gitignore ├── .travis.yml ├── ALDataRequestView.podspec ├── Changelog.md ├── Example ├── ALDataRequestView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── ALDataRequestView-Example.xcscheme ├── ALDataRequestView.xcworkspace │ └── contents.xcworkspacedata ├── ALDataRequestView │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift ├── Podfile ├── Podfile.lock └── Tests │ ├── Info.plist │ └── Tests.swift ├── LICENSE ├── README.md ├── Source ├── ALDataRequestView.swift ├── ReactiveCocoa │ └── ALRACDataRequestView.swift └── RxSwift │ └── ALRxDataRequestView.swift └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | Pods/ 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | language: objective-c 6 | # cache: cocoapods 7 | # podfile: Example/Podfile 8 | # before_install: 9 | # - gem install cocoapods # Since Travis is not always on latest version 10 | # - pod install --project-directory=Example 11 | script: 12 | - set -o pipefail && xcodebuild test -workspace Example/ALDataRequestView.xcworkspace -scheme ALDataRequestView-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty 13 | - pod lib lint 14 | -------------------------------------------------------------------------------- /ALDataRequestView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "ALDataRequestView" 3 | s.version = "2.3.2" 4 | s.summary = "A view representation for data requests. Support for ReactiveCocoa and RXSwift." 5 | s.description = "A view representation for data requests. Support for ReactiveCocoa and RXSwift by attached it to signalproducers and observables." 6 | s.homepage = "https://github.com/AvdLee/ALDataRequestView" 7 | s.license = 'MIT' 8 | s.author = { "Antoine van der Lee" => "info@avanderlee.com" } 9 | s.source = { :git => "https://github.com/AvdLee/ALDataRequestView.git", :tag => s.version.to_s } 10 | s.social_media_url = 'https://twitter.com/twannl' 11 | 12 | s.ios.deployment_target = '8.0' 13 | s.tvos.deployment_target = '9.0' 14 | s.requires_arc = true 15 | 16 | s.default_subspec = "Core" 17 | 18 | s.subspec "Core" do |ss| 19 | ss.source_files = "Source/*.swift" 20 | ss.dependency "PureLayout" 21 | ss.dependency "ReachabilitySwift" 22 | ss.framework = "Foundation" 23 | end 24 | 25 | s.subspec "RxSwift" do |ss| 26 | ss.source_files = "Source/RxSwift/*.swift" 27 | ss.dependency "RxSwift" 28 | ss.dependency "RxCocoa" 29 | ss.dependency "ALDataRequestView/Core" 30 | end 31 | 32 | s.subspec "ReactiveCocoa" do |ss| 33 | ss.source_files = "Source/ReactiveCocoa/*.swift" 34 | ss.dependency "ReactiveSwift" 35 | ss.dependency "ALDataRequestView/Core" 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # 2.3.2 2 | - Fixed Swift 3.1 warnings 3 | 4 | # 2.3.1 5 | - Fixed tvOS support 6 | 7 | # 2.3.0 8 | - tvOS support 9 | 10 | # 2.2.4 11 | - Fixed started issue 12 | 13 | # 2.2.3 14 | - Fixed crash when retrying 15 | 16 | # 2.2.2 17 | - Fixed crash when retrying many times after each other 18 | 19 | # 2.2.1 20 | - Using RxSwift 3.1 with Moya 8 21 | 22 | # 2.2.0 23 | - Using ReactiveSwift 1.0.0 24 | 25 | # 2.1.4 26 | - Using ReactiveSwift beta 4 27 | 28 | # 2.1.0 29 | Updated naming conventions to conform to Swift 3.0 30 | 31 | Make sure you update your DataSource methods 32 | 33 | ```swift 34 | func loadingView(for dataRequestView: ALDataRequestView) -> UIView? 35 | func reloadViewController(for dataRequestView: ALDataRequestView) -> ALDataReloadType? 36 | func emptyView(for dataRequestView: ALDataRequestView) -> UIView? 37 | func hideAnimationDuration(for dataRequestView: ALDataRequestView) -> Double 38 | func showAnimationDuration(for dataRequestView: ALDataRequestView) -> Double 39 | ``` 40 | 41 | And the attach method: 42 | ```swift 43 | .attachTo(dataRequestView: dataRequestView) 44 | ``` 45 | 46 | # 2.0.0 47 | Swift 3.0 compatible 48 | 49 | # 1.0.4 50 | - Fixed an issue hidden = false is not called on completion 51 | 52 | # 1.0.3 53 | - Fixed a bug in switching states quickly 54 | 55 | # 1.0.2 56 | 57 | - Allows for fading in and out of loading/error views 58 | - Pass through error when call fails 59 | - Fixed NoInternetConnection ReloadReason 60 | - Fixed RxSwift issue: RequestState will now be on .Loading when the observer subscribes 61 | - Allows nil for reload and empty view 62 | 63 | # 1.0.1 64 | 65 | - Fixed an retaining issue 66 | - DataSource methods now request an optional 67 | 68 | # 1.0.0 69 | 70 | - Initial release 71 | -------------------------------------------------------------------------------- /Example/ALDataRequestView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 11 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 12 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 13 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 14 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 15 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 16 | 6C1E37BD98057334689399DC /* Pods_ALDataRequestView_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA7DE3E03B0DBC1388150C78 /* Pods_ALDataRequestView_Example.framework */; }; 17 | FC6A0A0BAB99B81A7EF44A0C /* Pods_ALDataRequestView_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D4DEC4D03E89DA52F1E16A1 /* Pods_ALDataRequestView_Tests.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 26 | remoteInfo = ALDataRequestView; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 0F82ED4B049F765756F0380A /* ALDataRequestView.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = ALDataRequestView.podspec; path = ../ALDataRequestView.podspec; sourceTree = ""; }; 32 | 1244DBB7CB7BA69043D555D3 /* Pods-ALDataRequestView_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ALDataRequestView_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ALDataRequestView_Tests/Pods-ALDataRequestView_Tests.debug.xcconfig"; sourceTree = ""; }; 33 | 344F30876B64B304E9006824 /* Pods-ALDataRequestView_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ALDataRequestView_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ALDataRequestView_Example/Pods-ALDataRequestView_Example.debug.xcconfig"; sourceTree = ""; }; 34 | 42CC34EEB7DEE1AECA4ECE0F /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 35 | 607FACD01AFB9204008FA782 /* ALDataRequestView_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ALDataRequestView_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 38 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 39 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 40 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 41 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 42 | 607FACE51AFB9204008FA782 /* ALDataRequestView_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ALDataRequestView_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 45 | 730BB4D91DDF28CC0045BC4E /* Changelog.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = Changelog.md; path = ../Changelog.md; sourceTree = ""; }; 46 | 8D4DEC4D03E89DA52F1E16A1 /* Pods_ALDataRequestView_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ALDataRequestView_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | CA7DE3E03B0DBC1388150C78 /* Pods_ALDataRequestView_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ALDataRequestView_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | D726D31A2004B82535CF5003 /* Pods-ALDataRequestView_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ALDataRequestView_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-ALDataRequestView_Example/Pods-ALDataRequestView_Example.release.xcconfig"; sourceTree = ""; }; 49 | DCDCFD0A291F7478ECBBF07F /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 50 | F1279552799E372BAE90CEBC /* Pods-ALDataRequestView_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ALDataRequestView_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ALDataRequestView_Tests/Pods-ALDataRequestView_Tests.release.xcconfig"; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | 6C1E37BD98057334689399DC /* Pods_ALDataRequestView_Example.framework in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | FC6A0A0BAB99B81A7EF44A0C /* Pods_ALDataRequestView_Tests.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | 28ECB15B92070F79B66CA612 /* Frameworks */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | CA7DE3E03B0DBC1388150C78 /* Pods_ALDataRequestView_Example.framework */, 77 | 8D4DEC4D03E89DA52F1E16A1 /* Pods_ALDataRequestView_Tests.framework */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | 40C29EF296D428081FAD701A /* Pods */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 344F30876B64B304E9006824 /* Pods-ALDataRequestView_Example.debug.xcconfig */, 86 | D726D31A2004B82535CF5003 /* Pods-ALDataRequestView_Example.release.xcconfig */, 87 | 1244DBB7CB7BA69043D555D3 /* Pods-ALDataRequestView_Tests.debug.xcconfig */, 88 | F1279552799E372BAE90CEBC /* Pods-ALDataRequestView_Tests.release.xcconfig */, 89 | ); 90 | name = Pods; 91 | sourceTree = ""; 92 | }; 93 | 607FACC71AFB9204008FA782 = { 94 | isa = PBXGroup; 95 | children = ( 96 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 97 | 607FACD21AFB9204008FA782 /* Example for ALDataRequestView */, 98 | 607FACE81AFB9204008FA782 /* Tests */, 99 | 607FACD11AFB9204008FA782 /* Products */, 100 | 40C29EF296D428081FAD701A /* Pods */, 101 | 28ECB15B92070F79B66CA612 /* Frameworks */, 102 | ); 103 | sourceTree = ""; 104 | }; 105 | 607FACD11AFB9204008FA782 /* Products */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 607FACD01AFB9204008FA782 /* ALDataRequestView_Example.app */, 109 | 607FACE51AFB9204008FA782 /* ALDataRequestView_Tests.xctest */, 110 | ); 111 | name = Products; 112 | sourceTree = ""; 113 | }; 114 | 607FACD21AFB9204008FA782 /* Example for ALDataRequestView */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 118 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 119 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 120 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 121 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 122 | 607FACD31AFB9204008FA782 /* Supporting Files */, 123 | ); 124 | name = "Example for ALDataRequestView"; 125 | path = ALDataRequestView; 126 | sourceTree = ""; 127 | }; 128 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 607FACD41AFB9204008FA782 /* Info.plist */, 132 | ); 133 | name = "Supporting Files"; 134 | sourceTree = ""; 135 | }; 136 | 607FACE81AFB9204008FA782 /* Tests */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 140 | 607FACE91AFB9204008FA782 /* Supporting Files */, 141 | ); 142 | path = Tests; 143 | sourceTree = ""; 144 | }; 145 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 607FACEA1AFB9204008FA782 /* Info.plist */, 149 | ); 150 | name = "Supporting Files"; 151 | sourceTree = ""; 152 | }; 153 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 0F82ED4B049F765756F0380A /* ALDataRequestView.podspec */, 157 | DCDCFD0A291F7478ECBBF07F /* README.md */, 158 | 730BB4D91DDF28CC0045BC4E /* Changelog.md */, 159 | 42CC34EEB7DEE1AECA4ECE0F /* LICENSE */, 160 | ); 161 | name = "Podspec Metadata"; 162 | sourceTree = ""; 163 | }; 164 | /* End PBXGroup section */ 165 | 166 | /* Begin PBXNativeTarget section */ 167 | 607FACCF1AFB9204008FA782 /* ALDataRequestView_Example */ = { 168 | isa = PBXNativeTarget; 169 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ALDataRequestView_Example" */; 170 | buildPhases = ( 171 | FB5C490940BBE81FFF1AE731 /* [CP] Check Pods Manifest.lock */, 172 | 607FACCC1AFB9204008FA782 /* Sources */, 173 | 607FACCD1AFB9204008FA782 /* Frameworks */, 174 | 607FACCE1AFB9204008FA782 /* Resources */, 175 | 94117460A531FC946DEB2DED /* [CP] Embed Pods Frameworks */, 176 | 6ED1139AAAB699D8F164CEB3 /* [CP] Copy Pods Resources */, 177 | ); 178 | buildRules = ( 179 | ); 180 | dependencies = ( 181 | ); 182 | name = ALDataRequestView_Example; 183 | productName = ALDataRequestView; 184 | productReference = 607FACD01AFB9204008FA782 /* ALDataRequestView_Example.app */; 185 | productType = "com.apple.product-type.application"; 186 | }; 187 | 607FACE41AFB9204008FA782 /* ALDataRequestView_Tests */ = { 188 | isa = PBXNativeTarget; 189 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ALDataRequestView_Tests" */; 190 | buildPhases = ( 191 | 18607F631B47EC539B356D4C /* [CP] Check Pods Manifest.lock */, 192 | 607FACE11AFB9204008FA782 /* Sources */, 193 | 607FACE21AFB9204008FA782 /* Frameworks */, 194 | 607FACE31AFB9204008FA782 /* Resources */, 195 | FB4875EEB16F7643B5F60522 /* [CP] Embed Pods Frameworks */, 196 | 84F8722EF8EFC52A6E4773B9 /* [CP] Copy Pods Resources */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 202 | ); 203 | name = ALDataRequestView_Tests; 204 | productName = Tests; 205 | productReference = 607FACE51AFB9204008FA782 /* ALDataRequestView_Tests.xctest */; 206 | productType = "com.apple.product-type.bundle.unit-test"; 207 | }; 208 | /* End PBXNativeTarget section */ 209 | 210 | /* Begin PBXProject section */ 211 | 607FACC81AFB9204008FA782 /* Project object */ = { 212 | isa = PBXProject; 213 | attributes = { 214 | LastSwiftUpdateCheck = 0720; 215 | LastUpgradeCheck = 0820; 216 | ORGANIZATIONNAME = CocoaPods; 217 | TargetAttributes = { 218 | 607FACCF1AFB9204008FA782 = { 219 | CreatedOnToolsVersion = 6.3.1; 220 | }; 221 | 607FACE41AFB9204008FA782 = { 222 | CreatedOnToolsVersion = 6.3.1; 223 | TestTargetID = 607FACCF1AFB9204008FA782; 224 | }; 225 | }; 226 | }; 227 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ALDataRequestView" */; 228 | compatibilityVersion = "Xcode 3.2"; 229 | developmentRegion = English; 230 | hasScannedForEncodings = 0; 231 | knownRegions = ( 232 | en, 233 | Base, 234 | ); 235 | mainGroup = 607FACC71AFB9204008FA782; 236 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 237 | projectDirPath = ""; 238 | projectRoot = ""; 239 | targets = ( 240 | 607FACCF1AFB9204008FA782 /* ALDataRequestView_Example */, 241 | 607FACE41AFB9204008FA782 /* ALDataRequestView_Tests */, 242 | ); 243 | }; 244 | /* End PBXProject section */ 245 | 246 | /* Begin PBXResourcesBuildPhase section */ 247 | 607FACCE1AFB9204008FA782 /* Resources */ = { 248 | isa = PBXResourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 252 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 253 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | 607FACE31AFB9204008FA782 /* Resources */ = { 258 | isa = PBXResourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXResourcesBuildPhase section */ 265 | 266 | /* Begin PBXShellScriptBuildPhase section */ 267 | 18607F631B47EC539B356D4C /* [CP] Check Pods Manifest.lock */ = { 268 | isa = PBXShellScriptBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | ); 272 | inputPaths = ( 273 | ); 274 | name = "[CP] Check Pods Manifest.lock"; 275 | outputPaths = ( 276 | ); 277 | runOnlyForDeploymentPostprocessing = 0; 278 | shellPath = /bin/sh; 279 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 280 | showEnvVarsInLog = 0; 281 | }; 282 | 6ED1139AAAB699D8F164CEB3 /* [CP] Copy Pods Resources */ = { 283 | isa = PBXShellScriptBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | ); 287 | inputPaths = ( 288 | ); 289 | name = "[CP] Copy Pods Resources"; 290 | outputPaths = ( 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | shellPath = /bin/sh; 294 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ALDataRequestView_Example/Pods-ALDataRequestView_Example-resources.sh\"\n"; 295 | showEnvVarsInLog = 0; 296 | }; 297 | 84F8722EF8EFC52A6E4773B9 /* [CP] Copy Pods Resources */ = { 298 | isa = PBXShellScriptBuildPhase; 299 | buildActionMask = 2147483647; 300 | files = ( 301 | ); 302 | inputPaths = ( 303 | ); 304 | name = "[CP] Copy Pods Resources"; 305 | outputPaths = ( 306 | ); 307 | runOnlyForDeploymentPostprocessing = 0; 308 | shellPath = /bin/sh; 309 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ALDataRequestView_Tests/Pods-ALDataRequestView_Tests-resources.sh\"\n"; 310 | showEnvVarsInLog = 0; 311 | }; 312 | 94117460A531FC946DEB2DED /* [CP] Embed Pods Frameworks */ = { 313 | isa = PBXShellScriptBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | ); 317 | inputPaths = ( 318 | ); 319 | name = "[CP] Embed Pods Frameworks"; 320 | outputPaths = ( 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | shellPath = /bin/sh; 324 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ALDataRequestView_Example/Pods-ALDataRequestView_Example-frameworks.sh\"\n"; 325 | showEnvVarsInLog = 0; 326 | }; 327 | FB4875EEB16F7643B5F60522 /* [CP] Embed Pods Frameworks */ = { 328 | isa = PBXShellScriptBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | ); 332 | inputPaths = ( 333 | ); 334 | name = "[CP] Embed Pods Frameworks"; 335 | outputPaths = ( 336 | ); 337 | runOnlyForDeploymentPostprocessing = 0; 338 | shellPath = /bin/sh; 339 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ALDataRequestView_Tests/Pods-ALDataRequestView_Tests-frameworks.sh\"\n"; 340 | showEnvVarsInLog = 0; 341 | }; 342 | FB5C490940BBE81FFF1AE731 /* [CP] Check Pods Manifest.lock */ = { 343 | isa = PBXShellScriptBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | ); 347 | inputPaths = ( 348 | ); 349 | name = "[CP] Check Pods Manifest.lock"; 350 | outputPaths = ( 351 | ); 352 | runOnlyForDeploymentPostprocessing = 0; 353 | shellPath = /bin/sh; 354 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 355 | showEnvVarsInLog = 0; 356 | }; 357 | /* End PBXShellScriptBuildPhase section */ 358 | 359 | /* Begin PBXSourcesBuildPhase section */ 360 | 607FACCC1AFB9204008FA782 /* Sources */ = { 361 | isa = PBXSourcesBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 365 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 366 | ); 367 | runOnlyForDeploymentPostprocessing = 0; 368 | }; 369 | 607FACE11AFB9204008FA782 /* Sources */ = { 370 | isa = PBXSourcesBuildPhase; 371 | buildActionMask = 2147483647; 372 | files = ( 373 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 374 | ); 375 | runOnlyForDeploymentPostprocessing = 0; 376 | }; 377 | /* End PBXSourcesBuildPhase section */ 378 | 379 | /* Begin PBXTargetDependency section */ 380 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 381 | isa = PBXTargetDependency; 382 | target = 607FACCF1AFB9204008FA782 /* ALDataRequestView_Example */; 383 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 384 | }; 385 | /* End PBXTargetDependency section */ 386 | 387 | /* Begin PBXVariantGroup section */ 388 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 389 | isa = PBXVariantGroup; 390 | children = ( 391 | 607FACDA1AFB9204008FA782 /* Base */, 392 | ); 393 | name = Main.storyboard; 394 | sourceTree = ""; 395 | }; 396 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 397 | isa = PBXVariantGroup; 398 | children = ( 399 | 607FACDF1AFB9204008FA782 /* Base */, 400 | ); 401 | name = LaunchScreen.xib; 402 | sourceTree = ""; 403 | }; 404 | /* End PBXVariantGroup section */ 405 | 406 | /* Begin XCBuildConfiguration section */ 407 | 607FACED1AFB9204008FA782 /* Debug */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | ALWAYS_SEARCH_USER_PATHS = NO; 411 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 412 | CLANG_CXX_LIBRARY = "libc++"; 413 | CLANG_ENABLE_MODULES = YES; 414 | CLANG_ENABLE_OBJC_ARC = YES; 415 | CLANG_WARN_BOOL_CONVERSION = YES; 416 | CLANG_WARN_CONSTANT_CONVERSION = YES; 417 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 418 | CLANG_WARN_EMPTY_BODY = YES; 419 | CLANG_WARN_ENUM_CONVERSION = YES; 420 | CLANG_WARN_INFINITE_RECURSION = YES; 421 | CLANG_WARN_INT_CONVERSION = YES; 422 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 423 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 424 | CLANG_WARN_UNREACHABLE_CODE = YES; 425 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 426 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 427 | COPY_PHASE_STRIP = NO; 428 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 429 | ENABLE_STRICT_OBJC_MSGSEND = YES; 430 | ENABLE_TESTABILITY = YES; 431 | GCC_C_LANGUAGE_STANDARD = gnu99; 432 | GCC_DYNAMIC_NO_PIC = NO; 433 | GCC_NO_COMMON_BLOCKS = YES; 434 | GCC_OPTIMIZATION_LEVEL = 0; 435 | GCC_PREPROCESSOR_DEFINITIONS = ( 436 | "DEBUG=1", 437 | "$(inherited)", 438 | ); 439 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 440 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 441 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 442 | GCC_WARN_UNDECLARED_SELECTOR = YES; 443 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 444 | GCC_WARN_UNUSED_FUNCTION = YES; 445 | GCC_WARN_UNUSED_VARIABLE = YES; 446 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 447 | MTL_ENABLE_DEBUG_INFO = YES; 448 | ONLY_ACTIVE_ARCH = YES; 449 | SDKROOT = iphoneos; 450 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 451 | SWIFT_VERSION = 3.0; 452 | }; 453 | name = Debug; 454 | }; 455 | 607FACEE1AFB9204008FA782 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 460 | CLANG_CXX_LIBRARY = "libc++"; 461 | CLANG_ENABLE_MODULES = YES; 462 | CLANG_ENABLE_OBJC_ARC = YES; 463 | CLANG_WARN_BOOL_CONVERSION = YES; 464 | CLANG_WARN_CONSTANT_CONVERSION = YES; 465 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 466 | CLANG_WARN_EMPTY_BODY = YES; 467 | CLANG_WARN_ENUM_CONVERSION = YES; 468 | CLANG_WARN_INFINITE_RECURSION = YES; 469 | CLANG_WARN_INT_CONVERSION = YES; 470 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 471 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 472 | CLANG_WARN_UNREACHABLE_CODE = YES; 473 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 474 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 475 | COPY_PHASE_STRIP = NO; 476 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 477 | ENABLE_NS_ASSERTIONS = NO; 478 | ENABLE_STRICT_OBJC_MSGSEND = YES; 479 | GCC_C_LANGUAGE_STANDARD = gnu99; 480 | GCC_NO_COMMON_BLOCKS = YES; 481 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 482 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 483 | GCC_WARN_UNDECLARED_SELECTOR = YES; 484 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 485 | GCC_WARN_UNUSED_FUNCTION = YES; 486 | GCC_WARN_UNUSED_VARIABLE = YES; 487 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 488 | MTL_ENABLE_DEBUG_INFO = NO; 489 | SDKROOT = iphoneos; 490 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 491 | SWIFT_VERSION = 3.0; 492 | VALIDATE_PRODUCT = YES; 493 | }; 494 | name = Release; 495 | }; 496 | 607FACF01AFB9204008FA782 /* Debug */ = { 497 | isa = XCBuildConfiguration; 498 | baseConfigurationReference = 344F30876B64B304E9006824 /* Pods-ALDataRequestView_Example.debug.xcconfig */; 499 | buildSettings = { 500 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 501 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 502 | INFOPLIST_FILE = ALDataRequestView/Info.plist; 503 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 504 | MODULE_NAME = ExampleApp; 505 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | }; 508 | name = Debug; 509 | }; 510 | 607FACF11AFB9204008FA782 /* Release */ = { 511 | isa = XCBuildConfiguration; 512 | baseConfigurationReference = D726D31A2004B82535CF5003 /* Pods-ALDataRequestView_Example.release.xcconfig */; 513 | buildSettings = { 514 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 515 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 516 | INFOPLIST_FILE = ALDataRequestView/Info.plist; 517 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 518 | MODULE_NAME = ExampleApp; 519 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 520 | PRODUCT_NAME = "$(TARGET_NAME)"; 521 | }; 522 | name = Release; 523 | }; 524 | 607FACF31AFB9204008FA782 /* Debug */ = { 525 | isa = XCBuildConfiguration; 526 | baseConfigurationReference = 1244DBB7CB7BA69043D555D3 /* Pods-ALDataRequestView_Tests.debug.xcconfig */; 527 | buildSettings = { 528 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 529 | BUNDLE_LOADER = "$(TEST_HOST)"; 530 | FRAMEWORK_SEARCH_PATHS = ( 531 | "$(SDKROOT)/Developer/Library/Frameworks", 532 | "$(inherited)", 533 | ); 534 | GCC_PREPROCESSOR_DEFINITIONS = ( 535 | "DEBUG=1", 536 | "$(inherited)", 537 | ); 538 | INFOPLIST_FILE = Tests/Info.plist; 539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 540 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 541 | PRODUCT_NAME = "$(TARGET_NAME)"; 542 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ALDataRequestView_Example.app/ALDataRequestView_Example"; 543 | }; 544 | name = Debug; 545 | }; 546 | 607FACF41AFB9204008FA782 /* Release */ = { 547 | isa = XCBuildConfiguration; 548 | baseConfigurationReference = F1279552799E372BAE90CEBC /* Pods-ALDataRequestView_Tests.release.xcconfig */; 549 | buildSettings = { 550 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 551 | BUNDLE_LOADER = "$(TEST_HOST)"; 552 | FRAMEWORK_SEARCH_PATHS = ( 553 | "$(SDKROOT)/Developer/Library/Frameworks", 554 | "$(inherited)", 555 | ); 556 | INFOPLIST_FILE = Tests/Info.plist; 557 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 558 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 559 | PRODUCT_NAME = "$(TARGET_NAME)"; 560 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ALDataRequestView_Example.app/ALDataRequestView_Example"; 561 | }; 562 | name = Release; 563 | }; 564 | /* End XCBuildConfiguration section */ 565 | 566 | /* Begin XCConfigurationList section */ 567 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ALDataRequestView" */ = { 568 | isa = XCConfigurationList; 569 | buildConfigurations = ( 570 | 607FACED1AFB9204008FA782 /* Debug */, 571 | 607FACEE1AFB9204008FA782 /* Release */, 572 | ); 573 | defaultConfigurationIsVisible = 0; 574 | defaultConfigurationName = Release; 575 | }; 576 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ALDataRequestView_Example" */ = { 577 | isa = XCConfigurationList; 578 | buildConfigurations = ( 579 | 607FACF01AFB9204008FA782 /* Debug */, 580 | 607FACF11AFB9204008FA782 /* Release */, 581 | ); 582 | defaultConfigurationIsVisible = 0; 583 | defaultConfigurationName = Release; 584 | }; 585 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ALDataRequestView_Tests" */ = { 586 | isa = XCConfigurationList; 587 | buildConfigurations = ( 588 | 607FACF31AFB9204008FA782 /* Debug */, 589 | 607FACF41AFB9204008FA782 /* Release */, 590 | ); 591 | defaultConfigurationIsVisible = 0; 592 | defaultConfigurationName = Release; 593 | }; 594 | /* End XCConfigurationList section */ 595 | }; 596 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 597 | } 598 | -------------------------------------------------------------------------------- /Example/ALDataRequestView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/ALDataRequestView.xcodeproj/xcshareddata/xcschemes/ALDataRequestView-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Example/ALDataRequestView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/ALDataRequestView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ALDataRequestView 4 | // 5 | // Created by Antoine van der Lee on 02/28/2016. 6 | // Copyright (c) 2016 Antoine van der Lee. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Example/ALDataRequestView/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/ALDataRequestView/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 | 31 | 38 | 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 | 90 | 97 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /Example/ALDataRequestView/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 | } 39 | -------------------------------------------------------------------------------- /Example/ALDataRequestView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | NSAppTransportSecurity 39 | 40 | NSAllowsArbitraryLoads 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Example/ALDataRequestView/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ALDataRequestView 4 | // 5 | // Created by Antoine van der Lee on 02/28/2016. 6 | // Copyright (c) 2016 Antoine van der Lee. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ALDataRequestView 11 | import PureLayout 12 | import RxSwift 13 | import ReactiveSwift 14 | import RxCocoa 15 | import Result 16 | 17 | class ViewController: UIViewController { 18 | 19 | var dataRequestView:ALDataRequestView? 20 | var signalProducer:SignalProducer<[String], NSError>? 21 | var dataSignalProducer:SignalProducer? 22 | var rxDisposable:RxSwift.Disposable? 23 | let (signal, subscriber) = Signal<[String], NSError>.pipe() 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | 28 | dataRequestView = ALDataRequestView(forAutoLayout: ()) 29 | view.addSubview(dataRequestView!) 30 | dataRequestView?.autoPinEdgesToSuperviewEdges() 31 | dataRequestView?.dataSource = self 32 | view.sendSubview(toBack: dataRequestView!) 33 | 34 | 35 | testWithFailureCallObservable() 36 | // testWithFailureCallSignalProducer() 37 | // testWithFailureCallObservable() 38 | } 39 | 40 | deinit { 41 | print("Deinit vc") 42 | } 43 | 44 | func testWithEmptySignalProducer(){ 45 | signalProducer = SignalProducer(signal).attachTo(dataRequestView: dataRequestView!) 46 | signalProducer?.start() 47 | 48 | delay(delay: 3.0, closure: { [weak self] () -> Void in 49 | let emptyArray:[String] = [] 50 | self?.subscriber.send(value: emptyArray) // Send empty array 51 | self?.subscriber.sendCompleted() 52 | }) 53 | } 54 | 55 | func testWithFailureCallSignalProducer(){ 56 | let request = URLRequest(url: URL(string: "http://httpbin.org/status/400")!) 57 | dataSignalProducer = URLSession.shared 58 | .reactive.data(with: request) 59 | .flatMapError({ (error) -> SignalProducer<(Data, URLResponse), NSError> in 60 | return SignalProducer(error: error as NSError) 61 | }) 62 | .flatMap(.latest, transform: { (data, response) -> SignalProducer in 63 | if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode > 299 { 64 | return SignalProducer(error: NSError(domain: "", code: httpResponse.statusCode, userInfo: nil)) 65 | } 66 | return SignalProducer(value: data) 67 | }) 68 | .attachTo(dataRequestView: dataRequestView!) 69 | dataSignalProducer?.start() 70 | } 71 | 72 | func testWithFailureCallObservable(){ 73 | let request = URLRequest(url: URL(string: "http://httpbin.org/status/400")!) 74 | rxDisposable = URLSession.shared.rx.data(request: request).attachTo(dataRequestView: dataRequestView!).subscribe() 75 | } 76 | 77 | @IBAction func setLoadingButtonTapped(sender: UIButton) { 78 | dataRequestView?.changeRequestState(state: RequestState.loading) 79 | } 80 | 81 | @IBAction func setEmptyButtonTapped(sender: UIButton) { 82 | dataRequestView?.changeRequestState(state: RequestState.empty) 83 | } 84 | 85 | @IBAction func setReloadButtonTapped(sender: UIButton) { 86 | dataRequestView?.changeRequestState(state: RequestState.failed) 87 | } 88 | 89 | func delay(delay:Double, closure:@escaping ()->()) { 90 | DispatchQueue.main.asyncAfter(deadline: .now() + delay) { 91 | closure() 92 | } 93 | } 94 | } 95 | 96 | extension ViewController : ALDataRequestViewDataSource { 97 | func loadingViewForDataRequestView(dataRequestView: ALDataRequestView) -> UIView? { 98 | let loadingView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray) 99 | loadingView.startAnimating() 100 | return loadingView 101 | } 102 | 103 | func reloadViewController(for dataRequestView: ALDataRequestView) -> ALDataReloadType? { 104 | let reloadVC = ReloadViewController() 105 | return reloadVC 106 | } 107 | 108 | func emptyView(for dataRequestView: ALDataRequestView) -> UIView? { 109 | let emptyLabel = UILabel(forAutoLayout: ()) 110 | emptyLabel.text = "Data is empty" 111 | return emptyLabel 112 | } 113 | 114 | func hideAnimationDuration(for dataRequestView: ALDataRequestView) -> Double { 115 | return 0.25 116 | } 117 | 118 | func showAnimationDuration(for dataRequestView: ALDataRequestView) -> Double { 119 | return 0.25 120 | } 121 | } 122 | 123 | final class ReloadViewController : UIViewController, ALDataReloadType { 124 | 125 | var retryButton:UIButton? 126 | var statusLabel:UILabel! 127 | 128 | init(){ 129 | super.init(nibName: nil, bundle: nil) 130 | 131 | retryButton = UIButton(type: UIButtonType.system) 132 | retryButton?.setTitle("Reload!", for: UIControlState.normal) 133 | view.addSubview(retryButton!) 134 | retryButton?.autoCenterInSuperview() 135 | 136 | statusLabel = UILabel(forAutoLayout: ()) 137 | view.addSubview(statusLabel) 138 | statusLabel.autoAlignAxis(toSuperviewAxis: ALAxis.vertical) 139 | statusLabel.autoPinEdge(.bottom, to: .top, of: retryButton!) 140 | } 141 | 142 | required init?(coder aDecoder: NSCoder) { 143 | super.init(coder: aDecoder) 144 | } 145 | 146 | func setup(for reloadType:ReloadType){ 147 | switch reloadType.reason { 148 | case .generalError: 149 | statusLabel.text = "General error occured" 150 | case .noInternetConnection: 151 | statusLabel.text = "Your internet connection is lost" 152 | } 153 | } 154 | 155 | } 156 | 157 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | use_frameworks! 3 | 4 | def sharedPods 5 | pod 'ALDataRequestView', :path => '../' 6 | pod 'ALDataRequestView/ReactiveCocoa', :path => '../' 7 | pod 'ALDataRequestView/RxSwift', :path => '../' 8 | end 9 | 10 | target 'ALDataRequestView_Example' do 11 | sharedPods 12 | end 13 | 14 | target 'ALDataRequestView_Tests' do 15 | sharedPods 16 | end 17 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - ALDataRequestView (2.3.2): 3 | - ALDataRequestView/Core (= 2.3.2) 4 | - ALDataRequestView/Core (2.3.2): 5 | - PureLayout 6 | - ReachabilitySwift 7 | - ALDataRequestView/ReactiveCocoa (2.3.2): 8 | - ALDataRequestView/Core 9 | - ReactiveSwift 10 | - ALDataRequestView/RxSwift (2.3.2): 11 | - ALDataRequestView/Core 12 | - RxCocoa 13 | - RxSwift 14 | - PureLayout (3.0.2) 15 | - ReachabilitySwift (3) 16 | - ReactiveSwift (1.1.1): 17 | - Result (~> 3.2) 18 | - Result (3.2.1) 19 | - RxCocoa (3.4.0): 20 | - RxSwift (~> 3.4) 21 | - RxSwift (3.4.0) 22 | 23 | DEPENDENCIES: 24 | - ALDataRequestView (from `../`) 25 | - ALDataRequestView/ReactiveCocoa (from `../`) 26 | - ALDataRequestView/RxSwift (from `../`) 27 | 28 | EXTERNAL SOURCES: 29 | ALDataRequestView: 30 | :path: "../" 31 | 32 | SPEC CHECKSUMS: 33 | ALDataRequestView: 8148c8f84865578d99c6f4e87404eb85aad530df 34 | PureLayout: 4d550abe49a94f24c2808b9b95db9131685fe4cd 35 | ReachabilitySwift: f5b9bb30a0777fac8f09ce8b067e32faeb29bb64 36 | ReactiveSwift: 2489549e3231a6ff68ec99f3d320bb264f10ba0a 37 | Result: 2453a22e5c5b11c0c3a478736e82cd02f763b781 38 | RxCocoa: d14ef6b6029e1ddc6e966508c09289090de68ff9 39 | RxSwift: 3789a1af753002a14edecdb698a2424624296a9c 40 | 41 | PODFILE CHECKSUM: 8160a0e00b9ee46278a431dd49353bd52cad1a69 42 | 43 | COCOAPODS: 1.2.0 44 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import XCTest 3 | import ALDataRequestView 4 | 5 | class Tests: XCTestCase { 6 | 7 | override func setUp() { 8 | super.setUp() 9 | // Put setup code here. This method is called before the invocation of each test method in the class. 10 | } 11 | 12 | override func tearDown() { 13 | // Put teardown code here. This method is called after the invocation of each test method in the class. 14 | super.tearDown() 15 | } 16 | 17 | func testExample() { 18 | // This is an example of a functional test case. 19 | XCTAssert(true, "Pass") 20 | } 21 | 22 | func testPerformanceExample() { 23 | // This is an example of a performance test case. 24 | self.measure() { 25 | // Put the code you want to measure the time of here. 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Antoine van der Lee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ALDataRequestView 2 | 3 | [![Version](https://img.shields.io/cocoapods/v/ALDataRequestView.svg?style=flat)](http://cocoapods.org/pods/ALDataRequestView) 4 | [![Language](https://img.shields.io/badge/language-swift3.0-f48041.svg?style=flat)](https://developer.apple.com/swift) 5 | [![License](https://img.shields.io/cocoapods/l/ALDataRequestView.svg?style=flat)](http://cocoapods.org/pods/ALDataRequestView) 6 | [![Platform](https://img.shields.io/cocoapods/p/ALDataRequestView.svg?style=flat)](http://cocoapods.org/pods/ALDataRequestView) 7 | [![Twitter](https://img.shields.io/badge/twitter-@twannl-blue.svg?style=flat)](http://twitter.com/twannl) 8 | 9 | A simple way to show: 10 | 11 | * Loading view while a producer or observable is started 12 | * Reload view when a producer or observable is failed 13 | * Empty view when a producer or observable returns an empty array or an object which inherits the `Emptyable` protocol and `isEmpty` is true 14 | 15 | ## Usage 16 | 17 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 18 | 19 | ### ReactiveSwift & RxSwift 20 | Simply call 21 | 22 | ```swift 23 | attachTo(dataRequestView: dataRequestView) 24 | ``` 25 | on your signalProducer or observable. 26 | 27 | Make sure you implement the `ALDataRequestViewDataSource`: 28 | 29 | ```swift 30 | func loadingView(for dataRequestView: ALDataRequestView) -> UIView? 31 | func reloadViewController(for dataRequestView: ALDataRequestView) -> ALDataReloadType? 32 | func emptyView(for dataRequestView: ALDataRequestView) -> UIView? 33 | ``` 34 | 35 | #### Examples 36 | ##### RxSwift 37 | 38 | ```swift 39 | let request = URLRequest(url: URL(string: "http://httpbin.org/status/400")!) 40 | rxDisposable = URLSession.shared.rx.data(request: request).attachToDataRequestView(dataRequestView: dataRequestView!).subscribe() 41 | ``` 42 | ##### ReactiveSwift 43 | 44 | ```swift 45 | let request = URLRequest(url: URL(string: "http://httpbin.org/status/400")!) 46 | dataSignalProducer = URLSession.shared 47 | .reactive.data(with: request) 48 | .flatMap(.latest, transform: { (data, response) -> SignalProducer in 49 | if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode > 299 { 50 | return SignalProducer(error: NSError(domain: "", code: httpResponse.statusCode, userInfo: nil)) 51 | } 52 | return SignalProducer(value: data) 53 | }) 54 | .attachTo(dataRequestView: dataRequestView!) 55 | 56 | ``` 57 | 58 | 59 | ## Installation 60 | 61 | ALDataRequestView is available through [CocoaPods](http://cocoapods.org). To install 62 | it, simply add the following line to your Podfile: 63 | 64 | ```ruby 65 | pod "ALDataRequestView" 66 | ``` 67 | 68 | #### Swift version vs Pod version 69 | | Swift version | Pod version | 70 | | ------------- | --------------- | 71 | | 3.X | >= 2.0.0 | 72 | | 2.3 | 1.0.4 | 73 | 74 | ## Author 75 | 76 | Antoine van der Lee, info@avanderlee.com 77 | 78 | ## License 79 | 80 | ALDataRequestView is available under the MIT license. See the LICENSE file for more info. 81 | -------------------------------------------------------------------------------- /Source/ALDataRequestView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ALDataRequestView.swift 3 | // Pods 4 | // 5 | // Created by Antoine van der Lee on 28/02/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | import PureLayout 11 | import ReachabilitySwift 12 | 13 | public typealias RetryAction = (() -> Void) 14 | 15 | public enum RequestState { 16 | case possible 17 | case loading 18 | case failed 19 | case success 20 | case empty 21 | } 22 | 23 | public enum ReloadReason { 24 | case generalError 25 | case noInternetConnection 26 | } 27 | 28 | public struct ReloadType { 29 | public var reason: ReloadReason 30 | public var error: Error? 31 | } 32 | 33 | public protocol Emptyable { 34 | var isEmpty:Bool { get } 35 | } 36 | 37 | public protocol ALDataReloadType { 38 | var retryButton:UIButton? { get set } 39 | func setup(for reloadType:ReloadType) 40 | } 41 | 42 | // Make methods optional with default implementations 43 | public extension ALDataReloadType { 44 | func setup(for reloadType:ReloadType){ } 45 | } 46 | 47 | public protocol ALDataRequestViewDataSource : class { 48 | func loadingView(for dataRequestView: ALDataRequestView) -> UIView? 49 | func reloadViewController(for dataRequestView: ALDataRequestView) -> ALDataReloadType? 50 | func emptyView(for dataRequestView: ALDataRequestView) -> UIView? 51 | func hideAnimationDuration(for dataRequestView: ALDataRequestView) -> Double 52 | func showAnimationDuration(for dataRequestView: ALDataRequestView) -> Double 53 | } 54 | 55 | // Make methods optional with default implementations 56 | public extension ALDataRequestViewDataSource { 57 | func loadingView(for dataRequestView: ALDataRequestView) -> UIView? { return nil } 58 | func reloadViewController(for dataRequestView: ALDataRequestView) -> ALDataReloadType? { return nil } 59 | func emptyView(for dataRequestView: ALDataRequestView) -> UIView? { return nil } 60 | func hideAnimationDuration(for dataRequestView: ALDataRequestView) -> Double { return 0 } 61 | func showAnimationDuration(for dataRequestView: ALDataRequestView) -> Double { return 0 } 62 | } 63 | 64 | public class ALDataRequestView: UIView { 65 | 66 | // Public properties 67 | public weak var dataSource:ALDataRequestViewDataSource? 68 | 69 | /// Action for retrying a failed situation 70 | /// Will be triggered by the retry button, on foreground or when reachability changed to connected 71 | public var retryAction:RetryAction? 72 | 73 | /// If failed earlier, the retryAction will be triggered on foreground 74 | public var automaticallyRetryOnForeground:Bool = true 75 | 76 | /// If failed earlier, the retryAction will be triggered when reachability changed to reachable 77 | public var automaticallyRetryWhenReachable:Bool = true 78 | 79 | /// Set to true for debugging purposes 80 | public var loggingEnabled:Bool = false 81 | 82 | // Internal properties 83 | internal var state:RequestState = .possible 84 | 85 | // Private properties 86 | private var loadingView:UIView? 87 | private var reloadView:UIView? 88 | private var emptyView:UIView? 89 | fileprivate var reachability:Reachability? 90 | 91 | override public func awakeFromNib() { 92 | super.awakeFromNib() 93 | setup() 94 | } 95 | 96 | override init(frame: CGRect) { 97 | super.init(frame: frame) 98 | setup() 99 | } 100 | 101 | required public init?(coder aDecoder: NSCoder) { 102 | super.init(coder: aDecoder) 103 | } 104 | 105 | internal func setup(){ 106 | // Hide by default 107 | isHidden = true 108 | 109 | // Background color is not needed 110 | backgroundColor = UIColor.clear 111 | 112 | // Setup for automatic retrying 113 | initOnForegroundObserver() 114 | initReachabilityMonitoring() 115 | 116 | debugLog(logString: "Init DataRequestView") 117 | } 118 | 119 | deinit { 120 | NotificationCenter.default.removeObserver(self) 121 | debugLog(logString: "Deinit DataRequestView") 122 | } 123 | 124 | // MARK: Public Methods 125 | public func changeRequestState(state:RequestState, error: Error? = nil){ 126 | guard state != self.state else { return } 127 | 128 | layer.removeAllAnimations() 129 | 130 | self.state = state 131 | resetToPossibleState(completion: { [weak self] (completed) in () 132 | guard let state = self?.state else { return } 133 | switch state { 134 | case .loading: 135 | self?.showLoadingView() 136 | break 137 | case .failed: 138 | self?.showReloadView(error: error) 139 | break 140 | case .empty: 141 | self?.showEmptyView() 142 | break 143 | default: 144 | break 145 | } 146 | }) 147 | } 148 | 149 | // MARK: Private Methods 150 | 151 | /// This will remove all views added 152 | private func resetToPossibleState(completion: ((Bool) -> Void)?){ 153 | UIView.animate(withDuration: dataSource?.hideAnimationDuration(for: self) ?? 0, animations: { [weak self] in () 154 | self?.loadingView?.alpha = 0 155 | self?.emptyView?.alpha = 0 156 | self?.reloadView?.alpha = 0 157 | }) { [weak self] (completed) in 158 | self?.resetViews(views: [self?.loadingView, self?.emptyView, self?.reloadView]) 159 | self?.isHidden = true 160 | completion?(completed) 161 | } 162 | } 163 | 164 | private func resetViews(views: [UIView?]) { 165 | views.forEach { (view) in 166 | view?.alpha = 1 167 | view?.removeFromSuperview() 168 | } 169 | } 170 | 171 | /// This will show the loading view 172 | internal func showLoadingView(){ 173 | guard let dataSourceLoadingView = dataSource?.loadingView(for: self) else { 174 | debugLog(logString: "No loading view provided!") 175 | return 176 | } 177 | isHidden = false 178 | loadingView = dataSourceLoadingView 179 | 180 | // Only add if not yet added 181 | if loadingView?.superview == nil { 182 | addSubview(loadingView!) 183 | loadingView?.autoPinEdgesToSuperviewEdges() 184 | layoutIfNeeded() 185 | } 186 | 187 | dataSourceLoadingView.showWithDuration(duration: dataSource?.showAnimationDuration(for: self)) 188 | } 189 | 190 | /// This will show the reload view 191 | internal func showReloadView(error: Error? = nil){ 192 | guard let dataSourceReloadType = dataSource?.reloadViewController(for: self) else { 193 | debugLog(logString: "No reload view provided!") 194 | return 195 | } 196 | 197 | if let dataSourceReloadView = dataSourceReloadType as? UIView { 198 | reloadView = dataSourceReloadView 199 | } else if let dataSourceReloadViewController = dataSourceReloadType as? UIViewController { 200 | reloadView = dataSourceReloadViewController.view 201 | } 202 | 203 | guard let reloadView = reloadView else { 204 | debugLog(logString: "Could not determine reloadView") 205 | return 206 | } 207 | 208 | var reloadReason: ReloadReason = .generalError 209 | if let error = error as NSError?, error.isNetworkConnectionError() || reachability?.isReachable == false { 210 | reloadReason = .noInternetConnection 211 | } 212 | 213 | isHidden = false 214 | addSubview(reloadView) 215 | reloadView.autoPinEdgesToSuperviewEdges() 216 | dataSourceReloadType.setup(for: ReloadType(reason: reloadReason, error: error)) 217 | 218 | #if os(tvOS) 219 | if #available(iOS 9.0, *) { 220 | dataSourceReloadType.retryButton?.addTarget(self, action: #selector(ALDataRequestView.retryButtonTapped), for: UIControlEvents.primaryActionTriggered) 221 | } 222 | #else 223 | dataSourceReloadType.retryButton?.addTarget(self, action: #selector(ALDataRequestView.retryButtonTapped), for: UIControlEvents.touchUpInside) 224 | #endif 225 | 226 | reloadView.showWithDuration(duration: dataSource?.showAnimationDuration(for: self)) 227 | } 228 | 229 | /// This will show the empty view 230 | internal func showEmptyView(){ 231 | guard let dataSourceEmptyView = dataSource?.emptyView(for: self) else { 232 | debugLog(logString: "No empty view provided!") 233 | // Hide as we don't have anything to show from the empty view 234 | isHidden = true 235 | return 236 | } 237 | isHidden = false 238 | emptyView = dataSourceEmptyView 239 | addSubview(emptyView!) 240 | emptyView?.autoPinEdgesToSuperviewEdges() 241 | 242 | dataSourceEmptyView.showWithDuration(duration: dataSource?.showAnimationDuration(for: self)) 243 | } 244 | 245 | /// IBAction for the retry button 246 | @objc private func retryButtonTapped(button:UIButton){ 247 | retryIfRetryable() 248 | } 249 | 250 | /// This will trigger the retryAction if current state is failed 251 | fileprivate func retryIfRetryable(){ 252 | guard state == RequestState.failed else { 253 | return 254 | } 255 | 256 | guard let retryAction = retryAction else { 257 | debugLog(logString: "No retry action provided") 258 | return 259 | } 260 | 261 | retryAction() 262 | } 263 | } 264 | 265 | /// On foreground Observer methods. 266 | private extension ALDataRequestView { 267 | func initOnForegroundObserver(){ 268 | NotificationCenter.default.addObserver(self, selector: #selector(ALDataRequestView.onForeground), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil) 269 | } 270 | 271 | @objc private func onForeground(notification:NSNotification){ 272 | guard automaticallyRetryOnForeground == true else { 273 | return 274 | } 275 | retryIfRetryable() 276 | } 277 | } 278 | 279 | /// Reachability methods 280 | private extension ALDataRequestView { 281 | 282 | func initReachabilityMonitoring() { 283 | reachability = Reachability() 284 | 285 | reachability?.whenReachable = { [weak self] reachability in 286 | guard self?.automaticallyRetryWhenReachable == true else { 287 | return 288 | } 289 | 290 | self?.retryIfRetryable() 291 | } 292 | 293 | do { 294 | try reachability?.startNotifier() 295 | } catch { 296 | debugLog(logString: "Unable to start notifier") 297 | } 298 | } 299 | } 300 | 301 | /// Logging purposes 302 | private extension ALDataRequestView { 303 | func debugLog(logString:String){ 304 | guard loggingEnabled else { return } 305 | print("ALDataRequestView: \(logString)") 306 | } 307 | } 308 | 309 | /// NSError extension 310 | private extension NSError { 311 | func isNetworkConnectionError() -> Bool { 312 | let networkErrors = [NSURLErrorNetworkConnectionLost, NSURLErrorNotConnectedToInternet] 313 | 314 | if domain == NSURLErrorDomain && networkErrors.contains(code) { 315 | return true 316 | } 317 | return false 318 | } 319 | } 320 | 321 | /// UIView extension 322 | private extension UIView { 323 | 324 | func showWithDuration(duration: Double?) { 325 | guard let duration = duration else { 326 | alpha = 1 327 | return 328 | } 329 | 330 | alpha = 0 331 | UIView.animate(withDuration: duration, animations: { 332 | self.alpha = 1 333 | }) 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /Source/ReactiveCocoa/ALRACDataRequestView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ALRACDataRequestView.swift 3 | // Pods 4 | // 5 | // Created by Antoine van der Lee on 28/02/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | import ReactiveSwift 11 | 12 | public extension SignalProducerProtocol { 13 | func attachTo(dataRequestView:ALDataRequestView) -> SignalProducer { 14 | let newSignalProducer = producer.observe(on: UIScheduler()) 15 | .on(value: { [weak dataRequestView](object) in 16 | if let emptyableObject = object as? Emptyable, emptyableObject.isEmpty == true { 17 | dataRequestView?.changeRequestState(state: .empty) 18 | } else if let arrayObject = object as? NSArray, arrayObject.count == 0 { 19 | dataRequestView?.changeRequestState(state: .empty) 20 | } else { 21 | dataRequestView?.changeRequestState(state: .success) 22 | } 23 | }) 24 | .on(starting: { [weak dataRequestView] () -> () in 25 | dataRequestView?.changeRequestState(state: .loading) 26 | }) 27 | .on(failed: { [weak dataRequestView] (error) in 28 | dataRequestView?.changeRequestState(state: .failed, error: error) 29 | }) 30 | 31 | dataRequestView.retryAction = { () -> Void in 32 | newSignalProducer.start() 33 | } 34 | 35 | return newSignalProducer 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Source/RxSwift/ALRxDataRequestView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ALRxDataRequestView.swift 3 | // Pods 4 | // 5 | // Created by Antoine van der Lee on 28/02/16. 6 | // 7 | // 8 | 9 | import RxSwift 10 | import RxCocoa 11 | 12 | public extension ObservableType { 13 | 14 | func attachTo(dataRequestView:ALDataRequestView) -> Observable { 15 | let resourceFactory: () throws -> BooleanDisposable = { () -> BooleanDisposable in 16 | return BooleanDisposable() 17 | } 18 | let observableFactory:(Disposable) throws -> Observable = { [weak dataRequestView] (_) throws -> Observable in 19 | dataRequestView?.changeRequestState(state: .loading) 20 | 21 | return self.observeOn(MainScheduler.instance) 22 | .do(onNext: { [weak dataRequestView] (object) in 23 | if let emptyableObject = object as? Emptyable, emptyableObject.isEmpty == true { 24 | dataRequestView?.changeRequestState(state: .empty) 25 | } else if let arrayObject = object as? NSArray, arrayObject.count == 0 { 26 | dataRequestView?.changeRequestState(state: .empty) 27 | } else { 28 | dataRequestView?.changeRequestState(state:.success) 29 | } 30 | }, onError: { [weak dataRequestView] (error) in 31 | dataRequestView?.changeRequestState(state: .failed, error: error) 32 | }) 33 | } 34 | 35 | let observable = Observable.using(resourceFactory, observableFactory: observableFactory) 36 | 37 | dataRequestView.retryAction = { [weak dataRequestView] () -> Void in 38 | if let dataRequestView = dataRequestView { 39 | _ = observable.takeUntil(dataRequestView.rx.deallocated).subscribe() 40 | } 41 | } 42 | 43 | return observable 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------