Add RSWeb framework.

This commit is contained in:
Brent Simmons 2017-05-22 13:17:28 -07:00
parent 02503fadb9
commit 074bbca652
26 changed files with 1934 additions and 0 deletions

View File

@ -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 = "<group>"; };
84B06F961ED37DA000F0B54B /* RSDatabase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSDatabase.xcodeproj; path = Frameworks/RSDatabase/RSDatabase.xcodeproj; sourceTree = "<group>"; };
84B06FA21ED37DAC00F0B54B /* RSCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSCore.xcodeproj; path = Frameworks/RSCore/RSCore.xcodeproj; sourceTree = "<group>"; };
84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSWeb.xcodeproj; path = Frameworks/RSWeb/RSWeb.xcodeproj; sourceTree = "<group>"; };
/* 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 = "<group>";
@ -230,6 +264,16 @@
name = Products;
sourceTree = "<group>";
};
84B06FB71ED37E8B00F0B54B /* Products */ = {
isa = PBXGroup;
children = (
84B06FBD1ED37E8C00F0B54B /* RSWeb.framework */,
84B06FBF1ED37E8C00F0B54B /* RSWebTests.xctest */,
84B06FC11ED37E8C00F0B54B /* RSWeb.framework */,
);
name = Products;
sourceTree = "<group>";
};
/* 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 */

65
Frameworks/RSWeb/.gitignore vendored Executable file
View File

@ -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

21
Frameworks/RSWeb/LICENSE Executable file
View File

@ -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.

19
Frameworks/RSWeb/README.md Executable file
View File

@ -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. Its easy.
#### Slightly less easy way
See `DownloadSession` and `DownloadSessionDelegate` for when youre doing a bunch of downloads and you need to track progress.
#### Extras
`HTTPConditionalGetInfo` helps with supporting conditional GET, for when youre 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.

View File

@ -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 = "<group>"; };
842ED2E91E12FB91000CF738 /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPMethod.swift; path = RSWeb/HTTPMethod.swift; sourceTree = "<group>"; };
842ED2EC1E12FB97000CF738 /* HTTPResponseCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPResponseCode.swift; path = RSWeb/HTTPResponseCode.swift; sourceTree = "<group>"; };
842ED2EF1E12FB9B000CF738 /* HTTPResponseHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPResponseHeader.swift; path = RSWeb/HTTPResponseHeader.swift; sourceTree = "<group>"; };
842ED2F21E12FBAA000CF738 /* DownloadSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DownloadSession.swift; path = RSWeb/DownloadSession.swift; sourceTree = "<group>"; };
842ED2F51E12FBAF000CF738 /* OneShotDownload.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OneShotDownload.swift; path = RSWeb/OneShotDownload.swift; sourceTree = "<group>"; };
842ED2F81E12FBB5000CF738 /* DownloadProgress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DownloadProgress.swift; path = RSWeb/DownloadProgress.swift; sourceTree = "<group>"; };
842ED2FB1E12FBBB000CF738 /* DownloadObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DownloadObject.swift; path = RSWeb/DownloadObject.swift; sourceTree = "<group>"; };
842ED2FE1E12FBC1000CF738 /* UserAgent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UserAgent.swift; path = RSWeb/UserAgent.swift; sourceTree = "<group>"; };
842ED3011E12FBC7000CF738 /* HTTPConditionalGetInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HTTPConditionalGetInfo.swift; path = RSWeb/HTTPConditionalGetInfo.swift; sourceTree = "<group>"; };
842ED3041E12FBCC000CF738 /* NSMutableURLRequest+RSWeb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSMutableURLRequest+RSWeb.swift"; path = "RSWeb/NSMutableURLRequest+RSWeb.swift"; sourceTree = "<group>"; };
842ED3071E12FBD2000CF738 /* URLRequest+RSWeb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "URLRequest+RSWeb.swift"; path = "RSWeb/URLRequest+RSWeb.swift"; sourceTree = "<group>"; };
842ED30A1E12FBD8000CF738 /* URL+RSWeb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "URL+RSWeb.swift"; path = "RSWeb/URL+RSWeb.swift"; sourceTree = "<group>"; };
842ED30D1E12FBDD000CF738 /* URLResponse+RSWeb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "URLResponse+RSWeb.swift"; path = "RSWeb/URLResponse+RSWeb.swift"; sourceTree = "<group>"; };
842ED3101E12FBE1000CF738 /* MacWebBrowser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MacWebBrowser.swift; path = RSWeb/MacWebBrowser.swift; sourceTree = "<group>"; };
842ED3131E12FBE7000CF738 /* MimeType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MimeType.swift; path = RSWeb/MimeType.swift; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
849C08C61E0CAC86006B03FA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
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 = "<group>"; };
/* 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 = "<group>";
};
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 = "<group>";
};
849C08B71E0CAC85006B03FA /* Products */ = {
isa = PBXGroup;
children = (
849C08B61E0CAC85006B03FA /* RSWeb.framework */,
849C08BF1E0CAC86006B03FA /* RSWebTests.xctest */,
849C08D51E0CACA3006B03FA /* RSWeb.framework */,
);
name = Products;
sourceTree = "<group>";
};
849C08B81E0CAC85006B03FA /* RSWeb */ = {
isa = PBXGroup;
children = (
849C08BA1E0CAC85006B03FA /* Info.plist */,
);
path = RSWeb;
sourceTree = "<group>";
};
849C08C31E0CAC86006B03FA /* RSWebTests */ = {
isa = PBXGroup;
children = (
849C08C41E0CAC86006B03FA /* RSWebTests.swift */,
849C08C61E0CAC86006B03FA /* Info.plist */,
);
path = RSWebTests;
sourceTree = "<group>";
};
849C08D61E0CACA3006B03FA /* RSWebiOS */ = {
isa = PBXGroup;
children = (
849C08D81E0CACA3006B03FA /* Info.plist */,
);
path = RSWebiOS;
sourceTree = "<group>";
};
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 = "<group>";
};
/* 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 */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:RSWeb.xcodeproj">
</FileRef>
</Workspace>

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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<URLSessionTask>()
fileprivate var tasksPending = Set<URLSessionTask>()
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<String>()
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)
}
}

View File

@ -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)
}
}
}

View File

@ -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"
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Ranchero Software. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -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: "&amp;", with: "&")
urlString = urlString.replacingOccurrences(of: "&#38;", with: "&")
return URL(string: urlString)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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. Its 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)
}

View File

@ -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 doesnt 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)
}
}

View File

@ -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
}
}

View File

@ -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 isnt 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
}
}

View File

@ -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)"
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -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.
}
}
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>RSWeb</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>