Skip to content

Commit

Permalink
Swift 3 support (#65)
Browse files Browse the repository at this point in the history
* Update to Swift 3

* Bump version 0.4.0

* Rename HTTPURLResponse file too

* Prefer private over fileprivate

* Fix serializating HTTPURLResponse

* Don't say we failed if we didn't

* Swift 3.0.1

* tvOS target

* Swift 3.0.1

* Fix tvOS deployment target

* Fix upload tests

* Explicitly set SWIFT_VERSION

Without this, Xcode complains about the legacy Swift version not being
set.

* Remove explicit toolchain from CI

* Specify SDK for CI

* Add destinations for CI

...because Travis CI is so good.

* Use latest version of Xcode

* Clean up spacing
  • Loading branch information
eliperkins authored and hyperspacemark committed Jan 23, 2017
1 parent 62f4bb6 commit f042a92
Show file tree
Hide file tree
Showing 18 changed files with 661 additions and 300 deletions.
1 change: 1 addition & 0 deletions .swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.0.1
11 changes: 6 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
language: objective-c # lol
osx_image: xcode8
osx_image: xcode8.2
xcode_project: DVR.xcodeproj

script: xcodebuild -scheme "$TRAVIS_XCODE_SCHEME" test
script: xcodebuild -scheme "$TRAVIS_XCODE_SCHEME" -sdk "$TRAVIS_XCODE_SDK" -destination "$DESTINATION" test

matrix:
include:
- xcode_scheme: DVR-iOS
xcode_sdk: iphonesimulator
env:
- DESTINATION="OS=10.1,name=iPhone 7 Plus"
- xcode_scheme: DVR-OSX
xcode_sdk: macosx

env:
TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3
env:
- DESTINATION="arch=x86_64"
289 changes: 273 additions & 16 deletions DVR.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions DVR.xcodeproj/xcshareddata/xcschemes/DVR-tvOS.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0810"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2104F52D1DC658580039CA14"
BuildableName = "DVR.framework"
BlueprintName = "DVR-tvOS"
ReferencedContainer = "container:DVR.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2104F5351DC658590039CA14"
BuildableName = "DVRTests.xctest"
BlueprintName = "DVRTests-tvOS"
ReferencedContainer = "container:DVR.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2104F52D1DC658580039CA14"
BuildableName = "DVR.framework"
BlueprintName = "DVR-tvOS"
ReferencedContainer = "container:DVR.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2104F52D1DC658580039CA14"
BuildableName = "DVR.framework"
BlueprintName = "DVR-tvOS"
ReferencedContainer = "container:DVR.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2104F52D1DC658580039CA14"
BuildableName = "DVR.framework"
BlueprintName = "DVR-tvOS"
ReferencedContainer = "container:DVR.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
26 changes: 13 additions & 13 deletions DVR/Cassette.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ struct Cassette {

// MARK: - Functions

func interactionForRequest(request: NSURLRequest) -> Interaction? {
func interactionForRequest(_ request: URLRequest) -> Interaction? {
for interaction in interactions {
let interactionRequest = interaction.request

// Note: We don't check headers right now
if interactionRequest.HTTPMethod == request.HTTPMethod && interactionRequest.URL == request.URL && interactionRequest.hasHTTPBodyEqualToThatOfRequest(request) {
if interactionRequest.httpMethod == request.httpMethod && interactionRequest.url == request.url && interactionRequest.hasHTTPBodyEqualToThatOfRequest(request) {
return interaction
}
}
Expand All @@ -33,34 +33,34 @@ struct Cassette {


extension Cassette {
var dictionary: [String: AnyObject] {
var dictionary: [String: Any] {
return [
"name": name,
"name": name as Any,
"interactions": interactions.map { $0.dictionary }
]
}

init?(dictionary: [String: AnyObject]) {
init?(dictionary: [String: Any]) {
guard let name = dictionary["name"] as? String else { return nil }

self.name = name

if let array = dictionary["interactions"] as? [[String: AnyObject]] {
if let array = dictionary["interactions"] as? [[String: Any]] {
interactions = array.flatMap { Interaction(dictionary: $0) }
} else {
interactions = []
}
}
}

private extension NSURLRequest {
func hasHTTPBodyEqualToThatOfRequest(request: NSURLRequest) -> Bool {
guard let body1 = self.HTTPBody,
body2 = request.HTTPBody,
encoded1 = Interaction.encodeBody(body1, headers: self.allHTTPHeaderFields),
encoded2 = Interaction.encodeBody(body2, headers: request.allHTTPHeaderFields)
private extension URLRequest {
func hasHTTPBodyEqualToThatOfRequest(_ request: URLRequest) -> Bool {
guard let body1 = self.httpBody,
let body2 = request.httpBody,
let encoded1 = Interaction.encodeBody(body1, headers: self.allHTTPHeaderFields),
let encoded2 = Interaction.encodeBody(body2, headers: request.allHTTPHeaderFields)
else {
return self.HTTPBody == request.HTTPBody
return self.httpBody == request.httpBody
}

return encoded1.isEqual(encoded2)
Expand Down
30 changes: 14 additions & 16 deletions DVR/URLHTTPResponse.swift → DVR/HTTPURLResponse.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import Foundation

// There isn't a mutable NSHTTPURLResponse, so we have to make our own.
class URLHTTPResponse: NSHTTPURLResponse {
// There isn't a mutable HTTPURLResponse, so we have to make our own.
class HTTPURLResponse: Foundation.HTTPURLResponse {

// MARK: - Properties

private var _URL: NSURL?
override var URL: NSURL? {
private var _url: URL?
override var url: URL? {
get {
return _URL ?? super.URL
return _url ?? super.url
}

set {
_URL = newValue
_url = newValue
}
}

Expand All @@ -27,8 +27,8 @@ class URLHTTPResponse: NSHTTPURLResponse {
}
}

private var _allHeaderFields: [NSObject : AnyObject]?
override var allHeaderFields: [NSObject : AnyObject] {
private var _allHeaderFields: [AnyHashable: Any]?
override var allHeaderFields: [AnyHashable: Any] {
get {
return _allHeaderFields ?? super.allHeaderFields
}
Expand All @@ -40,8 +40,8 @@ class URLHTTPResponse: NSHTTPURLResponse {
}


extension NSHTTPURLResponse {
override var dictionary: [String: AnyObject] {
extension Foundation.HTTPURLResponse {
override var dictionary: [String: Any] {
var dictionary = super.dictionary

dictionary["headers"] = allHeaderFields
Expand All @@ -52,13 +52,11 @@ extension NSHTTPURLResponse {
}


extension URLHTTPResponse {
convenience init(dictionary: [String: AnyObject]) {
self.init()
extension HTTPURLResponse {
convenience init(dictionary: [String: Any]) {
let url = URL(string: dictionary["url"] as! String)!

if let string = dictionary["url"] as? String, url = NSURL(string: string) {
URL = url
}
self.init(url: url, mimeType: nil, expectedContentLength: 0, textEncodingName: nil)

if let headers = dictionary["headers"] as? [String: String] {
allHeaderFields = headers
Expand Down
48 changes: 24 additions & 24 deletions DVR/Interaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ struct Interaction {

// MARK: - Properties

let request: NSURLRequest
let response: NSURLResponse
let responseData: NSData?
let recordedAt: NSDate
let request: URLRequest
let response: Foundation.URLResponse
let responseData: Data?
let recordedAt: Date


// MARK: - Initializers

init(request: NSURLRequest, response: NSURLResponse, responseData: NSData? = nil, recordedAt: NSDate = NSDate()) {
init(request: URLRequest, response: Foundation.URLResponse, responseData: Data? = nil, recordedAt: Date = Date()) {
self.request = request
self.response = response
self.responseData = responseData
Expand All @@ -22,42 +22,42 @@ struct Interaction {

// MARK: - Encoding

static func encodeBody(body: NSData, headers: [String: String]? = nil) -> AnyObject? {
static func encodeBody(_ body: Data, headers: [String: String]? = nil) -> AnyObject? {
if let contentType = headers?["Content-Type"] {
// Text
if contentType.hasPrefix("text/") {
// TODO: Use text encoding if specified in headers
return NSString(data: body, encoding: NSUTF8StringEncoding)
return NSString(data: body, encoding: String.Encoding.utf8.rawValue)
}

// JSON
if contentType.hasPrefix("application/json") {
do {
return try NSJSONSerialization.JSONObjectWithData(body, options: [])
return try JSONSerialization.jsonObject(with: body, options: []) as AnyObject
} catch {
return nil
}
}
}

// Base64
return body.base64EncodedStringWithOptions([])
return body.base64EncodedString(options: []) as AnyObject?
}

static func dencodeBody(body: AnyObject?, headers: [String: String]? = nil) -> NSData? {
static func dencodeBody(_ body: Any?, headers: [String: String]? = nil) -> Data? {
guard let body = body else { return nil }

if let contentType = headers?["Content-Type"] {
// Text
if let string = body as? String where contentType.hasPrefix("text/") {
if let string = body as? String , contentType.hasPrefix("text/") {
// TODO: Use encoding if specified in headers
return string.dataUsingEncoding(NSUTF8StringEncoding)
return string.data(using: String.Encoding.utf8)
}

// JSON
if contentType.hasPrefix("application/json") {
do {
return try NSJSONSerialization.dataWithJSONObject(body, options: [])
return try JSONSerialization.data(withJSONObject: body, options: [])
} catch {
return nil
}
Expand All @@ -66,7 +66,7 @@ struct Interaction {

// Base64
if let base64 = body as? String {
return NSData(base64EncodedString: base64, options: [])
return Data(base64Encoded: base64, options: [])
}

return nil
Expand All @@ -75,29 +75,29 @@ struct Interaction {


extension Interaction {
var dictionary: [String: AnyObject] {
var dictionary: [String: AnyObject] = [
var dictionary: [String: Any] {
var dictionary: [String: Any] = [
"request": request.dictionary,
"recorded_at": recordedAt.timeIntervalSince1970
]

var response = self.response.dictionary
if let data = responseData, body = Interaction.encodeBody(data, headers: response["headers"] as? [String: String]) {
if let data = responseData, let body = Interaction.encodeBody(data, headers: response["headers"] as? [String: String]) {
response["body"] = body
}
dictionary["response"] = response

return dictionary
}

init?(dictionary: [String: AnyObject]) {
guard let request = dictionary["request"] as? [String: AnyObject],
response = dictionary["response"] as? [String: AnyObject],
recordedAt = dictionary["recorded_at"] as? Int else { return nil }
init?(dictionary: [String: Any]) {
guard let request = dictionary["request"] as? [String: Any],
let response = dictionary["response"] as? [String: Any],
let recordedAt = dictionary["recorded_at"] as? Int else { return nil }

self.request = NSMutableURLRequest(dictionary: request)
self.response = URLHTTPResponse(dictionary: response)
self.recordedAt = NSDate(timeIntervalSince1970: NSTimeInterval(recordedAt))
self.request = NSMutableURLRequest(dictionary: request) as URLRequest
self.response = HTTPURLResponse(dictionary: response)
self.recordedAt = Date(timeIntervalSince1970: TimeInterval(recordedAt))
self.responseData = Interaction.dencodeBody(response["body"], headers: response["headers"] as? [String: String])
}
}
2 changes: 1 addition & 1 deletion DVR/Resources/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.3.0</string>
<string>0.4.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
Loading

0 comments on commit f042a92

Please sign in to comment.