From 074bbca65245b38349d38dab88d7c12824e93403 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Mon, 22 May 2017 13:17:28 -0700 Subject: [PATCH] Add RSWeb framework. --- Evergreen.xcodeproj/project.pbxproj | 75 ++ Frameworks/RSWeb/.gitignore | 65 ++ Frameworks/RSWeb/LICENSE | 21 + Frameworks/RSWeb/README.md | 19 + .../RSWeb/RSWeb.xcodeproj/project.pbxproj | 706 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + Frameworks/RSWeb/RSWeb/DownloadObject.swift | 32 + Frameworks/RSWeb/RSWeb/DownloadProgress.swift | 74 ++ Frameworks/RSWeb/RSWeb/DownloadSession.swift | 304 ++++++++ .../RSWeb/RSWeb/HTTPConditionalGetInfo.swift | 44 ++ Frameworks/RSWeb/RSWeb/HTTPMethod.swift | 18 + .../RSWeb/RSWeb/HTTPRequestHeader.swift | 21 + Frameworks/RSWeb/RSWeb/HTTPResponseCode.swift | 61 ++ .../RSWeb/RSWeb/HTTPResponseHeader.swift | 20 + Frameworks/RSWeb/RSWeb/Info.plist | 26 + Frameworks/RSWeb/RSWeb/MacWebBrowser.swift | 44 ++ Frameworks/RSWeb/RSWeb/MimeType.swift | 55 ++ .../RSWeb/NSMutableURLRequest+RSWeb.swift | 28 + Frameworks/RSWeb/RSWeb/OneShotDownload.swift | 60 ++ Frameworks/RSWeb/RSWeb/URL+RSWeb.swift | 69 ++ Frameworks/RSWeb/RSWeb/URLRequest+RSWeb.swift | 29 + .../RSWeb/RSWeb/URLResponse+RSWeb.swift | 49 ++ Frameworks/RSWeb/RSWeb/UserAgent.swift | 23 + Frameworks/RSWeb/RSWebTests/Info.plist | 22 + Frameworks/RSWeb/RSWebTests/RSWebTests.swift | 36 + Frameworks/RSWeb/RSWebiOS/Info.plist | 26 + 26 files changed, 1934 insertions(+) create mode 100755 Frameworks/RSWeb/.gitignore create mode 100755 Frameworks/RSWeb/LICENSE create mode 100755 Frameworks/RSWeb/README.md create mode 100755 Frameworks/RSWeb/RSWeb.xcodeproj/project.pbxproj create mode 100755 Frameworks/RSWeb/RSWeb.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100755 Frameworks/RSWeb/RSWeb/DownloadObject.swift create mode 100755 Frameworks/RSWeb/RSWeb/DownloadProgress.swift create mode 100755 Frameworks/RSWeb/RSWeb/DownloadSession.swift create mode 100755 Frameworks/RSWeb/RSWeb/HTTPConditionalGetInfo.swift create mode 100755 Frameworks/RSWeb/RSWeb/HTTPMethod.swift create mode 100755 Frameworks/RSWeb/RSWeb/HTTPRequestHeader.swift create mode 100755 Frameworks/RSWeb/RSWeb/HTTPResponseCode.swift create mode 100755 Frameworks/RSWeb/RSWeb/HTTPResponseHeader.swift create mode 100755 Frameworks/RSWeb/RSWeb/Info.plist create mode 100755 Frameworks/RSWeb/RSWeb/MacWebBrowser.swift create mode 100755 Frameworks/RSWeb/RSWeb/MimeType.swift create mode 100755 Frameworks/RSWeb/RSWeb/NSMutableURLRequest+RSWeb.swift create mode 100755 Frameworks/RSWeb/RSWeb/OneShotDownload.swift create mode 100755 Frameworks/RSWeb/RSWeb/URL+RSWeb.swift create mode 100755 Frameworks/RSWeb/RSWeb/URLRequest+RSWeb.swift create mode 100755 Frameworks/RSWeb/RSWeb/URLResponse+RSWeb.swift create mode 100755 Frameworks/RSWeb/RSWeb/UserAgent.swift create mode 100755 Frameworks/RSWeb/RSWebTests/Info.plist create mode 100755 Frameworks/RSWeb/RSWebTests/RSWebTests.swift create mode 100755 Frameworks/RSWeb/RSWebiOS/Info.plist diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index 51aeea024..58121f13b 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ 84B06FAF1ED37DBD00F0B54B /* RSCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FA91ED37DAD00F0B54B /* RSCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 84B06FB21ED37DBD00F0B54B /* RSDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06F9D1ED37DA000F0B54B /* RSDatabase.framework */; }; 84B06FB31ED37DBD00F0B54B /* RSDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06F9D1ED37DA000F0B54B /* RSDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 84B06FC21ED37E9600F0B54B /* RSWeb.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FBD1ED37E8C00F0B54B /* RSWeb.framework */; }; + 84B06FC31ED37E9600F0B54B /* RSWeb.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FBD1ED37E8C00F0B54B /* RSWeb.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -112,6 +114,34 @@ remoteGlobalIDString = 84F22C541B52E0D9000060CE; remoteInfo = RSDatabase; }; + 84B06FBC1ED37E8C00F0B54B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 849C08B61E0CAC85006B03FA; + remoteInfo = RSWeb; + }; + 84B06FBE1ED37E8C00F0B54B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 849C08BF1E0CAC86006B03FA; + remoteInfo = RSWebTests; + }; + 84B06FC01ED37E8C00F0B54B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 849C08D51E0CACA3006B03FA; + remoteInfo = RSWebiOS; + }; + 84B06FC41ED37E9600F0B54B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 849C08B51E0CAC85006B03FA; + remoteInfo = RSWeb; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -123,6 +153,7 @@ files = ( 84B06FB31ED37DBD00F0B54B /* RSDatabase.framework in Embed Frameworks */, 84B06FAF1ED37DBD00F0B54B /* RSCore.framework in Embed Frameworks */, + 84B06FC31ED37E9600F0B54B /* RSWeb.framework in Embed Frameworks */, 84B06F831ED37BDD00F0B54B /* RSXML.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -143,6 +174,7 @@ 84B06F761ED37BCA00F0B54B /* RSXML.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSXML.xcodeproj; path = Frameworks/RSXML/RSXML.xcodeproj; sourceTree = ""; }; 84B06F961ED37DA000F0B54B /* RSDatabase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSDatabase.xcodeproj; path = Frameworks/RSDatabase/RSDatabase.xcodeproj; sourceTree = ""; }; 84B06FA21ED37DAC00F0B54B /* RSCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSCore.xcodeproj; path = Frameworks/RSCore/RSCore.xcodeproj; sourceTree = ""; }; + 84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSWeb.xcodeproj; path = Frameworks/RSWeb/RSWeb.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -152,6 +184,7 @@ files = ( 84B06FB21ED37DBD00F0B54B /* RSDatabase.framework in Frameworks */, 84B06FAE1ED37DBD00F0B54B /* RSCore.framework in Frameworks */, + 84B06FC21ED37E9600F0B54B /* RSWeb.framework in Frameworks */, 84B06F821ED37BDD00F0B54B /* RSXML.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -178,6 +211,7 @@ 849C64611ED37A5D003D8FC0 /* Products */, 84B06FA21ED37DAC00F0B54B /* RSCore.xcodeproj */, 84B06F961ED37DA000F0B54B /* RSDatabase.xcodeproj */, + 84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */, 84B06F761ED37BCA00F0B54B /* RSXML.xcodeproj */, ); sourceTree = ""; @@ -230,6 +264,16 @@ name = Products; sourceTree = ""; }; + 84B06FB71ED37E8B00F0B54B /* Products */ = { + isa = PBXGroup; + children = ( + 84B06FBD1ED37E8C00F0B54B /* RSWeb.framework */, + 84B06FBF1ED37E8C00F0B54B /* RSWebTests.xctest */, + 84B06FC11ED37E8C00F0B54B /* RSWeb.framework */, + ); + name = Products; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -248,6 +292,7 @@ 84B06F851ED37BDD00F0B54B /* PBXTargetDependency */, 84B06FB11ED37DBD00F0B54B /* PBXTargetDependency */, 84B06FB51ED37DBD00F0B54B /* PBXTargetDependency */, + 84B06FC51ED37E9600F0B54B /* PBXTargetDependency */, ); name = Evergreen; productName = Evergreen; @@ -315,6 +360,10 @@ ProductGroup = 84B06F971ED37DA000F0B54B /* Products */; ProjectRef = 84B06F961ED37DA000F0B54B /* RSDatabase.xcodeproj */; }, + { + ProductGroup = 84B06FB71ED37E8B00F0B54B /* Products */; + ProjectRef = 84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */; + }, { ProductGroup = 84B06F771ED37BCA00F0B54B /* Products */; ProjectRef = 84B06F761ED37BCA00F0B54B /* RSXML.xcodeproj */; @@ -392,6 +441,27 @@ remoteRef = 84B06FAC1ED37DAD00F0B54B /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 84B06FBD1ED37E8C00F0B54B /* RSWeb.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = RSWeb.framework; + remoteRef = 84B06FBC1ED37E8C00F0B54B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 84B06FBF1ED37E8C00F0B54B /* RSWebTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = RSWebTests.xctest; + remoteRef = 84B06FBE1ED37E8C00F0B54B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 84B06FC11ED37E8C00F0B54B /* RSWeb.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = RSWeb.framework; + remoteRef = 84B06FC01ED37E8C00F0B54B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -454,6 +524,11 @@ name = RSDatabase; targetProxy = 84B06FB41ED37DBD00F0B54B /* PBXContainerItemProxy */; }; + 84B06FC51ED37E9600F0B54B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = RSWeb; + targetProxy = 84B06FC41ED37E9600F0B54B /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ diff --git a/Frameworks/RSWeb/.gitignore b/Frameworks/RSWeb/.gitignore new file mode 100755 index 000000000..2c22487b5 --- /dev/null +++ b/Frameworks/RSWeb/.gitignore @@ -0,0 +1,65 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xcuserstate + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output diff --git a/Frameworks/RSWeb/LICENSE b/Frameworks/RSWeb/LICENSE new file mode 100755 index 000000000..6c6a3472e --- /dev/null +++ b/Frameworks/RSWeb/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Brent Simmons + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Frameworks/RSWeb/README.md b/Frameworks/RSWeb/README.md new file mode 100755 index 000000000..8a439e904 --- /dev/null +++ b/Frameworks/RSWeb/README.md @@ -0,0 +1,19 @@ +# RSWeb + +RSWeb is utility code — all Swift — for downloading things from the web. It builds a Mac framework and an iOS framework. + +#### Easy way + +See `OneShotDownload` for a top-level `download` function that takes a URL and a callback. The callback takes `Data`, `URLResponse`, and `Error` parameters. It’s easy. + +#### Slightly less easy way + +See `DownloadSession` and `DownloadSessionDelegate` for when you’re doing a bunch of downloads and you need to track progress. + +#### Extras + +`HTTPConditionalGetInfo` helps with supporting conditional GET, for when you’re downloading things that may not have changed. See [HTTP Conditional Get for RSS Hackers](http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers/) for more about conditional GET. This is especially critical when polling for changes, such as with an RSS reader. + +`MimeType` could use expansion, but is useful for some cases right now. + +`MacWebBrowser` makes it easy to open a URL in the default browser. You can specify whether or not to open in background. \ No newline at end of file diff --git a/Frameworks/RSWeb/RSWeb.xcodeproj/project.pbxproj b/Frameworks/RSWeb/RSWeb.xcodeproj/project.pbxproj new file mode 100755 index 000000000..0b0a94a54 --- /dev/null +++ b/Frameworks/RSWeb/RSWeb.xcodeproj/project.pbxproj @@ -0,0 +1,706 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 842ED2E71E12FB8A000CF738 /* HTTPRequestHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2E61E12FB8A000CF738 /* HTTPRequestHeader.swift */; }; + 842ED2E81E12FB8A000CF738 /* HTTPRequestHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2E61E12FB8A000CF738 /* HTTPRequestHeader.swift */; }; + 842ED2EA1E12FB91000CF738 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2E91E12FB91000CF738 /* HTTPMethod.swift */; }; + 842ED2EB1E12FB91000CF738 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2E91E12FB91000CF738 /* HTTPMethod.swift */; }; + 842ED2ED1E12FB97000CF738 /* HTTPResponseCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2EC1E12FB97000CF738 /* HTTPResponseCode.swift */; }; + 842ED2EE1E12FB97000CF738 /* HTTPResponseCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2EC1E12FB97000CF738 /* HTTPResponseCode.swift */; }; + 842ED2F01E12FB9B000CF738 /* HTTPResponseHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2EF1E12FB9B000CF738 /* HTTPResponseHeader.swift */; }; + 842ED2F11E12FB9B000CF738 /* HTTPResponseHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2EF1E12FB9B000CF738 /* HTTPResponseHeader.swift */; }; + 842ED2F31E12FBAA000CF738 /* DownloadSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2F21E12FBAA000CF738 /* DownloadSession.swift */; }; + 842ED2F41E12FBAA000CF738 /* DownloadSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2F21E12FBAA000CF738 /* DownloadSession.swift */; }; + 842ED2F61E12FBAF000CF738 /* OneShotDownload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2F51E12FBAF000CF738 /* OneShotDownload.swift */; }; + 842ED2F71E12FBAF000CF738 /* OneShotDownload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2F51E12FBAF000CF738 /* OneShotDownload.swift */; }; + 842ED2F91E12FBB5000CF738 /* DownloadProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2F81E12FBB5000CF738 /* DownloadProgress.swift */; }; + 842ED2FA1E12FBB5000CF738 /* DownloadProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2F81E12FBB5000CF738 /* DownloadProgress.swift */; }; + 842ED2FC1E12FBBB000CF738 /* DownloadObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2FB1E12FBBB000CF738 /* DownloadObject.swift */; }; + 842ED2FD1E12FBBB000CF738 /* DownloadObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2FB1E12FBBB000CF738 /* DownloadObject.swift */; }; + 842ED2FF1E12FBC1000CF738 /* UserAgent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2FE1E12FBC1000CF738 /* UserAgent.swift */; }; + 842ED3001E12FBC1000CF738 /* UserAgent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED2FE1E12FBC1000CF738 /* UserAgent.swift */; }; + 842ED3021E12FBC7000CF738 /* HTTPConditionalGetInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED3011E12FBC7000CF738 /* HTTPConditionalGetInfo.swift */; }; + 842ED3031E12FBC7000CF738 /* HTTPConditionalGetInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED3011E12FBC7000CF738 /* HTTPConditionalGetInfo.swift */; }; + 842ED3051E12FBCC000CF738 /* NSMutableURLRequest+RSWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED3041E12FBCC000CF738 /* NSMutableURLRequest+RSWeb.swift */; }; + 842ED3061E12FBCC000CF738 /* NSMutableURLRequest+RSWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED3041E12FBCC000CF738 /* NSMutableURLRequest+RSWeb.swift */; }; + 842ED3081E12FBD2000CF738 /* URLRequest+RSWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED3071E12FBD2000CF738 /* URLRequest+RSWeb.swift */; }; + 842ED3091E12FBD2000CF738 /* URLRequest+RSWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED3071E12FBD2000CF738 /* URLRequest+RSWeb.swift */; }; + 842ED30B1E12FBD8000CF738 /* URL+RSWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED30A1E12FBD8000CF738 /* URL+RSWeb.swift */; }; + 842ED30C1E12FBD8000CF738 /* URL+RSWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED30A1E12FBD8000CF738 /* URL+RSWeb.swift */; }; + 842ED30E1E12FBDD000CF738 /* URLResponse+RSWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED30D1E12FBDD000CF738 /* URLResponse+RSWeb.swift */; }; + 842ED30F1E12FBDD000CF738 /* URLResponse+RSWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED30D1E12FBDD000CF738 /* URLResponse+RSWeb.swift */; }; + 842ED3111E12FBE1000CF738 /* MacWebBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED3101E12FBE1000CF738 /* MacWebBrowser.swift */; }; + 842ED3141E12FBE7000CF738 /* MimeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED3131E12FBE7000CF738 /* MimeType.swift */; }; + 842ED3151E12FBE7000CF738 /* MimeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842ED3131E12FBE7000CF738 /* MimeType.swift */; }; + 849C08C01E0CAC86006B03FA /* RSWeb.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 849C08B61E0CAC85006B03FA /* RSWeb.framework */; }; + 849C08C51E0CAC86006B03FA /* RSWebTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849C08C41E0CAC86006B03FA /* RSWebTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 849C08C11E0CAC86006B03FA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 849C08AD1E0CAC85006B03FA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 849C08B51E0CAC85006B03FA; + remoteInfo = RSWeb; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 842ED2E61E12FB8A000CF738 /* HTTPRequestHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPRequestHeader.swift; path = RSWeb/HTTPRequestHeader.swift; sourceTree = ""; }; + 842ED2E91E12FB91000CF738 /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPMethod.swift; path = RSWeb/HTTPMethod.swift; sourceTree = ""; }; + 842ED2EC1E12FB97000CF738 /* HTTPResponseCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPResponseCode.swift; path = RSWeb/HTTPResponseCode.swift; sourceTree = ""; }; + 842ED2EF1E12FB9B000CF738 /* HTTPResponseHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPResponseHeader.swift; path = RSWeb/HTTPResponseHeader.swift; sourceTree = ""; }; + 842ED2F21E12FBAA000CF738 /* DownloadSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DownloadSession.swift; path = RSWeb/DownloadSession.swift; sourceTree = ""; }; + 842ED2F51E12FBAF000CF738 /* OneShotDownload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OneShotDownload.swift; path = RSWeb/OneShotDownload.swift; sourceTree = ""; }; + 842ED2F81E12FBB5000CF738 /* DownloadProgress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DownloadProgress.swift; path = RSWeb/DownloadProgress.swift; sourceTree = ""; }; + 842ED2FB1E12FBBB000CF738 /* DownloadObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DownloadObject.swift; path = RSWeb/DownloadObject.swift; sourceTree = ""; }; + 842ED2FE1E12FBC1000CF738 /* UserAgent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UserAgent.swift; path = RSWeb/UserAgent.swift; sourceTree = ""; }; + 842ED3011E12FBC7000CF738 /* HTTPConditionalGetInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPConditionalGetInfo.swift; path = RSWeb/HTTPConditionalGetInfo.swift; sourceTree = ""; }; + 842ED3041E12FBCC000CF738 /* NSMutableURLRequest+RSWeb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSMutableURLRequest+RSWeb.swift"; path = "RSWeb/NSMutableURLRequest+RSWeb.swift"; sourceTree = ""; }; + 842ED3071E12FBD2000CF738 /* URLRequest+RSWeb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "URLRequest+RSWeb.swift"; path = "RSWeb/URLRequest+RSWeb.swift"; sourceTree = ""; }; + 842ED30A1E12FBD8000CF738 /* URL+RSWeb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "URL+RSWeb.swift"; path = "RSWeb/URL+RSWeb.swift"; sourceTree = ""; }; + 842ED30D1E12FBDD000CF738 /* URLResponse+RSWeb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "URLResponse+RSWeb.swift"; path = "RSWeb/URLResponse+RSWeb.swift"; sourceTree = ""; }; + 842ED3101E12FBE1000CF738 /* MacWebBrowser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MacWebBrowser.swift; path = RSWeb/MacWebBrowser.swift; sourceTree = ""; }; + 842ED3131E12FBE7000CF738 /* MimeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MimeType.swift; path = RSWeb/MimeType.swift; sourceTree = ""; }; + 849C08B61E0CAC85006B03FA /* RSWeb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RSWeb.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 849C08BA1E0CAC85006B03FA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 849C08BF1E0CAC86006B03FA /* RSWebTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RSWebTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 849C08C41E0CAC86006B03FA /* RSWebTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSWebTests.swift; sourceTree = ""; }; + 849C08C61E0CAC86006B03FA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 849C08D51E0CACA3006B03FA /* RSWeb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RSWeb.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 849C08D81E0CACA3006B03FA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 849C08B21E0CAC85006B03FA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 849C08BC1E0CAC86006B03FA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 849C08C01E0CAC86006B03FA /* RSWeb.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 849C08D11E0CACA3006B03FA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 842ED2D41E11FE8B000CF738 /* Constants */ = { + isa = PBXGroup; + children = ( + 842ED2E61E12FB8A000CF738 /* HTTPRequestHeader.swift */, + 842ED2E91E12FB91000CF738 /* HTTPMethod.swift */, + 842ED2EC1E12FB97000CF738 /* HTTPResponseCode.swift */, + 842ED2EF1E12FB9B000CF738 /* HTTPResponseHeader.swift */, + ); + name = Constants; + sourceTree = ""; + }; + 849C08AC1E0CAC85006B03FA = { + isa = PBXGroup; + children = ( + 842ED2D41E11FE8B000CF738 /* Constants */, + 849C09231E0CAD67006B03FA /* Downloading */, + 842ED30A1E12FBD8000CF738 /* URL+RSWeb.swift */, + 842ED3131E12FBE7000CF738 /* MimeType.swift */, + 842ED3101E12FBE1000CF738 /* MacWebBrowser.swift */, + 849C08B81E0CAC85006B03FA /* RSWeb */, + 849C08C31E0CAC86006B03FA /* RSWebTests */, + 849C08D61E0CACA3006B03FA /* RSWebiOS */, + 849C08B71E0CAC85006B03FA /* Products */, + ); + sourceTree = ""; + }; + 849C08B71E0CAC85006B03FA /* Products */ = { + isa = PBXGroup; + children = ( + 849C08B61E0CAC85006B03FA /* RSWeb.framework */, + 849C08BF1E0CAC86006B03FA /* RSWebTests.xctest */, + 849C08D51E0CACA3006B03FA /* RSWeb.framework */, + ); + name = Products; + sourceTree = ""; + }; + 849C08B81E0CAC85006B03FA /* RSWeb */ = { + isa = PBXGroup; + children = ( + 849C08BA1E0CAC85006B03FA /* Info.plist */, + ); + path = RSWeb; + sourceTree = ""; + }; + 849C08C31E0CAC86006B03FA /* RSWebTests */ = { + isa = PBXGroup; + children = ( + 849C08C41E0CAC86006B03FA /* RSWebTests.swift */, + 849C08C61E0CAC86006B03FA /* Info.plist */, + ); + path = RSWebTests; + sourceTree = ""; + }; + 849C08D61E0CACA3006B03FA /* RSWebiOS */ = { + isa = PBXGroup; + children = ( + 849C08D81E0CACA3006B03FA /* Info.plist */, + ); + path = RSWebiOS; + sourceTree = ""; + }; + 849C09231E0CAD67006B03FA /* Downloading */ = { + isa = PBXGroup; + children = ( + 842ED2F21E12FBAA000CF738 /* DownloadSession.swift */, + 842ED2F81E12FBB5000CF738 /* DownloadProgress.swift */, + 842ED2F51E12FBAF000CF738 /* OneShotDownload.swift */, + 842ED2FB1E12FBBB000CF738 /* DownloadObject.swift */, + 842ED2FE1E12FBC1000CF738 /* UserAgent.swift */, + 842ED3011E12FBC7000CF738 /* HTTPConditionalGetInfo.swift */, + 842ED3071E12FBD2000CF738 /* URLRequest+RSWeb.swift */, + 842ED3041E12FBCC000CF738 /* NSMutableURLRequest+RSWeb.swift */, + 842ED30D1E12FBDD000CF738 /* URLResponse+RSWeb.swift */, + ); + name = Downloading; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 849C08B31E0CAC85006B03FA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 849C08D21E0CACA3006B03FA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 849C08B51E0CAC85006B03FA /* RSWeb */ = { + isa = PBXNativeTarget; + buildConfigurationList = 849C08CA1E0CAC86006B03FA /* Build configuration list for PBXNativeTarget "RSWeb" */; + buildPhases = ( + 849C08B11E0CAC85006B03FA /* Sources */, + 849C08B21E0CAC85006B03FA /* Frameworks */, + 849C08B31E0CAC85006B03FA /* Headers */, + 849C08B41E0CAC85006B03FA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RSWeb; + productName = RSWeb; + productReference = 849C08B61E0CAC85006B03FA /* RSWeb.framework */; + productType = "com.apple.product-type.framework"; + }; + 849C08BE1E0CAC86006B03FA /* RSWebTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 849C08CD1E0CAC86006B03FA /* Build configuration list for PBXNativeTarget "RSWebTests" */; + buildPhases = ( + 849C08BB1E0CAC86006B03FA /* Sources */, + 849C08BC1E0CAC86006B03FA /* Frameworks */, + 849C08BD1E0CAC86006B03FA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 849C08C21E0CAC86006B03FA /* PBXTargetDependency */, + ); + name = RSWebTests; + productName = RSWebTests; + productReference = 849C08BF1E0CAC86006B03FA /* RSWebTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 849C08D41E0CACA3006B03FA /* RSWebiOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 849C08DA1E0CACA3006B03FA /* Build configuration list for PBXNativeTarget "RSWebiOS" */; + buildPhases = ( + 849C08D01E0CACA3006B03FA /* Sources */, + 849C08D11E0CACA3006B03FA /* Frameworks */, + 849C08D21E0CACA3006B03FA /* Headers */, + 849C08D31E0CACA3006B03FA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RSWebiOS; + productName = RSWebiOS; + productReference = 849C08D51E0CACA3006B03FA /* RSWeb.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 849C08AD1E0CAC85006B03FA /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0820; + LastUpgradeCheck = 0820; + ORGANIZATIONNAME = "Ranchero Software"; + TargetAttributes = { + 849C08B51E0CAC85006B03FA = { + CreatedOnToolsVersion = 8.2.1; + DevelopmentTeam = 9C84TZ7Q6Z; + LastSwiftMigration = 0820; + ProvisioningStyle = Automatic; + }; + 849C08BE1E0CAC86006B03FA = { + CreatedOnToolsVersion = 8.2.1; + DevelopmentTeam = 9C84TZ7Q6Z; + ProvisioningStyle = Automatic; + }; + 849C08D41E0CACA3006B03FA = { + CreatedOnToolsVersion = 8.2.1; + DevelopmentTeam = 9C84TZ7Q6Z; + LastSwiftMigration = 0820; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 849C08B01E0CAC85006B03FA /* Build configuration list for PBXProject "RSWeb" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 849C08AC1E0CAC85006B03FA; + productRefGroup = 849C08B71E0CAC85006B03FA /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 849C08B51E0CAC85006B03FA /* RSWeb */, + 849C08BE1E0CAC86006B03FA /* RSWebTests */, + 849C08D41E0CACA3006B03FA /* RSWebiOS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 849C08B41E0CAC85006B03FA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 849C08BD1E0CAC86006B03FA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 849C08D31E0CACA3006B03FA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 849C08B11E0CAC85006B03FA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 842ED2F01E12FB9B000CF738 /* HTTPResponseHeader.swift in Sources */, + 842ED2FF1E12FBC1000CF738 /* UserAgent.swift in Sources */, + 842ED2FC1E12FBBB000CF738 /* DownloadObject.swift in Sources */, + 842ED30E1E12FBDD000CF738 /* URLResponse+RSWeb.swift in Sources */, + 842ED2F61E12FBAF000CF738 /* OneShotDownload.swift in Sources */, + 842ED2F31E12FBAA000CF738 /* DownloadSession.swift in Sources */, + 842ED3081E12FBD2000CF738 /* URLRequest+RSWeb.swift in Sources */, + 842ED3051E12FBCC000CF738 /* NSMutableURLRequest+RSWeb.swift in Sources */, + 842ED2E71E12FB8A000CF738 /* HTTPRequestHeader.swift in Sources */, + 842ED3111E12FBE1000CF738 /* MacWebBrowser.swift in Sources */, + 842ED3141E12FBE7000CF738 /* MimeType.swift in Sources */, + 842ED30B1E12FBD8000CF738 /* URL+RSWeb.swift in Sources */, + 842ED2F91E12FBB5000CF738 /* DownloadProgress.swift in Sources */, + 842ED2EA1E12FB91000CF738 /* HTTPMethod.swift in Sources */, + 842ED3021E12FBC7000CF738 /* HTTPConditionalGetInfo.swift in Sources */, + 842ED2ED1E12FB97000CF738 /* HTTPResponseCode.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 849C08BB1E0CAC86006B03FA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 849C08C51E0CAC86006B03FA /* RSWebTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 849C08D01E0CACA3006B03FA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 842ED3091E12FBD2000CF738 /* URLRequest+RSWeb.swift in Sources */, + 842ED2F71E12FBAF000CF738 /* OneShotDownload.swift in Sources */, + 842ED30F1E12FBDD000CF738 /* URLResponse+RSWeb.swift in Sources */, + 842ED2E81E12FB8A000CF738 /* HTTPRequestHeader.swift in Sources */, + 842ED3001E12FBC1000CF738 /* UserAgent.swift in Sources */, + 842ED2FA1E12FBB5000CF738 /* DownloadProgress.swift in Sources */, + 842ED2EB1E12FB91000CF738 /* HTTPMethod.swift in Sources */, + 842ED2F11E12FB9B000CF738 /* HTTPResponseHeader.swift in Sources */, + 842ED3061E12FBCC000CF738 /* NSMutableURLRequest+RSWeb.swift in Sources */, + 842ED2F41E12FBAA000CF738 /* DownloadSession.swift in Sources */, + 842ED2EE1E12FB97000CF738 /* HTTPResponseCode.swift in Sources */, + 842ED30C1E12FBD8000CF738 /* URL+RSWeb.swift in Sources */, + 842ED3151E12FBE7000CF738 /* MimeType.swift in Sources */, + 842ED3031E12FBC7000CF738 /* HTTPConditionalGetInfo.swift in Sources */, + 842ED2FD1E12FBBB000CF738 /* DownloadObject.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 849C08C21E0CAC86006B03FA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 849C08B51E0CAC85006B03FA /* RSWeb */; + targetProxy = 849C08C11E0CAC86006B03FA /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 849C08C81E0CAC86006B03FA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_STATIC_ANALYZER_MODE = deep; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_STRICT_SELECTOR_MATCH = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 849C08C91E0CAC86006B03FA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_STATIC_ANALYZER_MODE = deep; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_STRICT_SELECTOR_MATCH = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 849C08CB1E0CAC86006B03FA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 9C84TZ7Q6Z; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = RSWeb/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSWeb; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 849C08CC1E0CAC86006B03FA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 9C84TZ7Q6Z; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = RSWeb/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSWeb; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; + 849C08CE1E0CAC86006B03FA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 9C84TZ7Q6Z; + INFOPLIST_FILE = RSWebTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSWebTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 849C08CF1E0CAC86006B03FA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 9C84TZ7Q6Z; + INFOPLIST_FILE = RSWebTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSWebTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; + 849C08DB1E0CACA3006B03FA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 9C84TZ7Q6Z; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = RSWebiOS/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSWebiOS; + PRODUCT_NAME = RSWeb; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 849C08DC1E0CACA3006B03FA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 9C84TZ7Q6Z; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = RSWebiOS/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSWebiOS; + PRODUCT_NAME = RSWeb; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 849C08B01E0CAC85006B03FA /* Build configuration list for PBXProject "RSWeb" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 849C08C81E0CAC86006B03FA /* Debug */, + 849C08C91E0CAC86006B03FA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 849C08CA1E0CAC86006B03FA /* Build configuration list for PBXNativeTarget "RSWeb" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 849C08CB1E0CAC86006B03FA /* Debug */, + 849C08CC1E0CAC86006B03FA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 849C08CD1E0CAC86006B03FA /* Build configuration list for PBXNativeTarget "RSWebTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 849C08CE1E0CAC86006B03FA /* Debug */, + 849C08CF1E0CAC86006B03FA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 849C08DA1E0CACA3006B03FA /* Build configuration list for PBXNativeTarget "RSWebiOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 849C08DB1E0CACA3006B03FA /* Debug */, + 849C08DC1E0CACA3006B03FA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 849C08AD1E0CAC85006B03FA /* Project object */; +} diff --git a/Frameworks/RSWeb/RSWeb.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Frameworks/RSWeb/RSWeb.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000..329456513 --- /dev/null +++ b/Frameworks/RSWeb/RSWeb.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Frameworks/RSWeb/RSWeb/DownloadObject.swift b/Frameworks/RSWeb/RSWeb/DownloadObject.swift new file mode 100755 index 000000000..5ffd1f32f --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/DownloadObject.swift @@ -0,0 +1,32 @@ +// +// DownloadObject.swift +// RSWeb +// +// Created by Brent Simmons on 8/3/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public final class DownloadObject: Hashable { + + public let url: URL + public var data = Data() + + public var hashValue: Int { + get { + return url.hashValue + } + } + + public init(url: URL) { + + self.url = url + } + + public static func ==(lhs: DownloadObject, rhs: DownloadObject) -> Bool { + + return lhs.url == rhs.url && lhs.data == rhs.data + } +} + diff --git a/Frameworks/RSWeb/RSWeb/DownloadProgress.swift b/Frameworks/RSWeb/RSWeb/DownloadProgress.swift new file mode 100755 index 000000000..cecb8feff --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/DownloadProgress.swift @@ -0,0 +1,74 @@ +// +// DownloadProgress.swift +// RSWeb +// +// Created by Brent Simmons on 9/17/16. +// Copyright © 2016 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +public extension Notification.Name { + + public static let DownloadProgressDidChange = Notification.Name(rawValue: "DownloadProgressDidChangeNotification") +} + +public class DownloadProgress { + + public var numberOfTasks = 0 { + didSet { + if numberOfTasks == 0 { + numberRemaining = 0 + } + postDidChangeNotification() + } + } + + public var numberRemaining = 0 { + didSet { + postDidChangeNotification() + } + } + + public var numberCompleted: Int { + get { + var n = numberOfTasks - numberRemaining + if n < 0 { + n = 0 + } + if n > numberOfTasks { + n = numberOfTasks + } + return n + } + } + + public var isComplete: Bool { + get { + return numberRemaining < 1 + } + } + + public init(numberOfTasks: Int) { + + self.numberOfTasks = numberOfTasks + } + + public func addToNumberOfTasks(_ n: Int) { + + numberOfTasks = numberOfTasks + n + } + + public func clear() { + + numberOfTasks = 0 + } +} + +private extension DownloadProgress { + + func postDidChangeNotification() { + + NotificationCenter.default.post(name: .DownloadProgressDidChange, object: self) + } +} diff --git a/Frameworks/RSWeb/RSWeb/DownloadSession.swift b/Frameworks/RSWeb/RSWeb/DownloadSession.swift new file mode 100755 index 000000000..dfbe134ca --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/DownloadSession.swift @@ -0,0 +1,304 @@ +// +// DownloadSession.swift +// RSWeb +// +// Created by Brent Simmons on 3/12/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +// Create a DownloadSessionDelegate, then create a DownloadSession. +// To download things: call downloadObjects, with a set of represented objects, to download things. DownloadSession will call the various delegate methods. + +public protocol DownloadSessionDelegate { + + func downloadSession(_ downloadSession: DownloadSession, requestForRepresentedObject: AnyObject) -> URLRequest? + + func downloadSession(_ downloadSession: DownloadSession, downloadDidCompleteForRepresentedObject: AnyObject, response: URLResponse?, data: Data, error: NSError?) + + func downloadSession(_ downloadSession: DownloadSession, shouldContinueAfterReceivingData: Data, representedObject: AnyObject) -> Bool + + func downloadSession(_ downloadSession: DownloadSession, didReceiveUnexpectedResponse: URLResponse, representedObject: AnyObject) + + func downloadSession(_ downloadSession: DownloadSession, didReceiveNotModifiedResponse: URLResponse, representedObject: AnyObject) +} + + +@objc public final class DownloadSession: NSObject, URLSessionDataDelegate { + + public var progress = DownloadProgress(numberOfTasks: 0) + + fileprivate var urlSession: URLSession! + fileprivate var tasksInProgress = Set() + fileprivate var tasksPending = Set() + fileprivate var taskIdentifierToInfoDictionary = [Int: DownloadInfo]() + fileprivate let representedObjects = NSMutableSet() + fileprivate let delegate: DownloadSessionDelegate + fileprivate var redirectCache = [String: String]() + + public init(delegate: DownloadSessionDelegate) { + + self.delegate = delegate + + super.init() + + let sessionConfiguration = URLSessionConfiguration.default + sessionConfiguration.requestCachePolicy = .reloadIgnoringLocalCacheData + sessionConfiguration.timeoutIntervalForRequest = 60.0 + sessionConfiguration.httpShouldSetCookies = false + sessionConfiguration.httpCookieAcceptPolicy = .never + sessionConfiguration.httpMaximumConnectionsPerHost = 2 + sessionConfiguration.httpCookieStorage = nil + sessionConfiguration.urlCache = nil + + if let userAgent = UserAgent.fromInfoPlist() { + var headers = [AnyHashable : Any]() + headers[HTTPRequestHeader.userAgent] = userAgent + sessionConfiguration.httpAdditionalHeaders = headers + } + + urlSession = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: OperationQueue.main) + } + + deinit { + urlSession.invalidateAndCancel() + } + + // MARK: URLSessionTaskDelegate + + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + + tasksInProgress.remove(task) + + guard let info = infoForTask(task) else { + return + } + + info.error = error + + delegate.downloadSession(self, downloadDidCompleteForRepresentedObject: info.representedObject, response: info.urlResponse, data: info.data as Data, error: error as NSError?) + + removeTask(task) + } + + public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { + + if response.statusCode == 301 || response.statusCode == 308 { + if let oldURLString = task.originalRequest?.url?.absoluteString, let newURLString = request.url?.absoluteString { + cacheRedirect(oldURLString, newURLString) + } + } + + completionHandler(request) + } + + // MARK: URLSessionDataDelegate + + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { + + tasksInProgress.insert(dataTask) + tasksPending.remove(dataTask) + + if let info = infoForTask(dataTask) { + info.urlResponse = response + } + + if response.forcedStatusCode == 304 { + + if let representedObject = infoForTask(dataTask)?.representedObject { + delegate.downloadSession(self, didReceiveNotModifiedResponse: response, representedObject: representedObject) + } + + completionHandler(.cancel) + removeTask(dataTask) + + return + } + + if !response.statusIsOK { + + if let representedObject = infoForTask(dataTask)?.representedObject { + delegate.downloadSession(self, didReceiveUnexpectedResponse: response, representedObject: representedObject) + } + + completionHandler(.cancel) + removeTask(dataTask) + + return + } + + completionHandler(.allow) + } + + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + + guard let info = infoForTask(dataTask) else { + return + } + info.addData(data) + + if !delegate.downloadSession(self, shouldContinueAfterReceivingData: info.data as Data, representedObject: info.representedObject) { + + info.canceled = true + dataTask.cancel() + removeTask(dataTask) + } + } + + // MARK: API + + public func cancel() { + + // TODO + } + + public func downloadObjects(_ objects: NSSet) { + + var numberOfTasksAdded = 0 + + for oneObject in objects { + + if !representedObjects.contains(oneObject) { + representedObjects.add(oneObject) + addDataTask(oneObject as AnyObject) + numberOfTasksAdded += 1 + } + } + + progress.addToNumberOfTasks(numberOfTasksAdded) + updateProgress() + } +} + +private extension DownloadSession { + + func updateProgress() { + + progress.numberRemaining = tasksInProgress.count + tasksPending.count + if progress.numberRemaining < 1 { + progress.clear() + representedObjects.removeAllObjects() + } + } + + func addDataTask(_ representedObject: AnyObject) { + + guard let request = delegate.downloadSession(self, requestForRepresentedObject: representedObject) else { + return + } + + var requestToUse = request + + // If received permanent redirect earlier, use that URL. + + if let urlString = request.url?.absoluteString, let redirectedURLString = cachedRedirectForURLString(urlString) { + if let redirectedURL = URL(string: redirectedURLString) { + requestToUse.url = redirectedURL + } + } + + let task = urlSession.dataTask(with: requestToUse) + + let info = DownloadInfo(representedObject, urlRequest: requestToUse) + taskIdentifierToInfoDictionary[task.taskIdentifier] = info + + tasksPending.insert(task) + task.resume() + } + + func infoForTask(_ task: URLSessionTask) -> DownloadInfo? { + + return taskIdentifierToInfoDictionary[task.taskIdentifier] + } + + func removeTask(_ task: URLSessionTask) { + + tasksInProgress.remove(task) + tasksPending.remove(task) + taskIdentifierToInfoDictionary[task.taskIdentifier] = nil + updateProgress() + } + + func urlStringIsBlackListedRedirect(_ urlString: String) -> Bool { + + // Hotels and similar often do permanent redirects. We can catch some of those. + + let s = urlString.lowercased() + let badStrings = ["solutionip", "lodgenet", "monzoon", "landingpage", "btopenzone", "register", "login", "authentic"] + + for oneBadString in badStrings { + if s.contains(oneBadString) { + return true + } + } + + return false + } + + func cacheRedirect(_ oldURLString: String, _ newURLString: String) { + + if urlStringIsBlackListedRedirect(newURLString) { + return + } + + redirectCache[oldURLString] = newURLString + } + + func cachedRedirectForURLString(_ urlString: String) -> String? { + + // Follow chains of redirects, but avoid loops. + + var urlStrings = Set() + urlStrings.insert(urlString) + + var currentString = urlString + + while(true) { + + if let oneRedirectString = redirectCache[currentString] { + + if urlStrings.contains(oneRedirectString) { + // Cycle. Bail. + return nil + } + urlStrings.insert(oneRedirectString) + currentString = oneRedirectString + } + + else { + break + } + } + + return currentString == urlString ? nil : currentString + } +} + +private final class DownloadInfo { + + let representedObject: AnyObject + let urlRequest: URLRequest + let data = NSMutableData() + var error: Error? + var urlResponse: URLResponse? + var canceled = false + + var statusCode: Int { + get { + return urlResponse?.forcedStatusCode ?? 0 + } + } + + init(_ representedObject: AnyObject, urlRequest: URLRequest) { + + self.representedObject = representedObject + self.urlRequest = urlRequest + } + + func addData(_ d: Data) { + + data.append(d) + } +} + diff --git a/Frameworks/RSWeb/RSWeb/HTTPConditionalGetInfo.swift b/Frameworks/RSWeb/RSWeb/HTTPConditionalGetInfo.swift new file mode 100755 index 000000000..0798b84a3 --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/HTTPConditionalGetInfo.swift @@ -0,0 +1,44 @@ +// +// HTTPConditionalGetInfo.swift +// RSWeb +// +// Created by Brent Simmons on 4/11/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public struct HTTPConditionalGetInfo { + + public let lastModified: String? + public let etag: String? + public var isEmpty: Bool { + get { + return lastModified == nil && etag == nil + } + } + + public init(lastModified: String?, etag: String?) { + + self.lastModified = lastModified + self.etag = etag + } + + public init(urlResponse: HTTPURLResponse) { + + let lastModified = urlResponse.valueForHTTPHeaderField(HTTPResponseHeader.lastModified) + let etag = urlResponse.valueForHTTPHeaderField(HTTPResponseHeader.etag) + + self.init(lastModified: lastModified, etag: etag) + } + + public func addRequestHeadersToURLRequest(_ urlRequest: NSMutableURLRequest) { + + if let lastModified = lastModified { + urlRequest.addValue(lastModified, forHTTPHeaderField: HTTPRequestHeader.ifModifiedSince) + } + if let etag = etag { + urlRequest.addValue(etag, forHTTPHeaderField: HTTPRequestHeader.ifNoneMatch) + } + } +} diff --git a/Frameworks/RSWeb/RSWeb/HTTPMethod.swift b/Frameworks/RSWeb/RSWeb/HTTPMethod.swift new file mode 100755 index 000000000..43e117c7e --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/HTTPMethod.swift @@ -0,0 +1,18 @@ +// +// HTTPMethod.swift +// RSWeb +// +// Created by Brent Simmons on 12/26/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public struct HTTPMethod { + + public static let get = "GET" + public static let post = "POST" + public static let put = "PUT" + public static let patch = "PATCH" + public static let delete = "DELETE" +} diff --git a/Frameworks/RSWeb/RSWeb/HTTPRequestHeader.swift b/Frameworks/RSWeb/RSWeb/HTTPRequestHeader.swift new file mode 100755 index 000000000..6b177b129 --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/HTTPRequestHeader.swift @@ -0,0 +1,21 @@ +// +// HTTPRequestHeader.swift +// RSWeb +// +// Created by Brent Simmons on 12/26/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public struct HTTPRequestHeader { + + public static let userAgent = "User-Agent" + public static let authorization = "Authorization" + public static let contentType = "Content-Type" + + // Conditional GET + + public static let ifModifiedSince = "If-Modified-Since" + public static let ifNoneMatch = "If-None-Match" //Etag +} diff --git a/Frameworks/RSWeb/RSWeb/HTTPResponseCode.swift b/Frameworks/RSWeb/RSWeb/HTTPResponseCode.swift new file mode 100755 index 000000000..697164906 --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/HTTPResponseCode.swift @@ -0,0 +1,61 @@ +// +// HTTPResponseCode.swift +// RSWeb +// +// Created by Brent Simmons on 12/26/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public struct HTTPResponseCode { + + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + // Not an enum because the main interest is the actual values. + + public static let responseContinue = 100 //"continue" is a language keyword, hence the weird name + public static let switchingProtocols = 101 + + public static let OK = 200 + public static let created = 201 + public static let accepted = 202 + public static let nonAuthoritativeInformation = 203 + public static let noContent = 204 + public static let resetContent = 205 + public static let partialContent = 206 + + public static let redirectMultipleChoices = 300 + public static let redirectPermanent = 301 + public static let redirectTemporary = 302 + public static let redirectSeeOther = 303 + public static let notModified = 304 + public static let useProxy = 305 + public static let unused = 306 + public static let redirectVeryTemporary = 307 + + public static let badRequest = 400 + public static let unauthorized = 401 + public static let paymentRequired = 402 + public static let forbidden = 403 + public static let notFound = 404 + public static let methodNotAllowed = 405 + public static let notAcceptable = 406 + public static let proxyAuthenticationRequired = 407 + public static let requestTimeout = 408 + public static let conflict = 409 + public static let goone = 410 + public static let lengthRequired = 411 + public static let preconditionFailed = 412 + public static let entityTooLarge = 413 + public static let URITooLong = 414 + public static let unsupportedMediaType = 415 + public static let requestedRangeNotSatisfiable = 416 + public static let expectationFailed = 417 + + public static let internalServerError = 500 + public static let notImplemented = 501 + public static let badGateway = 502 + public static let serviceUnavailable = 503 + public static let gatewayTimeout = 504 + public static let HTTPVersionNotSupported = 505 +} diff --git a/Frameworks/RSWeb/RSWeb/HTTPResponseHeader.swift b/Frameworks/RSWeb/RSWeb/HTTPResponseHeader.swift new file mode 100755 index 000000000..fcaec3295 --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/HTTPResponseHeader.swift @@ -0,0 +1,20 @@ +// +// HTTPResponseHeader.swift +// RSWeb +// +// Created by Brent Simmons on 12/26/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public struct HTTPResponseHeader { + + public static let contentType = "Content-Type" + + // Conditional GET. See: + // http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers/ + + public static let lastModified = "Last-Modified" + public static let etag = "ETag" +} diff --git a/Frameworks/RSWeb/RSWeb/Info.plist b/Frameworks/RSWeb/RSWeb/Info.plist new file mode 100755 index 000000000..ea4032433 --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2016 Ranchero Software. All rights reserved. + NSPrincipalClass + + + diff --git a/Frameworks/RSWeb/RSWeb/MacWebBrowser.swift b/Frameworks/RSWeb/RSWeb/MacWebBrowser.swift new file mode 100755 index 000000000..05dc6cdb6 --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/MacWebBrowser.swift @@ -0,0 +1,44 @@ +// +// MacWebBrowser.swift +// RSWeb +// +// Created by Brent Simmons on 12/27/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Cocoa + +public class MacWebBrowser { + + public class func openURL(_ url: URL, inBackground: Bool) -> Bool { + + guard let preparedURL = url.preparedForOpeningInBrowser() else { + return false + } + + if (inBackground) { + do { + try NSWorkspace.shared().open(preparedURL, options: [.withoutActivation], configuration: [String: Any]()) + return true + } + catch { + return false + } + } + + return NSWorkspace.shared().open(preparedURL) + } +} + +private extension URL { + + func preparedForOpeningInBrowser() -> URL? { + + var urlString = absoluteString.replacingOccurrences(of: " ", with: "%20") + urlString = urlString.replacingOccurrences(of: "^", with: "%5E") + urlString = urlString.replacingOccurrences(of: "&", with: "&") + urlString = urlString.replacingOccurrences(of: "&", with: "&") + + return URL(string: urlString) + } +} diff --git a/Frameworks/RSWeb/RSWeb/MimeType.swift b/Frameworks/RSWeb/RSWeb/MimeType.swift new file mode 100755 index 000000000..4bdd06db8 --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/MimeType.swift @@ -0,0 +1,55 @@ +// +// MimeType.swift +// RSWeb +// +// Created by Brent Simmons on 12/26/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public struct MimeType { + + // This could certainly use expansion. + + public static let png = "image/png" + public static let jpeg = "image/jpeg" + public static let jpg = "image/jpg" + public static let gif = "image/gif" + public static let tiff = "image/tiff" +} + +public extension String { + + public func isMimeTypeImage() -> Bool { + + return self.isOfGeneralMimeType("image") + } + + public func isMimeTypeAudio() -> Bool { + + return self.isOfGeneralMimeType("audio") + } + + public func isMimeTypeVideo() -> Bool { + + return self.isOfGeneralMimeType("video") + } + + public func isMimeTypeTimeBasedMedia() -> Bool { + + return self.isMimeTypeAudio() || self.isMimeTypeVideo() + } + + private func isOfGeneralMimeType(_ type: String) -> Bool { + + let lower = self.lowercased() + if lower.hasPrefix(type) { + return true + } + if lower.hasPrefix("x-\(type)") { + return true + } + return false + } +} diff --git a/Frameworks/RSWeb/RSWeb/NSMutableURLRequest+RSWeb.swift b/Frameworks/RSWeb/RSWeb/NSMutableURLRequest+RSWeb.swift new file mode 100755 index 000000000..7b0c4903b --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/NSMutableURLRequest+RSWeb.swift @@ -0,0 +1,28 @@ +// +// NSMutableURLRequest+RSWeb.swift +// RSWeb +// +// Created by Brent Simmons on 12/27/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public extension NSMutableURLRequest { + + public func addBasicAuthorization(username: String, password: String) -> Bool { + + // Do this *only* with https. And not even then if you can help it. + + let s = "\(username):\(password)" + guard let d = s.data(using: String.Encoding.utf8, allowLossyConversion: false) else { + return false + } + + let base64EncodedString = d.base64EncodedString() + let authorization = "Basic \(base64EncodedString)" + setValue(authorization, forHTTPHeaderField: HTTPRequestHeader.authorization) + + return true + } +} diff --git a/Frameworks/RSWeb/RSWeb/OneShotDownload.swift b/Frameworks/RSWeb/RSWeb/OneShotDownload.swift new file mode 100755 index 000000000..aff8282de --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/OneShotDownload.swift @@ -0,0 +1,60 @@ +// +// OneShotDownload.swift +// RSWeb +// +// Created by Brent Simmons on 8/27/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public typealias OneShotDownloadCallback = (Data?, URLResponse?, Error?) -> Swift.Void + +private final class OneShotDownloadManager { + + private let urlSession: URLSession + fileprivate static let shared = OneShotDownloadManager() + + public init() { + + let sessionConfiguration = URLSessionConfiguration.ephemeral + sessionConfiguration.requestCachePolicy = .reloadIgnoringLocalCacheData + sessionConfiguration.httpShouldSetCookies = false + sessionConfiguration.httpCookieAcceptPolicy = .never + sessionConfiguration.httpMaximumConnectionsPerHost = 2 + sessionConfiguration.httpCookieStorage = nil + sessionConfiguration.urlCache = nil + sessionConfiguration.timeoutIntervalForRequest = 30 + + if let userAgent = UserAgent.fromInfoPlist() { + var headers = [AnyHashable : Any]() + headers[HTTPRequestHeader.userAgent] = userAgent + sessionConfiguration.httpAdditionalHeaders = headers + } + + urlSession = URLSession(configuration: sessionConfiguration) + } + + deinit { + urlSession.invalidateAndCancel() + } + + public func download(_ url: URL, _ callback: @escaping OneShotDownloadCallback) { + + let task = urlSession.dataTask(with: url) { (data, response, error) in + + DispatchQueue.main.async() { + callback(data, response, error) + } + } + task.resume() + } +} + +// Call this. It’s easier than referring to OneShotDownloadManager. +// callback is called on the main queue. + +public func download(_ url: URL, _ callback: @escaping OneShotDownloadCallback) { + + OneShotDownloadManager.shared.download(url, callback) +} diff --git a/Frameworks/RSWeb/RSWeb/URL+RSWeb.swift b/Frameworks/RSWeb/RSWeb/URL+RSWeb.swift new file mode 100755 index 000000000..4c5d57a4a --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/URL+RSWeb.swift @@ -0,0 +1,69 @@ +// +// NSURL+RSWeb.swift +// RSWeb +// +// Created by Brent Simmons on 12/26/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +private struct URLConstants { + static let schemeHTTP = "http" + static let schemeHTTPS = "https" + static let prefixHTTP = "http://" + static let prefixHTTPS = "https://" +} + +public extension URL { + + public func isHTTPSURL() -> Bool { + + return self.scheme?.lowercased() == URLConstants.schemeHTTPS + } + + public func isHTTPURL() -> Bool { + + return self.scheme?.lowercased() == URLConstants.schemeHTTP + } + + public func isHTTPOrHTTPSURL() -> Bool { + + return self.isHTTPSURL() || self.isHTTPURL() + } + + public func absoluteStringWithHTTPOrHTTPSPrefixRemoved() -> String? { + + // Case-inensitive. Turns http://example.com/foo into example.com/foo + + if isHTTPSURL() { + return absoluteString.stringByRemovingCaseInsensitivePrefix(URLConstants.prefixHTTPS) + } + else if isHTTPURL() { + return absoluteString.stringByRemovingCaseInsensitivePrefix(URLConstants.prefixHTTP) + } + + return nil + } +} + +private extension String { + + func stringByRemovingCaseInsensitivePrefix(_ prefix: String) -> String { + + // Returns self if it doesn’t have the given prefix. + + let lowerPrefix = prefix.lowercased() + let lowerSelf = self.lowercased() + + if (lowerSelf == lowerPrefix) { + return "" + } + if !lowerSelf.hasPrefix(lowerPrefix) { + return self + } + + let index = self.index(self.startIndex, offsetBy: prefix.characters.count) + return substring(from: index) + } +} diff --git a/Frameworks/RSWeb/RSWeb/URLRequest+RSWeb.swift b/Frameworks/RSWeb/RSWeb/URLRequest+RSWeb.swift new file mode 100755 index 000000000..5da49e44b --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/URLRequest+RSWeb.swift @@ -0,0 +1,29 @@ +// +// URLRequest+RSWeb.swift +// RSWeb +// +// Created by Brent Simmons on 12/27/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public extension URLRequest { + + // Experimental. Returns nil if scheme isn't http or https (about:blank, for instance). + + public func loadingURL() -> URL? { + + guard let url = mainDocumentURL else { + return nil + } + guard url.isHTTPOrHTTPSURL() else { + return nil + } + guard !url.absoluteString.isEmpty else { + return nil + } + + return url + } +} diff --git a/Frameworks/RSWeb/RSWeb/URLResponse+RSWeb.swift b/Frameworks/RSWeb/RSWeb/URLResponse+RSWeb.swift new file mode 100755 index 000000000..dbe46080f --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/URLResponse+RSWeb.swift @@ -0,0 +1,49 @@ +// +// URLResponse+RSWeb.swift +// RSWeb +// +// Created by Brent Simmons on 8/14/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public extension URLResponse { + + public var statusIsOK: Bool { + get { + return forcedStatusCode >= 200 && forcedStatusCode <= 299 + } + } + + public var forcedStatusCode: Int { + + get { + // Return actual statusCode or -1 if there isn’t one. + + if let response = self as? HTTPURLResponse { + return response.statusCode + } + return 0 + } + } +} + +public extension HTTPURLResponse { + + public func valueForHTTPHeaderField(_ headerField: String) -> String? { + + // Case-insensitive. HTTP headers may not be in the case you expect. + + let lowerHeaderField = headerField.lowercased() + + for (key, value) in allHeaderFields { + + if lowerHeaderField == (key as? String)?.lowercased() { + return value as? String + } + } + + return nil + } +} diff --git a/Frameworks/RSWeb/RSWeb/UserAgent.swift b/Frameworks/RSWeb/RSWeb/UserAgent.swift new file mode 100755 index 000000000..d2bdc65cb --- /dev/null +++ b/Frameworks/RSWeb/RSWeb/UserAgent.swift @@ -0,0 +1,23 @@ +// +// UserAgent.swift +// RSWeb +// +// Created by Brent Simmons on 8/27/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import Foundation + +public class UserAgent { + + public class func fromInfoPlist() -> String? { + + guard let userAgentName = Bundle.main.object(forInfoDictionaryKey: "UserAgent") else { + return nil + } + guard let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") else { + return nil + } + return "\(userAgentName) \(version)" + } +} diff --git a/Frameworks/RSWeb/RSWebTests/Info.plist b/Frameworks/RSWeb/RSWebTests/Info.plist new file mode 100755 index 000000000..6c6c23c43 --- /dev/null +++ b/Frameworks/RSWeb/RSWebTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Frameworks/RSWeb/RSWebTests/RSWebTests.swift b/Frameworks/RSWeb/RSWebTests/RSWebTests.swift new file mode 100755 index 000000000..c35e02dc0 --- /dev/null +++ b/Frameworks/RSWeb/RSWebTests/RSWebTests.swift @@ -0,0 +1,36 @@ +// +// RSWebTests.swift +// RSWebTests +// +// Created by Brent Simmons on 12/22/16. +// Copyright © 2016 Ranchero Software. All rights reserved. +// + +import XCTest +@testable import RSWeb + +class RSWebTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Frameworks/RSWeb/RSWebiOS/Info.plist b/Frameworks/RSWeb/RSWebiOS/Info.plist new file mode 100755 index 000000000..6e571e315 --- /dev/null +++ b/Frameworks/RSWeb/RSWebiOS/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + RSWeb + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + +