Merge pull request #37 from mkoehnke/swift3

Swift3
This commit is contained in:
Mathias Köhnke 2016-09-11 12:00:02 +02:00 committed by GitHub
commit 8ed6434ce3
42 changed files with 676 additions and 632 deletions

View File

@ -1,6 +1,6 @@
language: objective-c
osx_image: xcode7.3
osx_image: xcode8
before_install:
- bash Scripts/update-carthage.sh 0.11
- bash Scripts/update-carthage.sh 0.16.2
script:
- Scripts/test-framework.sh

View File

@ -23,16 +23,18 @@
import Foundation
typealias FetchCompletion = (result : NSData?, response: NSURLResponse?, error: NSError?) -> Void
typealias FetchCompletion = (_ result : Data?, _ response: URLResponse?, _ error: Error?) -> Void
internal class ContentFetcher {
private let defaultSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
fileprivate let defaultSession = URLSession(configuration: URLSessionConfiguration.default)
func fetch(url: NSURL, completion: FetchCompletion) -> NSURLSessionTask? {
let request = NSURLRequest(URL: url)
let task = defaultSession.dataTaskWithRequest(request, completionHandler: { (data, urlResponse, error) in
completion(result: data, response: urlResponse, error: error)
@discardableResult
func fetch(_ url: URL, completion: @escaping FetchCompletion) -> URLSessionTask? {
let request = URLRequest(url: url)
let task = defaultSession.dataTask(with: request, completionHandler: { (data, urlResponse, error) -> Void in
completion(data, urlResponse, error)
})
task.resume()
return task

View File

@ -28,27 +28,27 @@ public enum SearchType<T: HTMLElement> {
/**
* Returns an element that matches the specified id.
*/
case Id(String)
case id(String)
/**
* Returns all elements matching the specified value for their name attribute.
*/
case Name(String)
case name(String)
/**
* Returns all elements with inner content, that contain the specified text.
*/
case Text(String)
case text(String)
/**
* Returns all elements that match the specified class name.
*/
case Class(String)
case `class`(String)
/**
Returns all elements that match the specified attribute name/value combination.
*/
case Attribute(String, String)
case attribute(String, String)
/**
Returns all elements with an attribute containing the specified value.
*/
case Contains(String, String)
case contains(String, String)
/**
Returns all elements that match the specified XPath query.
*/
@ -56,12 +56,12 @@ public enum SearchType<T: HTMLElement> {
func xPathQuery() -> String {
switch self {
case .Text(let value): return T.createXPathQuery("[contains(text(),'\(value)')]")
case .Id(let id): return T.createXPathQuery("[@id='\(id)']")
case .Name(let name): return T.createXPathQuery("[@name='\(name)']")
case .Attribute(let key, let value): return T.createXPathQuery("[@\(key)='\(value)']")
case .Class(let className): return T.createXPathQuery("[@class='\(className)']")
case .Contains(let key, let value): return T.createXPathQuery("[contains(@\(key), '\(value)')]")
case .text(let value): return T.createXPathQuery("[contains(text(),'\(value)')]")
case .id(let id): return T.createXPathQuery("[@id='\(id)']")
case .name(let name): return T.createXPathQuery("[@name='\(name)']")
case .attribute(let key, let value): return T.createXPathQuery("[@\(key)='\(value)']")
case .class(let className): return T.createXPathQuery("[@class='\(className)']")
case .contains(let key, let value): return T.createXPathQuery("[contains(@\(key), '\(value)')]")
case .XPathQuery(let query): return query
}
}
@ -72,23 +72,23 @@ public enum SearchType<T: HTMLElement> {
//========================================
public enum Result<T> {
case Success(T)
case Error(ActionError)
case success(T)
case error(ActionError)
init(_ error: ActionError?, _ value: T) {
if let err = error {
self = .Error(err)
self = .error(err)
} else {
self = .Success(value)
self = .success(value)
}
}
}
public extension Result where T:CollectionType {
public extension Result where T:Collection {
public func first<A>() -> Result<A> {
switch self {
case .Success(let result): return resultFromOptional(result.first as? A, error: .NotFound)
case .Error(let error): return resultFromOptional(nil, error: error)
case .success(let result): return resultFromOptional(result.first as? A, error: .notFound)
case .error(let error): return resultFromOptional(nil, error: error)
}
}
}
@ -96,10 +96,10 @@ public extension Result where T:CollectionType {
extension Result: CustomDebugStringConvertible {
public var debugDescription: String {
switch self {
case .Success(let value):
return "Success: \(String(value))"
case .Error(let error):
return "Error: \(String(error))"
case .success(let value):
return "Success: \(String(describing: value))"
case .error(let error):
return "Error: \(String(describing: error))"
}
}
}
@ -109,27 +109,27 @@ extension Result: CustomDebugStringConvertible {
//========================================
internal struct Response {
var data: NSData?
var data: Data?
var statusCode: Int = ActionError.Static.DefaultStatusCodeError
init(data: NSData?, urlResponse: NSURLResponse) {
init(data: Data?, urlResponse: URLResponse) {
self.data = data
if let httpResponse = urlResponse as? NSHTTPURLResponse {
if let httpResponse = urlResponse as? HTTPURLResponse {
self.statusCode = httpResponse.statusCode
}
}
init(data: NSData?, statusCode: Int) {
init(data: Data?, statusCode: Int) {
self.data = data
self.statusCode = statusCode
}
}
infix operator >>> { associativity left precedence 170 }
internal func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> {
infix operator >>>: AdditionPrecedence
internal func >>><A, B>(a: Result<A>, f: (A) -> Result<B>) -> Result<B> {
switch a {
case let .Success(x): return f(x)
case let .Error(error): return .Error(error)
case let .success(x): return f(x)
case let .error(error): return .error(error)
}
}
@ -142,7 +142,7 @@ internal func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> {
- returns: An Action.
*/
public func >>><T, U>(a: Action<T>, f: T -> Action<U>) -> Action<U> {
public func >>><T, U>(a: Action<T>, f: @escaping (T) -> Action<U>) -> Action<U> {
return a.andThen(f)
}
@ -156,7 +156,7 @@ public func >>><T, U>(a: Action<T>, f: T -> Action<U>) -> Action<U> {
- returns: An Action.
*/
public func >>><T, U>(a: Action<T>, b: Action<U>) -> Action<U> {
let f : (T -> Action<U>) = { _ in b }
let f : ((T) -> Action<U>) = { _ in b }
return a.andThen(f)
}
@ -179,7 +179,7 @@ public func >>><T, U: Page>(a: Action<T>, f: () -> Action<U>) -> Action<U> {
*Note:* This a workaround to remove the brackets of functions without any parameters (e.g. **inspect()**)
to provide a consistent API.
*/
public func >>><T:Page, U>(a: () -> Action<T>, f: T -> Action<U>) -> Action<U> {
public func >>><T:Page, U>(a: () -> Action<T>, f: @escaping (T) -> Action<U>) -> Action<U> {
return a() >>> f
}
@ -190,11 +190,11 @@ public func >>><T:Page, U>(a: () -> Action<T>, f: T -> Action<U>) -> Action<U> {
- parameter a: An Action.
- parameter completion: A Completion Block.
*/
public func ===<T>(a: Action<T>, completion: T? -> Void) {
public func ===<T>(a: Action<T>, completion: @escaping (T?) -> Void) {
return a.start { result in
switch result {
case .Success(let value): completion(value)
case .Error: completion(nil)
case .success(let value): completion(value)
case .error: completion(nil)
}
}
}
@ -206,36 +206,36 @@ public func ===<T>(a: Action<T>, completion: T? -> Void) {
- parameter a: An Action.
- parameter completion: An output function/closure.
*/
public func ===<T>(a: Action<T>, completion: Result<T> -> Void) {
public func ===<T>(a: Action<T>, completion: @escaping (Result<T>) -> Void) {
return a.start { result in
completion(result)
}
}
internal func parseResponse(response: Response) -> Result<NSData> {
internal func parseResponse(_ response: Response) -> Result<Data> {
let successRange = 200..<300
if !successRange.contains(response.statusCode) {
return .Error(.NetworkRequestFailure)
return .error(.networkRequestFailure)
}
return Result(nil, response.data ?? NSData())
return Result(nil, response.data ?? Data())
}
internal func resultFromOptional<A>(optional: A?, error: ActionError) -> Result<A> {
internal func resultFromOptional<A>(_ optional: A?, error: ActionError) -> Result<A> {
if let a = optional {
return .Success(a)
return .success(a)
} else {
return .Error(error)
return .error(error)
}
}
internal func decodeResult<T: Page>(url: NSURL? = nil) -> (data: NSData?) -> Result<T> {
return { (data: NSData?) -> Result<T> in
return resultFromOptional(T.pageWithData(data, url: url) as? T, error: .NetworkRequestFailure)
internal func decodeResult<T: Page>(_ url: URL? = nil) -> (_ data: Data?) -> Result<T> {
return { (data: Data?) -> Result<T> in
return resultFromOptional(T.pageWithData(data, url: url) as? T, error: .networkRequestFailure)
}
}
internal func decodeString(data: NSData?) -> Result<String> {
return resultFromOptional(data?.toString(), error: .TransformFailure)
internal func decodeString(_ data: Data?) -> Result<String> {
return resultFromOptional(data?.toString(), error: .transformFailure)
}
//========================================
@ -246,34 +246,34 @@ internal func decodeString(data: NSData?) -> Result<String> {
public struct Action<T> {
public typealias ResultType = Result<T>
public typealias Completion = ResultType -> ()
public typealias AsyncOperation = Completion -> ()
public typealias Completion = (ResultType) -> ()
public typealias AsyncOperation = (@escaping Completion) -> ()
private let operation: AsyncOperation
fileprivate let operation: AsyncOperation
public init(result: ResultType) {
self.init(operation: { completion in
dispatch_async(dispatch_get_main_queue(), {
DispatchQueue.main.async(execute: {
completion(result)
})
})
}
public init(value: T) {
self.init(result: .Success(value))
self.init(result: .success(value))
}
public init(error: ActionError) {
self.init(result: .Error(error))
self.init(result: .error(error))
}
public init(operation: AsyncOperation) {
public init(operation: @escaping AsyncOperation) {
self.operation = operation
}
public func start(completion: Completion) {
public func start(_ completion: @escaping Completion) {
self.operation() { result in
dispatch_async(dispatch_get_main_queue(), {
DispatchQueue.main.async(execute: {
completion(result)
})
}
@ -281,45 +281,45 @@ public struct Action<T> {
}
public extension Action {
public func map<U>(f: T -> U) -> Action<U> {
public func map<U>(_ f: @escaping (T) -> U) -> Action<U> {
return Action<U>(operation: { completion in
self.start { result in
dispatch_async(dispatch_get_main_queue(), {
DispatchQueue.main.async(execute: {
switch result {
case .Success(let value): completion(Result.Success(f(value)))
case .Error(let error): completion(Result.Error(error))
case .success(let value): completion(Result.success(f(value)))
case .error(let error): completion(Result.error(error))
}
})
}
})
}
public func flatMap<U>(f: T -> U?) -> Action<U> {
public func flatMap<U>(_ f: @escaping (T) -> U?) -> Action<U> {
return Action<U>(operation: { completion in
self.start { result in
dispatch_async(dispatch_get_main_queue(), {
DispatchQueue.main.async(execute: {
switch result {
case .Success(let value):
case .success(let value):
if let result = f(value) {
completion(Result.Success(result))
completion(Result.success(result))
} else {
completion(Result.Error(.TransformFailure))
completion(Result.error(.transformFailure))
}
case .Error(let error): completion(Result.Error(error))
case .error(let error): completion(Result.error(error))
}
})
}
})
}
public func andThen<U>(f: T -> Action<U>) -> Action<U> {
public func andThen<U>(_ f: @escaping (T) -> Action<U>) -> Action<U> {
return Action<U>(operation: { completion in
self.start { firstFutureResult in
switch firstFutureResult {
case .Success(let value): f(value).start(completion)
case .Error(let error):
dispatch_async(dispatch_get_main_queue(), {
completion(Result.Error(error))
case .success(let value): f(value).start(completion)
case .error(let error):
DispatchQueue.main.async(execute: {
completion(Result.error(error))
})
}
}
@ -344,24 +344,24 @@ public extension Action {
- returns: The collected Sction results.
*/
internal static func collect(initial: T, f: T -> Action<T>, until: T -> Bool) -> Action<[T]> {
internal static func collect(_ initial: T, f: @escaping (T) -> Action<T>, until: @escaping (T) -> Bool) -> Action<[T]> {
var values = [T]()
func loop(future: Action<T>) -> Action<[T]> {
func loop(_ future: Action<T>) -> Action<[T]> {
return Action<[T]>(operation: { completion in
future.start { result in
switch result {
case .Success(let newValue):
case .success(let newValue):
values.append(newValue)
if until(newValue) == true {
loop(f(newValue)).start(completion)
} else {
dispatch_async(dispatch_get_main_queue(), {
completion(Result.Success(values))
DispatchQueue.main.async(execute: {
completion(Result.success(values))
})
}
case .Error(let error):
dispatch_async(dispatch_get_main_queue(), {
completion(Result.Error(error))
case .error(let error):
DispatchQueue.main.async(execute: {
completion(Result.error(error))
})
}
}
@ -379,26 +379,26 @@ public extension Action {
- returns: The collected Action results.
*/
internal static func batch<U>(elements: [T], f: T -> Action<U>) -> Action<[U]> {
internal static func batch<U>(_ elements: [T], f: @escaping (T) -> Action<U>) -> Action<[U]> {
return Action<[U]>(operation: { completion in
let group = dispatch_group_create()
let group = DispatchGroup()
var results = [U]()
for element in elements {
dispatch_group_enter(group)
group.enter()
f(element).start({ result in
switch result {
case .Success(let value):
case .success(let value):
results.append(value)
dispatch_group_leave(group)
case .Error(let error):
dispatch_async(dispatch_get_main_queue(), {
completion(Result.Error(error))
group.leave()
case .error(let error):
DispatchQueue.main.async(execute: {
completion(Result.error(error))
})
}
})
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
completion(Result.Success(results))
group.notify(queue: DispatchQueue.main) {
completion(Result.success(results))
}
})
}
@ -420,16 +420,16 @@ public enum PostAction {
- returns: Time in Seconds.
*/
case Wait(NSTimeInterval)
case wait(TimeInterval)
/**
The action will complete if the specified JavaScript expression/script returns 'true'
or a timeout occurs.
- returns: Validation Script.
*/
case Validate(String)
case validate(String)
/// No Post Action will be performed.
case None
case none
}
@ -439,45 +439,45 @@ public enum PostAction {
// https://robots.thoughtbot.com/efficient-json-in-swift-with-functional-concepts-and-generics
//========================================
public typealias JSON = AnyObject
public typealias JSONElement = [String : AnyObject]
public typealias JSON = Any
public typealias JSONElement = [String : Any]
internal func parseJSON<U: JSON>(data: NSData) -> Result<U> {
internal func parseJSON<U: JSON>(_ data: Data) -> Result<U> {
var jsonOptional: U?
var __error = ActionError.ParsingFailure
var __error = ActionError.parsingFailure
do {
if let data = htmlToData(NSString(data: data, encoding: NSUTF8StringEncoding)) {
jsonOptional = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(rawValue: 0)) as? U
if let data = htmlToData(NSString(data: data, encoding: String.Encoding.utf8.rawValue)) {
jsonOptional = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0)) as? U
}
} catch _ as NSError {
__error = .ParsingFailure
} catch _ {
__error = .parsingFailure
jsonOptional = nil
}
return resultFromOptional(jsonOptional, error: __error)
}
internal func decodeJSON<U: JSONDecodable>(json: JSON?) -> Result<U> {
internal func decodeJSON<U: JSONDecodable>(_ json: JSON?) -> Result<U> {
if let element = json as? JSONElement {
return resultFromOptional(U.decode(element), error: .ParsingFailure)
return resultFromOptional(U.decode(element), error: .parsingFailure)
}
return Result.Error(.ParsingFailure)
return Result.error(.parsingFailure)
}
internal func decodeJSON<U: JSONDecodable>(json: JSON?) -> Result<[U]> {
internal func decodeJSON<U: JSONDecodable>(_ json: JSON?) -> Result<[U]> {
let result = [U]()
if let elements = json as? [JSONElement] {
var result = [U]()
for element in elements {
let decodable : Result<U> = decodeJSON(element)
let decodable : Result<U> = decodeJSON(element as JSON?)
switch decodable {
case .Success(let value): result.append(value)
case .Error(let error): return Result.Error(error)
case .success(let value): result.append(value)
case .error(let error): return Result.error(error)
}
}
}
return Result.Success(result)
return Result.success(result)
}
@ -487,54 +487,54 @@ internal func decodeJSON<U: JSONDecodable>(json: JSON?) -> Result<[U]> {
//========================================
private func htmlToData(html: NSString?) -> NSData? {
private func htmlToData(_ html: NSString?) -> Data? {
if let html = html {
let json = html.stringByReplacingOccurrencesOfString("<[^>]+>", withString: "", options: .RegularExpressionSearch, range: NSMakeRange(0, html.length))
return json.dataUsingEncoding(NSUTF8StringEncoding)
let json = html.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: NSMakeRange(0, html.length))
return json.data(using: String.Encoding.utf8)
}
return nil
}
extension Dictionary : JSONParsable {
public func content() -> JSON? {
return self as? JSON
return self
}
}
extension Array : JSONParsable {
public func content() -> JSON? {
return self as? JSON
return self
}
}
extension String {
internal func terminate() -> String {
let terminator : Character = ";"
var trimmed = stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
var trimmed = trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
if (trimmed.characters.last != terminator) { trimmed += String(terminator) }
return trimmed
}
}
extension NSData {
extension Data {
internal func toString() -> String? {
return String(data: self, encoding: NSUTF8StringEncoding)
return String(data: self, encoding: String.Encoding.utf8)
}
}
func dispatch_sync_on_main_thread(block: dispatch_block_t) {
if NSThread.isMainThread() {
func dispatch_sync_on_main_thread(_ block: ()->()) {
if Thread.isMainThread {
block()
} else {
dispatch_sync(dispatch_get_main_queue(), block)
DispatchQueue.main.sync(execute: block)
}
}
internal func delay(time: NSTimeInterval, completion: () -> Void) {
if let currentQueue = NSOperationQueue.currentQueue()?.underlyingQueue {
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(time * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, currentQueue) {
internal func delay(_ time: TimeInterval, completion: @escaping () -> Void) {
if let currentQueue = OperationQueue.current?.underlyingQueue {
let delayTime = DispatchTime.now() + Double(Int64(time * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
currentQueue.asyncAfter(deadline: delayTime) {
completion()
}
} else {

View File

@ -27,14 +27,12 @@ public protocol ErrorType { }
public enum NoError: ErrorType { }
extension NSError: ErrorType { }
public enum ActionError: ErrorType {
case NetworkRequestFailure
case NotFound
case ParsingFailure
case TransformFailure
case SnapshotFailure
case networkRequestFailure
case notFound
case parsingFailure
case transformFailure
case snapshotFailure
internal struct Static {
static let DefaultStatusCodeSuccess : Int = 200
@ -45,11 +43,11 @@ public enum ActionError: ErrorType {
extension ActionError: CustomDebugStringConvertible {
public var debugDescription: String {
switch self {
case .NetworkRequestFailure: return "Network Request Failure"
case .NotFound: return "Not Found"
case .ParsingFailure: return "Parsing Failure"
case .TransformFailure: return "Transform Failure"
case .SnapshotFailure: return "Snapshot Failure"
case .networkRequestFailure: return "Network Request Failure"
case .notFound: return "Not Found"
case .parsingFailure: return "Parsing Failure"
case .transformFailure: return "Transform Failure"
case .snapshotFailure: return "Snapshot Failure"
}
}
}

View File

@ -37,7 +37,7 @@ import Foundation
__using the shared WKZombie instance__.
- seealso: _open()_ function in _WKZombie_ class for more info.
*/
public func open<T: Page>(url: NSURL) -> Action<T> {
public func open<T: Page>(_ url: URL) -> Action<T> {
return WKZombie.sharedInstance.open(url)
}
@ -46,7 +46,7 @@ public func open<T: Page>(url: NSURL) -> Action<T> {
__using the shared WKZombie instance__.
- seealso: _open()_ function in _WKZombie_ class for more info.
*/
public func open<T: Page>(then postAction: PostAction) -> (url: NSURL) -> Action<T> {
public func open<T: Page>(then postAction: PostAction) -> (_ url: URL) -> Action<T> {
return WKZombie.sharedInstance.open(then: postAction)
}
@ -67,7 +67,7 @@ public func inspect<T: Page>() -> Action<T> {
Submits the specified HTML form __using the shared WKZombie instance__.
- seealso: _submit()_ function in _WKZombie_ class for more info.
*/
public func submit<T: Page>(form: HTMLForm) -> Action<T> {
public func submit<T: Page>(_ form: HTMLForm) -> Action<T> {
return WKZombie.sharedInstance.submit(form)
}
@ -75,7 +75,7 @@ public func submit<T: Page>(form: HTMLForm) -> Action<T> {
Submits the specified HTML form __using the shared WKZombie instance__.
- seealso: _submit()_ function in _WKZombie_ class for more info.
*/
public func submit<T: Page>(then postAction: PostAction) -> (form: HTMLForm) -> Action<T> {
public func submit<T: Page>(then postAction: PostAction) -> (_ form: HTMLForm) -> Action<T> {
return WKZombie.sharedInstance.submit(then: postAction)
}
@ -88,7 +88,7 @@ public func submit<T: Page>(then postAction: PostAction) -> (form: HTMLForm) ->
Simulates the click of a HTML link __using the shared WKZombie instance__.
- seealso: _click()_ function in _WKZombie_ class for more info.
*/
public func click<T: Page>(link : HTMLLink) -> Action<T> {
public func click<T: Page>(_ link : HTMLLink) -> Action<T> {
return WKZombie.sharedInstance.click(link)
}
@ -96,7 +96,7 @@ public func click<T: Page>(link : HTMLLink) -> Action<T> {
Simulates the click of a HTML link __using the shared WKZombie instance__.
- seealso: _click()_ function in _WKZombie_ class for more info.
*/
public func click<T: Page>(then postAction: PostAction) -> (link : HTMLLink) -> Action<T> {
public func click<T: Page>(then postAction: PostAction) -> (_ link : HTMLLink) -> Action<T> {
return WKZombie.sharedInstance.click(then: postAction)
}
@ -104,7 +104,7 @@ public func click<T: Page>(then postAction: PostAction) -> (link : HTMLLink) ->
Simulates HTMLButton press __using the shared WKZombie instance__.
- seealso: _press()_ function in _WKZombie_ class for more info.
*/
public func press<T: Page>(button : HTMLButton) -> Action<T> {
public func press<T: Page>(_ button : HTMLButton) -> Action<T> {
return WKZombie.sharedInstance.press(button)
}
@ -112,7 +112,7 @@ public func press<T: Page>(button : HTMLButton) -> Action<T> {
Simulates HTMLButton press __using the shared WKZombie instance__.
- seealso: _press()_ function in _WKZombie_ class for more info.
*/
public func press<T: Page>(then postAction: PostAction) -> (button : HTMLButton) -> Action<T> {
public func press<T: Page>(then postAction: PostAction) -> (_ button : HTMLButton) -> Action<T> {
return WKZombie.sharedInstance.press(then: postAction)
}
@ -124,7 +124,7 @@ public func press<T: Page>(then postAction: PostAction) -> (button : HTMLButton)
The returned WKZombie Action will swap the current page context with the context of an embedded iframe.
- seealso: _swap()_ function in _WKZombie_ class for more info.
*/
public func swap<T: Page>(iframe : HTMLFrame) -> Action<T> {
public func swap<T: Page>(_ iframe : HTMLFrame) -> Action<T> {
return WKZombie.sharedInstance.swap(iframe)
}
@ -132,7 +132,7 @@ public func swap<T: Page>(iframe : HTMLFrame) -> Action<T> {
The returned WKZombie Action will swap the current page context with the context of an embedded iframe.
- seealso: _swap()_ function in _WKZombie_ class for more info.
*/
public func swap<T: Page>(then postAction: PostAction) -> (iframe : HTMLFrame) -> Action<T> {
public func swap<T: Page>(then postAction: PostAction) -> (_ iframe : HTMLFrame) -> Action<T> {
return WKZombie.sharedInstance.swap(then: postAction)
}
@ -145,7 +145,7 @@ public func swap<T: Page>(then postAction: PostAction) -> (iframe : HTMLFrame) -
__using the shared WKZombie instance__.
- seealso: _setAttribute()_ function in _WKZombie_ class for more info.
*/
public func setAttribute<T: HTMLElement>(key: String, value: String?) -> (element: T) -> Action<HTMLPage> {
public func setAttribute<T: HTMLElement>(_ key: String, value: String?) -> (_ element: T) -> Action<HTMLPage> {
return WKZombie.sharedInstance.setAttribute(key, value: value)
}
@ -160,7 +160,7 @@ public func setAttribute<T: HTMLElement>(key: String, value: String?) -> (elemen
the passed key/value attributes. __The the shared WKZombie instance will be used__.
- seealso: _getAll()_ function in _WKZombie_ class for more info.
*/
public func getAll<T: HTMLElement>(by searchType: SearchType<T>) -> (page: HTMLPage) -> Action<[T]> {
public func getAll<T: HTMLElement>(by searchType: SearchType<T>) -> (_ page: HTMLPage) -> Action<[T]> {
return WKZombie.sharedInstance.getAll(by: searchType)
}
@ -169,7 +169,7 @@ public func getAll<T: HTMLElement>(by searchType: SearchType<T>) -> (page: HTMLP
the passed key/value attributes. __The shared WKZombie instance will be used__.
- seealso: _get()_ function in _WKZombie_ class for more info.
*/
public func get<T: HTMLElement>(by searchType: SearchType<T>) -> (page: HTMLPage) -> Action<T> {
public func get<T: HTMLElement>(by searchType: SearchType<T>) -> (_ page: HTMLPage) -> Action<T> {
return WKZombie.sharedInstance.get(by: searchType)
}
@ -183,7 +183,7 @@ public func get<T: HTMLElement>(by searchType: SearchType<T>) -> (page: HTMLPage
The returned WKZombie Action will execute a JavaScript string __using the shared WKZombie instance__.
- seealso: _execute()_ function in _WKZombie_ class for more info.
*/
public func execute(script: JavaScript) -> Action<JavaScriptResult> {
public func execute(_ script: JavaScript) -> Action<JavaScriptResult> {
return WKZombie.sharedInstance.execute(script)
}
@ -191,7 +191,7 @@ public func execute(script: JavaScript) -> Action<JavaScriptResult> {
The returned WKZombie Action will execute a JavaScript string __using the shared WKZombie instance__.
- seealso: _execute()_ function in _WKZombie_ class for more info.
*/
public func execute<T: HTMLPage>(script: JavaScript) -> (page: T) -> Action<JavaScriptResult> {
public func execute<T: HTMLPage>(_ script: JavaScript) -> (_ page: T) -> Action<JavaScriptResult> {
return WKZombie.sharedInstance.execute(script)
}
@ -206,7 +206,7 @@ public func execute<T: HTMLPage>(script: JavaScript) -> (page: T) -> Action<Java
__using the shared WKZombie instance__.
- seealso: _fetch()_ function in _WKZombie_ class for more info.
*/
public func fetch<T: HTMLFetchable>(fetchable: T) -> Action<T> {
public func fetch<T: HTMLFetchable>(_ fetchable: T) -> Action<T> {
return WKZombie.sharedInstance.fetch(fetchable)
}
@ -220,7 +220,7 @@ public func fetch<T: HTMLFetchable>(fetchable: T) -> Action<T> {
__The shared WKZombie instance will be used__.
- seealso: _map()_ function in _WKZombie_ class for more info.
*/
public func map<T, A>(f: T -> A) -> (object: T) -> Action<A> {
public func map<T, A>(_ f: @escaping (T) -> A) -> (_ object: T) -> Action<A> {
return WKZombie.sharedInstance.map(f)
}
@ -228,7 +228,7 @@ public func map<T, A>(f: T -> A) -> (object: T) -> Action<A> {
This function transforms an object into another object using the specified closure.
- seealso: _map()_ function in _WKZombie_ class for more info.
*/
public func map<T, A>(f: T -> A) -> (object: T) -> A {
public func map<T, A>(_ f: @escaping (T) -> A) -> (_ object: T) -> A {
return WKZombie.sharedInstance.map(f)
}
@ -243,7 +243,7 @@ public func map<T, A>(f: T -> A) -> (object: T) -> A {
__The shared WKZombie instance will be used__.
- seealso: _collect()_ function in _WKZombie_ class for more info.
*/
public func collect<T>(f: T -> Action<T>, until: T -> Bool) -> (initial: T) -> Action<[T]> {
public func collect<T>(_ f: @escaping (T) -> Action<T>, until: @escaping (T) -> Bool) -> (_ initial: T) -> Action<[T]> {
return WKZombie.sharedInstance.collect(f, until: until)
}
@ -253,7 +253,7 @@ public func collect<T>(f: T -> Action<T>, until: T -> Bool) -> (initial: T) -> A
__The shared WKZombie instance will be used__.
- seealso: _batch()_ function in _WKZombie_ class for more info.
*/
public func batch<T, U>(f: T -> Action<U>) -> (elements: [T]) -> Action<[U]> {
public func batch<T, U>(_ f: @escaping (T) -> Action<U>) -> (_ elements: [T]) -> Action<[U]> {
return WKZombie.sharedInstance.batch(f)
}
@ -268,7 +268,7 @@ public func batch<T, U>(f: T -> Action<U>) -> (elements: [T]) -> Action<[U]> {
__The shared WKZombie instance will be used__.
- seealso: _parse()_ function in _WKZombie_ class for more info.
*/
public func parse<T: JSON>(data: NSData) -> Action<T> {
public func parse<T: JSON>(_ data: Data) -> Action<T> {
return WKZombie.sharedInstance.parse(data)
}
@ -279,7 +279,7 @@ public func parse<T: JSON>(data: NSData) -> Action<T> {
__The shared WKZombie instance will be used__.
- seealso: _decode()_ function in _WKZombie_ class for more info.
*/
public func decode<T : JSONDecodable>(element: JSONParsable) -> Action<T> {
public func decode<T : JSONDecodable>(_ element: JSONParsable) -> Action<T> {
return WKZombie.sharedInstance.decode(element)
}
@ -290,7 +290,7 @@ public func decode<T : JSONDecodable>(element: JSONParsable) -> Action<T> {
__The shared WKZombie instance will be used__.
- seealso: _decode()_ function in _WKZombie_ class for more info.
*/
public func decode<T : JSONDecodable>(array: JSONParsable) -> Action<[T]> {
public func decode<T : JSONDecodable>(_ array: JSONParsable) -> Action<[T]> {
return WKZombie.sharedInstance.decode(array)
}
@ -305,13 +305,13 @@ public func decode<T : JSONDecodable>(array: JSONParsable) -> Action<[T]> {
This is a convenience operator for the _snap()_ command. It is equal to the __>>>__ operator with the difference
that a snapshot will be taken after the left Action has been finished.
*/
infix operator >>* { associativity left precedence 170 }
infix operator >>*: AdditionPrecedence
private func assertIfNotSharedInstance() {
assert(WKZombie.Static.instance != nil, "The >>* operator can only be used with the WKZombie shared instance.")
}
public func >>*<T, U>(a: Action<T>, f: T -> Action<U>) -> Action<U> {
public func >>*<T, U>(a: Action<T>, f: @escaping ((T) -> Action<U>)) -> Action<U> {
assertIfNotSharedInstance()
return a >>> snap >>> f
}
@ -326,7 +326,7 @@ public func decode<T : JSONDecodable>(array: JSONParsable) -> Action<[T]> {
return a >>> snap >>> f
}
public func >>*<T:Page, U>(a: () -> Action<T>, f: T -> Action<U>) -> Action<U> {
public func >>*<T:Page, U>(a: () -> Action<T>, f: @escaping ((T) -> Action<U>)) -> Action<U> {
assertIfNotSharedInstance()
return a >>> snap >>> f
}
@ -337,7 +337,7 @@ public func decode<T : JSONDecodable>(array: JSONParsable) -> Action<[T]> {
__The shared WKZombie instance will be used__.
- seealso: _snap()_ function in _WKZombie_ class for more info.
*/
public func snap<T>(element: T) -> Action<T> {
public func snap<T>(_ element: T) -> Action<T> {
return WKZombie.sharedInstance.snap(element)
}

View File

@ -9,14 +9,14 @@
import Foundation
/// HTML Button class, which represents the <button> element in the DOM.
public class HTMLButton : HTMLRedirectable {
open class HTMLButton : HTMLRedirectable {
//========================================
// MARK: Overrides
//========================================
internal override class func createXPathQuery(parameters: String) -> String {
internal override class func createXPathQuery(_ parameters: String) -> String {
return "//button\(parameters)"
}
}
}

View File

@ -25,16 +25,16 @@ import Foundation
/// The HTMLElement class is a base class, which can represent every element
/// in the DOM, e.g. <img>, <a>, <form> etc.
public class HTMLElement : HTMLParserElement {
open class HTMLElement : HTMLParserElement {
internal class func createXPathQuery(parameters: String) -> String {
internal class func createXPathQuery(_ parameters: String) -> String {
return "//*\(parameters)"
}
internal func createSetAttributeCommand(key : String, value: String?) -> String? {
internal func createSetAttributeCommand(_ key : String, value: String?) -> String? {
if let query = XPathQuery {
return "getElementByXpath(\"\(query)\").setAttribute(\"\(key)\", \"\(value ?? "")\");"
}
return nil
}
}
}

View File

@ -29,7 +29,7 @@ import ObjectiveC
//==========================================
public protocol HTMLFetchable : NSObjectProtocol {
var fetchURL : NSURL? { get }
var fetchURL : URL? { get }
func fetchedContent<T: HTMLFetchableContent>() -> T?
}
@ -40,9 +40,9 @@ private var WKZFetchedDataKey: UInt8 = 0
//==========================================
extension HTMLFetchable {
internal var fetchedData: NSData? {
internal var fetchedData: Data? {
get {
return objc_getAssociatedObject(self, &WKZFetchedDataKey) as? NSData
return objc_getAssociatedObject(self, &WKZFetchedDataKey) as? Data
}
set(newValue) {
objc_setAssociatedObject(self, &WKZFetchedDataKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
@ -52,8 +52,8 @@ extension HTMLFetchable {
public func fetchedContent<T : HTMLFetchableContent>() -> T? {
if let fetchedData = fetchedData {
switch T.instanceFromData(fetchedData) {
case .Success(let value): return value as? T
case .Error: return nil
case .success(let value): return value as? T
case .error: return nil
}
}
return nil
@ -67,7 +67,7 @@ extension HTMLFetchable {
public protocol HTMLFetchableContent {
associatedtype ContentType
static func instanceFromData(data: NSData) -> Result<ContentType>
static func instanceFromData(_ data: Data) -> Result<ContentType>
}
//==========================================
@ -78,29 +78,29 @@ public protocol HTMLFetchableContent {
import UIKit
extension UIImage : HTMLFetchableContent {
public typealias ContentType = UIImage
public static func instanceFromData(data: NSData) -> Result<ContentType> {
public static func instanceFromData(_ data: Data) -> Result<ContentType> {
if let image = UIImage(data: data) {
return Result.Success(image)
return Result.success(image)
}
return Result.Error(.TransformFailure)
return Result.error(.transformFailure)
}
}
#elseif os(OSX)
import Cocoa
extension NSImage : HTMLFetchableContent {
public typealias ContentType = NSImage
public static func instanceFromData(data: NSData) -> Result<ContentType> {
public static func instanceFromData(_ data: Data) -> Result<ContentType> {
if let image = NSImage(data: data) {
return Result.Success(image)
return Result.success(image)
}
return Result.Error(.TransformFailure)
return Result.error(.transformFailure)
}
}
#endif
extension NSData : HTMLFetchableContent {
public typealias ContentType = NSData
public static func instanceFromData(data: NSData) -> Result<ContentType> {
return Result.Success(data)
extension Data : HTMLFetchableContent {
public typealias ContentType = Data
public static func instanceFromData(_ data: Data) -> Result<ContentType> {
return Result.success(data)
}
}
}

View File

@ -24,10 +24,10 @@
import Foundation
/// HTML Form class, which represents the <form> element in the DOM.
public class HTMLForm : HTMLElement {
open class HTMLForm : HTMLElement {
/// All inputs fields (keys and values) of this form.
public private(set) var inputElements = [String : String]()
open fileprivate(set) var inputElements = [String : String]()
required public init?(element: AnyObject, XPathQuery: String? = nil) {
super.init(element: element, XPathQuery: XPathQuery)
@ -37,17 +37,17 @@ public class HTMLForm : HTMLElement {
}
/// Returns the value for the name attribute.
public var name : String? {
open var name : String? {
return objectForKey("name")
}
/// Returns the value for the id attribute.
public var id : String? {
open var id : String? {
return objectForKey("id")
}
/// Returns the value for the action attribute.
public var action : String? {
open var action : String? {
return objectForKey("action")
}
@ -58,7 +58,7 @@ public class HTMLForm : HTMLElement {
- returns: The Input field attribute value.
*/
public subscript(key: String) -> String? {
open subscript(key: String) -> String? {
return inputElements[key]
}
@ -79,7 +79,7 @@ public class HTMLForm : HTMLElement {
// MARK: Overrides
//========================================
internal override class func createXPathQuery(parameters: String) -> String {
internal override class func createXPathQuery(_ parameters: String) -> String {
return "//form\(parameters)"
}
@ -87,16 +87,16 @@ public class HTMLForm : HTMLElement {
// MARK: Private Methods
//========================================
private func retrieveAllInputs(element: HTMLElement) {
if let tagName = element.tagName as String? where tagName == "input" {
fileprivate func retrieveAllInputs(_ element: HTMLElement) {
if let tagName = element.tagName as String? , tagName == "input" {
if let name = element.objectForKey("name") {
inputElements[name] = element.objectForKey("value")
}
}
if let children = element.children() as [HTMLElement]? where children.count > 0 {
if let children = element.children() as [HTMLElement]? , children.count > 0 {
for child in children {
retrieveAllInputs(child)
}
}
}
}
}

View File

@ -24,10 +24,10 @@
import Foundation
/// HTML iframe Class, which represents the <iframe> element in the DOM.
public class HTMLFrame : HTMLRedirectable {
open class HTMLFrame : HTMLRedirectable {
/// Returns the value of the src attribute of the iframe.
public var source : String? {
open var source : String? {
return objectForKey("src")
}
@ -46,7 +46,7 @@ public class HTMLFrame : HTMLRedirectable {
// MARK: Overrides
//========================================
internal override class func createXPathQuery(parameters: String) -> String {
internal override class func createXPathQuery(_ parameters: String) -> String {
return "//iframe\(parameters)"
}
}
}

View File

@ -24,7 +24,7 @@
import Foundation
/// HTML Image class, which represents the <img> element in the DOM.
public class HTMLImage : HTMLElement, HTMLFetchable {
open class HTMLImage : HTMLElement, HTMLFetchable {
//========================================
// MARK: Initializer
@ -35,7 +35,7 @@ public class HTMLImage : HTMLElement, HTMLFetchable {
}
/// Returns the value of the src attribute of the image.
public var source : String? {
open var source : String? {
return objectForKey("src")
}
@ -43,9 +43,9 @@ public class HTMLImage : HTMLElement, HTMLFetchable {
// MARK: HTMLFetchable Protocol
//========================================
public var fetchURL : NSURL? {
open var fetchURL : URL? {
if let source = source {
return NSURL(string: source)
return URL(string: source)
}
return nil
}
@ -54,7 +54,7 @@ public class HTMLImage : HTMLElement, HTMLFetchable {
// MARK: Overrides
//========================================
internal override class func createXPathQuery(parameters: String) -> String {
internal override class func createXPathQuery(_ parameters: String) -> String {
return "//img\(parameters)"
}
}

View File

@ -24,19 +24,19 @@
import Foundation
/// HTML Link class, which represents the <a> element in the DOM.
public class HTMLLink : HTMLRedirectable, HTMLFetchable {
open class HTMLLink : HTMLRedirectable, HTMLFetchable {
/// Returns the value of the href attribute of the link.
public var href : String? {
open var href : String? {
return text
}
/// Returns the link text.
public var linkText : String? {
open var linkText : String? {
return content
}
override public var description : String {
override open var description : String {
return href ?? ""
}
@ -57,9 +57,9 @@ public class HTMLLink : HTMLRedirectable, HTMLFetchable {
// MARK: HTMLFetchable Protocol
//========================================
public var fetchURL : NSURL? {
open var fetchURL : URL? {
if let href = objectForKey("href") {
return NSURL(string: href)
return URL(string: href)
}
return nil
}
@ -68,10 +68,10 @@ public class HTMLLink : HTMLRedirectable, HTMLFetchable {
// MARK: Overrides
//========================================
internal override class func createXPathQuery(parameters: String) -> String {
internal override class func createXPathQuery(_ parameters: String) -> String {
return "//a\(parameters)/@href"
}
}

View File

@ -24,7 +24,7 @@
import Foundation
/// HTMLPage class, which represents the DOM of a HTML page.
public class HTMLPage : HTMLParser, Page {
open class HTMLPage : HTMLParser, Page {
//========================================
// MARK: Initializer
@ -38,7 +38,7 @@ public class HTMLPage : HTMLParser, Page {
- returns: A HTML page.
*/
public static func pageWithData(data: NSData?, url: NSURL?) -> Page? {
open static func pageWithData(_ data: Data?, url: URL?) -> Page? {
if let data = data {
return HTMLPage(data: data, url: url)
}
@ -49,11 +49,11 @@ public class HTMLPage : HTMLParser, Page {
// MARK: Find Elements
//========================================
public func findElements<T: HTMLElement>(searchType: SearchType<T>) -> Result<[T]> {
open func findElements<T: HTMLElement>(_ searchType: SearchType<T>) -> Result<[T]> {
let query = searchType.xPathQuery()
if let parsedObjects = searchWithXPathQuery(query) where parsedObjects.count > 0 {
return resultFromOptional(parsedObjects.flatMap { T(element: $0, XPathQuery: query) }, error: .NotFound)
if let parsedObjects = searchWithXPathQuery(query) , parsedObjects.count > 0 {
return resultFromOptional(parsedObjects.flatMap { T(element: $0, XPathQuery: query) }, error: .notFound)
}
return Result.Error(.NotFound)
return Result.error(.notFound)
}
}
}

View File

@ -9,7 +9,7 @@
import Foundation
/// Base class for redirectable HTML elements (e.g. HTMLLink, HTMLButton).
public class HTMLRedirectable : HTMLElement {
open class HTMLRedirectable : HTMLElement {
//========================================
// MARK: Initializer
@ -29,4 +29,4 @@ public class HTMLRedirectable : HTMLElement {
}
return nil
}
}
}

View File

@ -24,10 +24,10 @@
import Foundation
/// HTML Table class, which represents the <table> element in the DOM.
public class HTMLTable : HTMLElement {
open class HTMLTable : HTMLElement {
/// Returns all row elements within this table
public var rows : [HTMLTableRow]? {
open var rows : [HTMLTableRow]? {
let rows : [HTMLTableRow]? = children()
return (rows?.first?.tagName == "tbody") ? rows?.first?.children() : rows
}
@ -36,17 +36,17 @@ public class HTMLTable : HTMLElement {
// MARK: Overrides
//========================================
internal override class func createXPathQuery(parameters: String) -> String {
internal override class func createXPathQuery(_ parameters: String) -> String {
return "//table\(parameters)"
}
}
/// HTML Table Row Class, which represents the <tr> element in the DOM.
public class HTMLTableRow : HTMLElement {
open class HTMLTableRow : HTMLElement {
/// Returns all columns within this row.
public var columns : [HTMLTableColumn]? {
open var columns : [HTMLTableColumn]? {
return children()
}
@ -54,19 +54,19 @@ public class HTMLTableRow : HTMLElement {
// MARK: Overrides
//========================================
internal override class func createXPathQuery(parameters: String) -> String {
internal override class func createXPathQuery(_ parameters: String) -> String {
return "//tr\(parameters)"
}
}
/// HTML Table Column class, which represents the <td> element in the DOM.
public class HTMLTableColumn : HTMLElement {
open class HTMLTableColumn : HTMLElement {
//========================================
// MARK: Overrides
//========================================
internal override class func createXPathQuery(parameters: String) -> String {
internal override class func createXPathQuery(_ parameters: String) -> String {
return "//td\(parameters)"
}
}

View File

@ -34,7 +34,7 @@ public protocol JSONDecodable {
- returns: The model object.
*/
static func decode(json: JSONElement) -> Self?
static func decode(_ json: JSONElement) -> Self?
}
/**
@ -51,7 +51,7 @@ public protocol JSONParsable {
/// JSONPage class, which represents the entire JSON document.
public class JSONPage : JSONParser, Page, JSONParsable {
open class JSONPage : JSONParser, Page, JSONParsable {
/**
Returns a JSON page instance for the specified JSON data.
@ -61,10 +61,10 @@ public class JSONPage : JSONParser, Page, JSONParsable {
- returns: A JSON page.
*/
public static func pageWithData(data: NSData?, url: NSURL?) -> Page? {
open static func pageWithData(_ data: Data?, url: URL?) -> Page? {
if let data = data {
return JSONPage(data: data, url: url)
}
return nil
}
}
}

View File

@ -24,11 +24,11 @@
import Foundation
/// WKZombie Console Logger
public class Logger : NSObject {
open class Logger : NSObject {
public static var enabled : Bool = true
open static var enabled : Bool = true
public class func log(message: String, lineBreak: Bool = true) {
open class func log(_ message: String, lineBreak: Bool = true) {
if enabled {
if lineBreak {
print("\(message)")

View File

@ -35,7 +35,7 @@ public protocol Page {
- returns: A HTML or JSON page.
*/
static func pageWithData(data: NSData?, url: NSURL?) -> Page?
static func pageWithData(_ data: Data?, url: URL?) -> Page?
}

View File

@ -25,10 +25,10 @@ import Foundation
import hpple
/// Base class for the HTMLParser and JSONParser.
public class Parser : NSObject {
open class Parser : NSObject {
/// The URL of the page.
public private(set) var url : NSURL?
open fileprivate(set) var url : URL?
/**
Returns a (HTML or JSON) parser instance for the specified data.
@ -38,7 +38,7 @@ public class Parser : NSObject {
- returns: A HTML or JSON page.
*/
required public init(data: NSData, url: NSURL? = nil) {
required public init(data: Data, url: URL? = nil) {
super.init()
self.url = url
}
@ -50,32 +50,32 @@ public class Parser : NSObject {
//========================================
/// A HTML Parser class, which wraps the functionality of the TFHpple class.
public class HTMLParser : Parser {
open class HTMLParser : Parser {
private var doc : TFHpple?
fileprivate var doc : TFHpple?
required public init(data: NSData, url: NSURL? = nil) {
required public init(data: Data, url: URL? = nil) {
super.init(data: data, url: url)
self.doc = TFHpple(HTMLData: data)
self.doc = TFHpple(htmlData: data)
}
public func searchWithXPathQuery(xPathOrCSS: String) -> [AnyObject]? {
return doc?.searchWithXPathQuery(xPathOrCSS)
open func searchWithXPathQuery(_ xPathOrCSS: String) -> [AnyObject]? {
return doc?.search(withXPathQuery: xPathOrCSS) as [AnyObject]?
}
public var data: NSData? {
open var data: Data? {
return doc?.data
}
override public var description : String {
return (NSString(data: doc?.data ?? NSData(), encoding: NSUTF8StringEncoding) ?? "") as String
override open var description : String {
return (NSString(data: doc?.data ?? Data(), encoding: String.Encoding.utf8.rawValue) ?? "") as String
}
}
/// A HTML Parser Element class, which wraps the functionality of the TFHppleElement class.
public class HTMLParserElement : NSObject {
private var element : TFHppleElement?
public internal(set) var XPathQuery : String?
open class HTMLParserElement : NSObject {
fileprivate var element : TFHppleElement?
open internal(set) var XPathQuery : String?
required public init?(element: AnyObject, XPathQuery : String? = nil) {
super.init()
@ -87,39 +87,39 @@ public class HTMLParserElement : NSObject {
}
}
public var innerContent : String? {
open var innerContent : String? {
return element?.raw as String?
}
public var text : String? {
open var text : String? {
return element?.text() as String?
}
public var content : String? {
open var content : String? {
return element?.content as String?
}
public var tagName : String? {
open var tagName : String? {
return element?.tagName as String?
}
public func objectForKey(key: String) -> String? {
return element?.objectForKey(key.lowercaseString) as String?
open func objectForKey(_ key: String) -> String? {
return element?.object(forKey: key.lowercased()) as String?
}
public func childrenWithTagName<T: HTMLElement>(tagName: String) -> [T]? {
return element?.childrenWithTagName(tagName).flatMap { T(element: $0) }
open func childrenWithTagName<T: HTMLElement>(_ tagName: String) -> [T]? {
return element?.children(withTagName: tagName).flatMap { T(element: $0 as AnyObject) }
}
public func children<T: HTMLElement>() -> [T]? {
return element?.children.flatMap { T(element:$0) }
open func children<T: HTMLElement>() -> [T]? {
return element?.children.flatMap { T(element:$0 as AnyObject) }
}
public func hasChildren() -> Bool {
open func hasChildren() -> Bool {
return element?.hasChildren() ?? false
}
override public var description : String {
override open var description : String {
return element?.raw ?? ""
}
}
@ -130,24 +130,24 @@ public class HTMLParserElement : NSObject {
//========================================
/// A JSON Parser class, which represents a JSON document.
public class JSONParser : Parser {
open class JSONParser : Parser {
private var json : JSON?
fileprivate var json : JSON?
required public init(data: NSData, url: NSURL? = nil) {
required public init(data: Data, url: URL? = nil) {
super.init(data: data, url: url)
let result : Result<JSON> = parseJSON(data)
switch result {
case .Success(let json): self.json = json
case .Error: Logger.log("Error parsing JSON!")
case .success(let json): self.json = json
case .error: Logger.log("Error parsing JSON!")
}
}
public func content() -> JSON? {
open func content() -> JSON? {
return json
}
override public var description : String {
override open var description : String {
return "\(json)"
}
}

View File

@ -28,97 +28,97 @@ import WebKit
// MARK: RenderOperation
//========================================
internal class RenderOperation : NSOperation {
internal class RenderOperation : Operation {
typealias RequestBlock = (operation: RenderOperation) -> Void
typealias RequestBlock = (_ operation: RenderOperation) -> Void
private(set) weak var webView : WKWebView?
private var timeout : NSTimer?
private let timeoutInSeconds : NSTimeInterval
private var stopRunLoop : Bool = false
fileprivate(set) weak var webView : WKWebView?
fileprivate var timeout : Timer?
fileprivate let timeoutInSeconds : TimeInterval
fileprivate var stopRunLoop : Bool = false
var loadMediaContent : Bool = true
var requestBlock : RequestBlock?
var postAction: PostAction = .None
var postAction: PostAction = .none
internal private(set) var result : NSData?
internal private(set) var response : NSURLResponse?
internal private(set) var error : NSError?
internal fileprivate(set) var result : Data?
internal fileprivate(set) var response : URLResponse?
internal fileprivate(set) var error : Error?
private var _executing: Bool = false
override var executing: Bool {
fileprivate var _executing: Bool = false
override var isExecuting: Bool {
get {
return _executing
}
set {
if _executing != newValue {
willChangeValueForKey("isExecuting")
willChangeValue(forKey: "isExecuting")
_executing = newValue
didChangeValueForKey("isExecuting")
didChangeValue(forKey: "isExecuting")
}
}
}
private var _finished: Bool = false;
override var finished: Bool {
fileprivate var _finished: Bool = false;
override var isFinished: Bool {
get {
return _finished
}
set {
if _finished != newValue {
willChangeValueForKey("isFinished")
willChangeValue(forKey: "isFinished")
_finished = newValue
didChangeValueForKey("isFinished")
didChangeValue(forKey: "isFinished")
}
}
}
init(webView: WKWebView, timeoutInSeconds : NSTimeInterval = 30.0) {
init(webView: WKWebView, timeoutInSeconds : TimeInterval = 30.0) {
self.timeoutInSeconds = timeoutInSeconds
super.init()
self.webView = webView
}
override func start() {
if self.cancelled {
if self.isCancelled {
return
} else {
Logger.log("\(name ?? String())")
Logger.log("[", lineBreak: false)
executing = true
isExecuting = true
startTimeout()
// Wait for WKWebView to finish loading before starting the operation.
wait { [unowned self] in self.webView?.loading == false ?? true }
wait { [unowned self] in self.webView?.isLoading == false }
setupReferences()
requestBlock?(operation: self)
requestBlock?(self)
// Loading
wait { [unowned self] in self.stopRunLoop }
}
}
func wait(condition: () -> Bool) {
let updateInterval : NSTimeInterval = 0.1
var loopUntil = NSDate(timeIntervalSinceNow: updateInterval)
while condition() == false && NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: loopUntil) {
loopUntil = NSDate(timeIntervalSinceNow: updateInterval)
func wait(_ condition: () -> Bool) {
let updateInterval : TimeInterval = 0.1
var loopUntil = Date(timeIntervalSinceNow: updateInterval)
while condition() == false && RunLoop.current.run(mode: RunLoopMode.defaultRunLoopMode, before: loopUntil) {
loopUntil = Date(timeIntervalSinceNow: updateInterval)
Logger.log(".", lineBreak: false)
}
}
func completeRendering(webView: WKWebView?, result: NSData? = nil, error: NSError? = nil) {
func completeRendering(_ webView: WKWebView?, result: Data? = nil, error: Error? = nil) {
stopTimeout()
if executing == true && finished == false {
if isExecuting == true && isFinished == false {
self.result = result ?? self.result
self.error = error ?? self.error
cleanupReferences()
executing = false
finished = true
isExecuting = false
isFinished = true
Logger.log("]\n")
}
@ -129,32 +129,32 @@ internal class RenderOperation : NSOperation {
super.cancel()
stopTimeout()
cleanupReferences()
executing = false
finished = true
isExecuting = false
isFinished = true
}
// MARK: Helper Methods
private func startTimeout() {
fileprivate func startTimeout() {
stopRunLoop = false
timeout = NSTimer(timeInterval: timeoutInSeconds, target: self, selector: #selector(RenderOperation.cancel), userInfo: nil, repeats: false)
NSRunLoop.currentRunLoop().addTimer(timeout!, forMode: NSDefaultRunLoopMode)
timeout = Timer(timeInterval: timeoutInSeconds, target: self, selector: #selector(RenderOperation.cancel), userInfo: nil, repeats: false)
RunLoop.current.add(timeout!, forMode: RunLoopMode.defaultRunLoopMode)
}
private func stopTimeout() {
fileprivate func stopTimeout() {
timeout?.invalidate()
timeout = nil
stopRunLoop = true
}
private func setupReferences() {
webView?.configuration.userContentController.addScriptMessageHandler(self, name: "doneLoading")
fileprivate func setupReferences() {
webView?.configuration.userContentController.add(self, name: "doneLoading")
webView?.navigationDelegate = self
}
private func cleanupReferences() {
fileprivate func cleanupReferences() {
webView?.navigationDelegate = nil
webView?.configuration.userContentController.removeScriptMessageHandlerForName("doneLoading")
webView?.configuration.userContentController.removeScriptMessageHandler(forName: "doneLoading")
webView = nil
}
}
@ -165,15 +165,15 @@ internal class RenderOperation : NSOperation {
extension RenderOperation : WKScriptMessageHandler {
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
//None of the content loaded after this point is necessary (images, videos, etc.)
if let webView = message.webView {
if message.name == "doneLoading" && loadMediaContent == false {
if let url = webView.URL where response == nil {
response = NSHTTPURLResponse(URL: url, statusCode: 200, HTTPVersion: nil, headerFields: nil)
if let url = webView.url , response == nil {
response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)
}
webView.stopLoading()
self.webView(webView, didFinishNavigation: nil)
self.webView(webView, didFinish: nil)
}
}
}
@ -185,13 +185,13 @@ extension RenderOperation : WKScriptMessageHandler {
extension RenderOperation : WKNavigationDelegate {
func webView(webView: WKWebView, decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void) {
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
response = navigationResponse.response
decisionHandler(.Allow)
decisionHandler(.allow)
}
func webView(webView: WKWebView, didFailNavigation navigation: WKNavigation!, withError error: NSError) {
if let response = response as? NSHTTPURLResponse, _ = completionBlock {
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
if let response = response as? HTTPURLResponse, let _ = completionBlock {
let successRange = 200..<300
if !successRange.contains(response.statusCode) {
self.error = error
@ -200,10 +200,10 @@ extension RenderOperation : WKNavigationDelegate {
}
}
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
switch postAction {
case .Wait, .Validate: handlePostAction(postAction, webView: webView)
case .None: finishedLoading(webView)
case .wait, .validate: handlePostAction(postAction, webView: webView)
case .none: finishedLoading(webView)
}
}
@ -215,17 +215,17 @@ extension RenderOperation : WKNavigationDelegate {
extension RenderOperation {
func finishedLoading(webView: WKWebView) {
func finishedLoading(_ webView: WKWebView) {
webView.evaluateJavaScript("\(Renderer.scrapingCommand);") { [weak self] result, error in
self?.result = result?.dataUsingEncoding(NSUTF8StringEncoding)
self?.result = (result as? String)?.data(using: String.Encoding.utf8)
self?.completeRendering(webView)
}
}
func validate(condition: String, webView: WKWebView) {
if finished == false && cancelled == false {
func validate(_ condition: String, webView: WKWebView) {
if isFinished == false && isCancelled == false {
webView.evaluateJavaScript(condition) { [weak self] result, error in
if let result = result as? Bool where result == true {
if let result = result as? Bool , result == true {
self?.finishedLoading(webView)
} else {
delay(0.5, completion: {
@ -236,19 +236,19 @@ extension RenderOperation {
}
}
func waitAndFinish(time: NSTimeInterval, webView: WKWebView) {
func waitAndFinish(_ time: TimeInterval, webView: WKWebView) {
delay(time) {
self.finishedLoading(webView)
}
}
func handlePostAction(postAction: PostAction, webView: WKWebView) {
func handlePostAction(_ postAction: PostAction, webView: WKWebView) {
switch postAction {
case .Validate(let script): validate(script, webView: webView)
case .Wait(let time): waitAndFinish(time, webView: webView)
case .validate(let script): validate(script, webView: webView)
case .wait(let time): waitAndFinish(time, webView: webView)
default: Logger.log("Something went wrong!")
}
self.postAction = .None
self.postAction = .none
}
}

View File

@ -25,7 +25,7 @@ import Foundation
import WebKit
typealias RenderCompletion = (result : AnyObject?, response: NSURLResponse?, error: NSError?) -> Void
typealias RenderCompletion = (_ result : Any?, _ response: URLResponse?, _ error: Error?) -> Void
internal class Renderer : NSObject {
@ -40,27 +40,27 @@ internal class Renderer : NSObject {
}
}
var timeoutInSeconds : NSTimeInterval = 30.0
var timeoutInSeconds : TimeInterval = 30.0
private var renderQueue : NSOperationQueue = {
let instance = NSOperationQueue()
fileprivate var renderQueue : OperationQueue = {
let instance = OperationQueue()
instance.maxConcurrentOperationCount = 1
instance.qualityOfService = .UserInitiated
instance.qualityOfService = .userInitiated
return instance
}()
private var webView : WKWebView!
fileprivate var webView : WKWebView!
internal static let scrapingCommand = "document.documentElement.outerHTML"
init(processPool: WKProcessPool? = nil) {
super.init()
let doneLoadingWithoutMediaContentScript = "window.webkit.messageHandlers.doneLoading.postMessage(\(Renderer.scrapingCommand));"
let doneLoadingUserScript = WKUserScript(source: doneLoadingWithoutMediaContentScript, injectionTime: .AtDocumentEnd, forMainFrameOnly: true)
let doneLoadingUserScript = WKUserScript(source: doneLoadingWithoutMediaContentScript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
let getElementByXPathScript = "function getElementByXpath(path) { " +
" return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; " +
"}"
let getElementUserScript = WKUserScript(source: getElementByXPathScript, injectionTime: .AtDocumentEnd, forMainFrameOnly: false)
let getElementUserScript = WKUserScript(source: getElementByXPathScript, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
let contentController = WKUserContentController()
contentController.addUserScript(doneLoadingUserScript)
@ -80,18 +80,18 @@ internal class Renderer : NSObject {
dispatch_sync_on_main_thread {
let warning = "The keyWindow or contentView is missing."
#if os(iOS)
let bounds = UIScreen.mainScreen().bounds
let bounds = UIScreen.main.bounds
self.webView = WKWebView(frame: bounds, configuration: config)
if let window = UIApplication.sharedApplication().keyWindow {
if let window = UIApplication.shared.keyWindow {
self.webView.alpha = 0.01
window.insertSubview(self.webView, atIndex: 0)
window.insertSubview(self.webView, at: 0)
} else {
Logger.log(warning)
}
#elseif os(OSX)
self.webView = WKWebView(frame: CGRectZero, configuration: config)
if let window = NSApplication.sharedApplication().keyWindow, view = window.contentView {
self.webView.frame = CGRect(origin: CGPointZero, size: view.frame.size)
self.webView = WKWebView(frame: CGRect.zero, configuration: config)
if let window = NSApplication.shared().keyWindow, let view = window.contentView {
self.webView.frame = CGRect(origin: CGPoint.zero, size: view.frame.size)
self.webView.alphaValue = 0.01
view.addSubview(self.webView)
} else {
@ -111,16 +111,16 @@ internal class Renderer : NSObject {
// MARK: Render Page
//========================================
internal func renderPageWithRequest(request: NSURLRequest, postAction: PostAction = .None, completionHandler: RenderCompletion) {
let requestBlock : (operation: RenderOperation) -> Void = { operation in
if let url = request.URL where url.fileURL {
operation.webView?.loadFileURL(url, allowingReadAccessToURL: url.URLByDeletingLastPathComponent ?? url)
internal func renderPageWithRequest(_ request: URLRequest, postAction: PostAction = .none, completionHandler: @escaping RenderCompletion) {
let requestBlock : (_ operation: RenderOperation) -> Void = { operation in
if let url = request.url , url.isFileURL {
_ = operation.webView?.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
} else {
operation.webView?.loadRequest(request)
_ = operation.webView?.load(request)
}
}
let operation = operationWithRequestBlock(requestBlock, postAction: postAction, completionHandler: completionHandler)
operation.name = "Request".uppercaseString + "\n\(request.URL?.absoluteString ?? String())"
operation.name = "Request".uppercased() + "\n\(request.url?.absoluteString ?? String())"
renderQueue.addOperation(operation)
}
@ -129,23 +129,23 @@ internal class Renderer : NSObject {
// MARK: Execute JavaScript
//========================================
internal func executeScript(script: String, willLoadPage: Bool? = false, postAction: PostAction = .None, completionHandler: RenderCompletion?) {
var requestBlock : (operation : RenderOperation) -> Void
if let willLoadPage = willLoadPage where willLoadPage == true {
internal func executeScript(_ script: String, willLoadPage: Bool? = false, postAction: PostAction = .none, completionHandler: RenderCompletion?) {
var requestBlock : (_ operation : RenderOperation) -> Void
if let willLoadPage = willLoadPage , willLoadPage == true {
requestBlock = { $0.webView?.evaluateJavaScript(script, completionHandler: nil) }
} else {
requestBlock = { operation in
operation.webView?.evaluateJavaScript(script, completionHandler: { result, error in
var data : NSData?
var data : Data?
if let result = result {
data = "\(result)".dataUsingEncoding(NSUTF8StringEncoding)
data = "\(result)".data(using: String.Encoding.utf8)
}
operation.completeRendering(operation.webView, result: data, error: error)
})
}
}
let operation = operationWithRequestBlock(requestBlock, postAction: postAction, completionHandler: completionHandler)
operation.name = "Script".uppercaseString + "\n\(script ?? String())"
operation.name = "Script".uppercased() + "\n\(script )"
renderQueue.addOperation(operation)
}
@ -153,24 +153,24 @@ internal class Renderer : NSObject {
// MARK: Helper Methods
//========================================
private func operationWithRequestBlock(requestBlock: (operation: RenderOperation) -> Void, postAction: PostAction = .None, completionHandler: RenderCompletion?) -> NSOperation {
fileprivate func operationWithRequestBlock(_ requestBlock: @escaping (_ operation: RenderOperation) -> Void, postAction: PostAction = .none, completionHandler: RenderCompletion?) -> Operation {
let operation = RenderOperation(webView: webView, timeoutInSeconds: timeoutInSeconds)
operation.loadMediaContent = loadMediaContent
operation.postAction = postAction
operation.completionBlock = { [weak operation] in
completionHandler?(result: operation?.result, response: operation?.response, error: operation?.error)
completionHandler?(operation?.result, operation?.response, operation?.error)
}
operation.requestBlock = requestBlock
return operation
}
internal func currentContent(completionHandler: RenderCompletion) {
internal func currentContent(_ completionHandler: @escaping RenderCompletion) {
webView.evaluateJavaScript(Renderer.scrapingCommand.terminate()) { result, error in
var data : NSData?
var data : Data?
if let result = result {
data = "\(result)".dataUsingEncoding(NSUTF8StringEncoding)
data = "\(result)".data(using: String.Encoding.utf8)
}
completionHandler(result: data, response: nil, error: error)
completionHandler(data as AnyObject?, nil, error as Error?)
}
}
@ -183,10 +183,10 @@ internal class Renderer : NSObject {
extension Renderer {
internal func clearCache() {
let distantPast = NSDate.distantPast()
NSHTTPCookieStorage.sharedHTTPCookieStorage().removeCookiesSinceDate(distantPast)
let distantPast = Date.distantPast
HTTPCookieStorage.shared.removeCookies(since: distantPast)
let websiteDataTypes = Set([WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache])
WKWebsiteDataStore.defaultDataStore().removeDataOfTypes(websiteDataTypes, modifiedSince: distantPast, completionHandler:{ })
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: distantPast, completionHandler:{ })
}
}
@ -200,12 +200,12 @@ extension Renderer {
internal func snapshot() -> Snapshot? {
precondition(webView.superview != nil, "WKWebView has no superview. Cannot take snapshot.")
UIGraphicsBeginImageContextWithOptions(webView.bounds.size, true, 0)
webView.scrollView.drawViewHierarchyInRect(webView.bounds, afterScreenUpdates: true)
webView.scrollView.drawHierarchy(in: webView.bounds, afterScreenUpdates: false)
let snapshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
if let data = UIImagePNGRepresentation(snapshot) {
return Snapshot(data: data, page: webView.URL)
if let data = UIImagePNGRepresentation(snapshot!) {
return Snapshot(data: data, page: webView.url)
}
return nil
}

View File

@ -29,24 +29,22 @@ import Cocoa
public typealias SnapshotImage = NSImage
#endif
public typealias SnapshotHandler = Snapshot -> Void
public typealias SnapshotHandler = (Snapshot) -> Void
/// WKZombie Snapshot Helper Class
public class Snapshot {
public let page : NSURL?
public let file : NSURL
public lazy var image : SnapshotImage? = {
if let path = self.file.path {
#if os(iOS)
return UIImage(contentsOfFile: path)
#elseif os(OSX)
return NSImage(contentsOfFile: path)
#endif
}
return nil
open class Snapshot {
open let page : URL?
open let file : URL
open lazy var image : SnapshotImage? = {
let path = self.file.path
#if os(iOS)
return UIImage(contentsOfFile: path)
#elseif os(OSX)
return NSImage(contentsOfFile: path)
#endif
}()
internal init?(data: NSData, page: NSURL? = nil) {
internal init?(data: Data, page: URL? = nil) {
do {
self.file = try Snapshot.store(data)
self.page = page
@ -56,13 +54,13 @@ public class Snapshot {
}
}
private static func store(data: NSData) throws -> NSURL {
let identifier = NSProcessInfo.processInfo().globallyUniqueString
fileprivate static func store(_ data: Data) throws -> URL {
let identifier = ProcessInfo.processInfo.globallyUniqueString
let fileName = String(format: "wkzombie-snapshot-%@", identifier)
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(fileName)
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
try data.writeToURL(fileURL, options: .AtomicWrite)
try data.write(to: fileURL, options: .atomicWrite)
return fileURL
}
@ -76,13 +74,11 @@ public class Snapshot {
- returns: The URL with the new file location.
*/
public func moveTo(directory: NSURL) throws -> NSURL? {
let fileManager = NSFileManager.defaultManager()
if let fileName = file.lastPathComponent {
let destination = directory.URLByAppendingPathComponent(fileName)
try fileManager.moveItemAtURL(file, toURL: destination)
return destination
}
return nil
open func moveTo(_ directory: URL) throws -> URL? {
let fileManager = FileManager.default
let fileName = file.lastPathComponent
let destination = directory.appendingPathComponent(fileName)
try fileManager.moveItem(at: file, to: destination)
return destination
}
}

View File

@ -24,37 +24,39 @@
import Foundation
import WebKit
public class WKZombie : NSObject {
open class WKZombie : NSObject {
private static var __once: () = { Static.instance = WKZombie() }()
/// A shared instance of `Manager`, used by top-level WKZombie methods,
/// and suitable for multiple web sessions.
public class var sharedInstance: WKZombie {
dispatch_once(&Static.token) { Static.instance = WKZombie() }
open class var sharedInstance: WKZombie {
_ = WKZombie.__once
return Static.instance!
}
internal struct Static {
static var token : dispatch_once_t = 0
static var token : Int = 0
static var instance : WKZombie?
}
private var _renderer : Renderer!
private var _fetcher : ContentFetcher!
fileprivate var _renderer : Renderer!
fileprivate var _fetcher : ContentFetcher!
/// Returns the name of this WKZombie session.
public private(set) var name : String!
open fileprivate(set) var name : String!
/// If false, the loading progress will finish once the 'raw' HTML data
/// has been transmitted. Media content such as videos or images won't
/// be loaded.
public var loadMediaContent : Bool = true {
open var loadMediaContent : Bool = true {
didSet {
_renderer.loadMediaContent = loadMediaContent
}
}
/// The custom user agent string or nil if no custom user agent string has been set.
public var userAgent : String? {
open var userAgent : String? {
get {
return self._renderer.userAgent
}
@ -65,7 +67,7 @@ public class WKZombie : NSObject {
/// An operation is cancelled if the time it needs to complete exceeds the time
/// specified by this property. Default is 30 seconds.
public var timeoutInSeconds : NSTimeInterval {
open var timeoutInSeconds : TimeInterval {
get {
return self._renderer.timeoutInSeconds
}
@ -76,7 +78,7 @@ public class WKZombie : NSObject {
#if os(iOS)
/// Snapshot Handler
public var snapshotHandler : SnapshotHandler?
open var snapshotHandler : SnapshotHandler?
#endif
/**
@ -97,12 +99,12 @@ public class WKZombie : NSObject {
// MARK: Response Handling
//========================================
private func _handleResponse(data: NSData?, response: NSURLResponse?, error: NSError?) -> Result<NSData> {
fileprivate func _handleResponse(_ data: Data?, response: URLResponse?, error: Error?) -> Result<Data> {
var statusCode : Int = (error == nil) ? ActionError.Static.DefaultStatusCodeSuccess : ActionError.Static.DefaultStatusCodeError
if let response = response as? NSHTTPURLResponse {
if let response = response as? HTTPURLResponse {
statusCode = response.statusCode
}
let errorDomain : ActionError? = (error == nil) ? nil : .NetworkRequestFailure
let errorDomain : ActionError? = (error == nil) ? nil : .networkRequestFailure
let responseResult: Result<Response> = Result(errorDomain, Response(data: data, statusCode: statusCode))
return responseResult >>> parseResponse
}
@ -111,16 +113,16 @@ public class WKZombie : NSObject {
// MARK: HTMLRedirectable Handling
//========================================
private func redirect<T: Page, U: HTMLRedirectable>(then postAction: PostAction = .None) -> (redirectable : U) -> Action<T> {
fileprivate func redirect<T: Page, U: HTMLRedirectable>(then postAction: PostAction = .none) -> (_ redirectable : U) -> Action<T> {
return { (redirectable: U) -> Action<T> in
return Action() { [unowned self] completion in
if let script = redirectable.actionScript() {
self._renderer.executeScript(script, willLoadPage: true, postAction: postAction, completionHandler: { result, response, error in
let data = self._handleResponse(result as? NSData, response: response, error: error)
completion(data >>> decodeResult(response?.URL))
let data = self._handleResponse(result as? Data, response: response, error: error)
completion(data >>> decodeResult(response?.url))
})
} else {
completion(Result.Error(.NetworkRequestFailure))
completion(Result.error(.networkRequestFailure))
}
}
}
@ -140,8 +142,8 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func open<T: Page>(url: NSURL) -> Action<T> {
return open(then: .None)(url: url)
public func open<T: Page>(_ url: URL) -> Action<T> {
return open(then: .none)(url)
}
/**
@ -152,13 +154,13 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func open<T: Page>(then postAction: PostAction) -> (url: NSURL) -> Action<T> {
return { (url: NSURL) -> Action<T> in
public func open<T: Page>(then postAction: PostAction) -> (_ url: URL) -> Action<T> {
return { (url: URL) -> Action<T> in
return Action() { [unowned self] completion in
let request = NSURLRequest(URL: url)
let request = URLRequest(url: url)
self._renderer.renderPageWithRequest(request, postAction: postAction, completionHandler: { data, response, error in
let data = self._handleResponse(data as? NSData, response: response, error: error)
completion(data >>> decodeResult(response?.URL))
let data = self._handleResponse(data as? Data, response: response, error: error)
completion(data >>> decodeResult(response?.url))
})
}
}
@ -172,8 +174,8 @@ extension WKZombie {
public func inspect<T: Page>() -> Action<T> {
return Action() { [unowned self] completion in
self._renderer.currentContent({ (result, response, error) in
let data = self._handleResponse(result as? NSData, response: response, error: error)
completion(data >>> decodeResult(response?.URL))
let data = self._handleResponse(result as? Data, response: response, error: error)
completion(data >>> decodeResult(response?.url))
})
}
}
@ -192,8 +194,8 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func submit<T: Page>(form: HTMLForm) -> Action<T> {
return submit(then: .None)(form: form)
public func submit<T: Page>(_ form: HTMLForm) -> Action<T> {
return submit(then: .none)(form)
}
/**
@ -204,16 +206,16 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func submit<T: Page>(then postAction: PostAction) -> (form: HTMLForm) -> Action<T> {
public func submit<T: Page>(then postAction: PostAction) -> (_ form: HTMLForm) -> Action<T> {
return { (form: HTMLForm) -> Action<T> in
return Action() { [unowned self] completion in
if let script = form.actionScript() {
self._renderer.executeScript(script, willLoadPage: true, postAction: postAction, completionHandler: { result, response, error in
let data = self._handleResponse(result as? NSData, response: response, error: error)
completion(data >>> decodeResult(response?.URL))
let data = self._handleResponse(result as? Data, response: response, error: error)
completion(data >>> decodeResult(response?.url))
})
} else {
completion(Result.Error(.NetworkRequestFailure))
completion(Result.error(.networkRequestFailure))
}
}
}
@ -233,8 +235,8 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func click<T: Page>(link : HTMLLink) -> Action<T> {
return click(then: .None)(link: link)
public func click<T: Page>(_ link : HTMLLink) -> Action<T> {
return click(then: .none)(link)
}
/**
@ -245,9 +247,9 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func click<T: Page>(then postAction: PostAction) -> (link : HTMLLink) -> Action<T> {
public func click<T: Page>(then postAction: PostAction) -> (_ link : HTMLLink) -> Action<T> {
return { [unowned self] (link: HTMLLink) -> Action<T> in
return self.redirect(then: postAction)(redirectable: link)
return self.redirect(then: postAction)(link)
}
}
@ -258,8 +260,8 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func press<T: Page>(button : HTMLButton) -> Action<T> {
return press(then: .None)(button: button)
public func press<T: Page>(_ button : HTMLButton) -> Action<T> {
return press(then: .none)(button)
}
/**
@ -270,9 +272,9 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func press<T: Page>(then postAction: PostAction) -> (button : HTMLButton) -> Action<T> {
public func press<T: Page>(then postAction: PostAction) -> (_ button : HTMLButton) -> Action<T> {
return { [unowned self] (button: HTMLButton) -> Action<T> in
return self.redirect(then: postAction)(redirectable: button)
return self.redirect(then: postAction)(button)
}
}
}
@ -289,8 +291,8 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func swap<T: Page>(iframe : HTMLFrame) -> Action<T> {
return swap(then: .None)(iframe: iframe)
public func swap<T: Page>(_ iframe : HTMLFrame) -> Action<T> {
return swap(then: .none)(iframe)
}
/**
@ -301,9 +303,9 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func swap<T: Page>(then postAction: PostAction) -> (iframe : HTMLFrame) -> Action<T> {
public func swap<T: Page>(then postAction: PostAction) -> (_ iframe : HTMLFrame) -> Action<T> {
return { [unowned self] (iframe: HTMLFrame) -> Action<T> in
return self.redirect(then: postAction)(redirectable: iframe)
return self.redirect(then: postAction)(iframe)
}
}
}
@ -323,15 +325,15 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func setAttribute<T: HTMLElement>(key: String, value: String?) -> (element: T) -> Action<HTMLPage> {
public func setAttribute<T: HTMLElement>(_ key: String, value: String?) -> (_ element: T) -> Action<HTMLPage> {
return { (element: T) -> Action<HTMLPage> in
return Action() { [unowned self] completion in
if let script = element.createSetAttributeCommand(key, value: value) {
self._renderer.executeScript("\(script) \(Renderer.scrapingCommand.terminate())", completionHandler: { result, response, error in
completion(decodeResult(nil)(data: result as? NSData))
completion(decodeResult(nil)(result as? Data))
})
} else {
completion(Result.Error(.NetworkRequestFailure))
completion(Result.error(.networkRequestFailure))
}
}
}
@ -353,7 +355,7 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func getAll<T: HTMLElement>(by searchType: SearchType<T>) -> (page: HTMLPage) -> Action<[T]> {
public func getAll<T: HTMLElement>(by searchType: SearchType<T>) -> (_ page: HTMLPage) -> Action<[T]> {
return { (page: HTMLPage) -> Action<[T]> in
let elements : Result<[T]> = page.findElements(searchType)
return Action(result: elements)
@ -369,7 +371,7 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func get<T: HTMLElement>(by searchType: SearchType<T>) -> (page: HTMLPage) -> Action<T> {
public func get<T: HTMLElement>(by searchType: SearchType<T>) -> (_ page: HTMLPage) -> Action<T> {
return { (page: HTMLPage) -> Action<T> in
let elements : Result<[T]> = page.findElements(searchType)
return Action(result: elements.first())
@ -393,12 +395,12 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func execute(script: JavaScript) -> Action<JavaScriptResult> {
public func execute(_ script: JavaScript) -> Action<JavaScriptResult> {
return Action() { [unowned self] completion in
self._renderer.executeScript(script, completionHandler: { result, response, error in
let data = self._handleResponse(result as? NSData, response: response, error: error)
let data = self._handleResponse(result as? Data, response: response, error: error)
let output = data >>> decodeString
Logger.log("Script Result".uppercaseString + "\n\(output)\n")
Logger.log("Script Result".uppercased() + "\n\(output)\n")
completion(output)
})
}
@ -412,7 +414,7 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func execute<T: HTMLPage>(script: JavaScript) -> (page : T) -> Action<JavaScriptResult> {
public func execute<T: HTMLPage>(_ script: JavaScript) -> (_ page : T) -> Action<JavaScriptResult> {
return { [unowned self] (page : T) -> Action<JavaScriptResult> in
return self.execute(script)
}
@ -432,21 +434,21 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func fetch<T: HTMLFetchable>(fetchable: T) -> Action<T> {
public func fetch<T: HTMLFetchable>(_ fetchable: T) -> Action<T> {
return Action() { [unowned self] completion in
if let fetchURL = fetchable.fetchURL {
self._fetcher.fetch(fetchURL, completion: { (result, response, error) in
let data = self._handleResponse(result, response: response, error: error)
switch data {
case .Success(let value): fetchable.fetchedData = value
case .Error(let error):
completion(Result.Error(error))
case .success(let value): fetchable.fetchedData = value
case .error(let error):
completion(Result.error(error))
return
}
completion(Result.Success(fetchable))
completion(Result.success(fetchable))
})
} else {
completion(Result.Error(.NotFound))
completion(Result.error(.notFound))
}
}
}
@ -466,9 +468,9 @@ extension WKZombie {
- returns: The WKZombie Action.
*/
public func map<T, A>(f: T -> A) -> (object: T) -> Action<A> {
public func map<T, A>(_ f: @escaping (T) -> A) -> (_ object: T) -> Action<A> {
return { (object: T) -> Action<A> in
return Action(result: resultFromOptional(f(object), error: .NotFound))
return Action(result: resultFromOptional(f(object), error: .notFound))
}
}
@ -480,7 +482,7 @@ extension WKZombie {
- returns: The transformed object.
*/
public func map<T, A>(f: T -> A) -> (object: T) -> A {
public func map<T, A>(_ f: @escaping (T) -> A) -> (_ object: T) -> A {
return { (object: T) -> A in
return f(object)
}
@ -502,7 +504,7 @@ extension WKZombie {
- returns: The collected Sction results.
*/
public func collect<T>(f: T -> Action<T>, until: T -> Bool) -> (initial: T) -> Action<[T]> {
public func collect<T>(_ f: @escaping (T) -> Action<T>, until: @escaping (T) -> Bool) -> (_ initial: T) -> Action<[T]> {
return { (initial: T) -> Action<[T]> in
return Action.collect(initial, f: f, until: until)
}
@ -517,7 +519,7 @@ extension WKZombie {
- returns: The collected Action results.
*/
public func batch<T, U>(f: T -> Action<U>) -> (elements: [T]) -> Action<[U]> {
public func batch<T, U>(_ f: @escaping (T) -> Action<U>) -> (_ elements: [T]) -> Action<[U]> {
return { (elements: [T]) -> Action<[U]> in
return Action.batch(elements, f: f)
}
@ -537,7 +539,7 @@ extension WKZombie {
- returns: A JSON object.
*/
public func parse<T: JSON>(data: NSData) -> Action<T> {
public func parse<T: JSON>(_ data: Data) -> Action<T> {
return Action(result: parseJSON(data))
}
@ -550,7 +552,7 @@ extension WKZombie {
- returns: A JSONDecodable object.
*/
public func decode<T : JSONDecodable>(element: JSONParsable) -> Action<T> {
public func decode<T : JSONDecodable>(_ element: JSONParsable) -> Action<T> {
return Action(result: decodeJSON(element.content()))
}
@ -563,7 +565,7 @@ extension WKZombie {
- returns: A JSONDecodable array.
*/
public func decode<T : JSONDecodable>(array: JSONParsable) -> Action<[T]> {
public func decode<T : JSONDecodable>(_ array: JSONParsable) -> Action<[T]> {
return Action(result: decodeJSON(array.content()))
}
@ -586,14 +588,14 @@ extension WKZombie {
- returns: A snapshot class.
*/
public func snap<T>(element: T) -> Action<T> {
public func snap<T>(_ element: T) -> Action<T> {
return Action<T>(operation: { [unowned self] completion in
delay(DefaultSnapshotDelay, completion: {
if let snapshotHandler = self.snapshotHandler, snapshot = self._renderer.snapshot() {
if let snapshotHandler = self.snapshotHandler, let snapshot = self._renderer.snapshot() {
snapshotHandler(snapshot)
completion(Result.Success(element))
completion(Result.success(element))
} else {
completion(Result.Error(.SnapshotFailure))
completion(Result.error(.snapshotFailure))
}
})
})
@ -612,7 +614,7 @@ extension WKZombie {
*/
public func dump() {
_renderer.currentContent { (result, response, error) in
if let output = (result as? NSData)?.toString() {
if let output = (result as? Data)?.toString() {
Logger.log(output)
} else {
Logger.log("No Output available.")

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15D21" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="XfG-lQ-9wD">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11201"/>
</dependencies>
<scenes>
<!--Application-->
@ -642,7 +642,7 @@
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
</connections>
</application>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="WKZombieDemo" customModuleProvider="target"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Example_OSX" customModuleProvider="target"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="0.0"/>
@ -668,7 +668,7 @@
<!--Developer Portal-->
<scene sceneID="hIz-AP-VOD">
<objects>
<viewController title="Developer Portal" id="XfG-lQ-9wD" customClass="ViewController" customModule="WKZombieDemo" customModuleProvider="target" sceneMemberID="viewController">
<viewController title="Developer Portal" id="XfG-lQ-9wD" customClass="ViewController" customModule="Example_OSX" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="m2S-Jp-Qdl">
<rect key="frame" x="0.0" y="0.0" width="640" height="480"/>
<autoresizingMask key="autoresizingMask"/>

View File

@ -29,15 +29,15 @@ class ViewController: NSViewController {
@IBOutlet weak var imageView : NSImageView!
@IBOutlet weak var activityIndicator : NSProgressIndicator!
let url = NSURL(string: "https://github.com/logos")!
let url = URL(string: "https://github.com/logos")!
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator.startAnimation(nil)
getTopTrendingEntry(url)
getTopTrendingEntry(url: url)
}
func getTopTrendingEntry(url: NSURL) {
func getTopTrendingEntry(url: URL) {
open(url)
>>> get(by: .XPathQuery("//img[contains(@class, 'gh-octocat')]"))
>>> fetch

View File

@ -29,30 +29,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

View File

@ -23,14 +23,14 @@
import UIKit
public class Button : UIButton {
open class Button : UIButton {
override public var enabled: Bool {
override open var isEnabled: Bool {
didSet {
if enabled == true {
if isEnabled == true {
backgroundColor = UIColor(red: 0.0/255.9, green: 122.0/255.0, blue: 255.0/255.0, alpha: 1.0)
} else {
backgroundColor = .darkGrayColor()
backgroundColor = .darkGray
}
}
}

View File

@ -31,8 +31,8 @@ class LoginViewController : UIViewController {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var loginButton : UIButton!
private let url = NSURL(string: "https://developer.apple.com/membercenter/index.action")!
private var snapshots = [Snapshot]()
fileprivate let url = URL(string: "https://developer.apple.com/membercenter/index.action")!
fileprivate var snapshots = [Snapshot]()
override func viewDidLoad() {
super.viewDidLoad()
@ -42,9 +42,9 @@ class LoginViewController : UIViewController {
}
}
@IBAction func loginButtonTouched(button: UIButton) {
guard let user = nameTextField.text, password = passwordTextField.text else { return }
button.enabled = false
@IBAction func loginButtonTouched(_ button: UIButton) {
guard let user = nameTextField.text, let password = passwordTextField.text else { return }
button.isEnabled = false
snapshots.removeAll()
activityIndicator.startAnimating()
getProvisioningProfiles(url, user: user, password: password)
@ -54,17 +54,17 @@ class LoginViewController : UIViewController {
// MARK: HTML Navigation
//========================================
func getProvisioningProfiles(url: NSURL, user: String, password: String) {
func getProvisioningProfiles(_ url: URL, user: String, password: String) {
open(url)
>>* get(by: .Id("accountname"))
>>* get(by: .id("accountname"))
>>> setAttribute("value", value: user)
>>* get(by: .Id("accountpassword"))
>>* get(by: .id("accountpassword"))
>>> setAttribute("value", value: password)
>>* get(by: .Name("form2"))
>>> submit(then: .Wait(2.0))
>>* get(by: .Contains("href", "/account/"))
>>> click(then: .Wait(2.5))
>>* getAll(by: .Contains("class", "row-"))
>>* get(by: .name("form2"))
>>> submit(then: .wait(2.0))
>>* get(by: .contains("href", "/account/"))
>>> click(then: .wait(2.5))
>>* getAll(by: .contains("class", "row-"))
=== handleResult
}
@ -72,21 +72,21 @@ class LoginViewController : UIViewController {
// MARK: Handle Result
//========================================
func handleResult(result: Result<[HTMLTableRow]>) {
func handleResult(_ result: Result<[HTMLTableRow]>) {
switch result {
case .Success(let value): self.outputResult(value)
case .Error(let error): self.handleError(error)
case .success(let value): self.outputResult(value)
case .error(let error): self.handleError(error)
}
}
func outputResult(rows: [HTMLTableRow]) {
func outputResult(_ rows: [HTMLTableRow]) {
let columns = rows.flatMap { $0.columns?.first }
performSegueWithIdentifier("detailSegue", sender: columns)
performSegue(withIdentifier: "detailSegue", sender: columns)
}
func handleError(error: ActionError) {
func handleError(_ error: ActionError) {
print("Error loading page: \(error)")
loginButton.enabled = true
loginButton.isEnabled = true
activityIndicator.stopAnimating()
dump()
@ -97,9 +97,9 @@ class LoginViewController : UIViewController {
// MARK: Segue
//========================================
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSegue" {
if let vc = segue.destinationViewController as? ProfileViewController, items = sender as? [HTMLTableColumn] {
if let vc = segue.destination as? ProfileViewController, let items = sender as? [HTMLTableColumn] {
vc.items = items
vc.snapshots = snapshots
}

View File

@ -34,20 +34,20 @@ class ProfileViewController: UITableViewController {
navigationItem.hidesBackButton = true
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items?.count ?? 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let item = items?[indexPath.row].children()?.first as HTMLElement?
cell.textLabel?.text = item?.text
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "snapshotSegue" {
let vc = segue.destinationViewController as? SnapshotViewController
let vc = segue.destination as? SnapshotViewController
vc?.snapshots = snapshots
}
}

View File

@ -19,30 +19,29 @@ class SnapshotViewController: UICollectionViewController {
var snapshots : [Snapshot]?
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return snapshots?.count ?? 0
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(SnapshotCell.cellIdentifier, forIndexPath: indexPath)
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SnapshotCell.cellIdentifier, for: indexPath)
if let cell = cell as? SnapshotCell {
cell.imageView.image = snapshots?[indexPath.row].image
}
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
}
extension SnapshotViewController : UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = (view.bounds.size.width / 2) - 1
let height = (view.bounds.size.height * width) / view.bounds.size.width
return CGSize(width: width, height: height)
}
}
extension SnapshotViewController : UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1.0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 2.0
}
}
}

View File

@ -153,12 +153,12 @@
isa = PBXNativeTarget;
buildConfigurationList = BF01D1551CB6DF400095BCE4 /* Build configuration list for PBXNativeTarget "Example iOS" */;
buildPhases = (
7F0DCE50C224DEA28E978AA5 /* 📦 Check Pods Manifest.lock */,
7F0DCE50C224DEA28E978AA5 /* [CP] Check Pods Manifest.lock */,
BF01D13F1CB6DF400095BCE4 /* Sources */,
BF01D1401CB6DF400095BCE4 /* Frameworks */,
BF01D1411CB6DF400095BCE4 /* Resources */,
BE397A7E64678C9CCD379BF7 /* 📦 Embed Pods Frameworks */,
CA49EA506B324CD052DC997F /* 📦 Copy Pods Resources */,
BE397A7E64678C9CCD379BF7 /* [CP] Embed Pods Frameworks */,
CA49EA506B324CD052DC997F /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -173,12 +173,12 @@
isa = PBXNativeTarget;
buildConfigurationList = BF01D1681CB6DF690095BCE4 /* Build configuration list for PBXNativeTarget "Example OSX" */;
buildPhases = (
B2EA9311BFC6F98696DCF43F /* 📦 Check Pods Manifest.lock */,
B2EA9311BFC6F98696DCF43F /* [CP] Check Pods Manifest.lock */,
BF01D1581CB6DF690095BCE4 /* Sources */,
BF01D1591CB6DF690095BCE4 /* Frameworks */,
BF01D15A1CB6DF690095BCE4 /* Resources */,
74B172EE250EEE4E3EB42085 /* 📦 Embed Pods Frameworks */,
2D8A74D67D9E30AC4A02CD87 /* 📦 Copy Pods Resources */,
74B172EE250EEE4E3EB42085 /* [CP] Embed Pods Frameworks */,
2D8A74D67D9E30AC4A02CD87 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -196,11 +196,12 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0730;
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = "Mathias Köhnke";
TargetAttributes = {
BF01D1421CB6DF400095BCE4 = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0800;
};
BF01D15B1CB6DF690095BCE4 = {
CreatedOnToolsVersion = 7.3;
@ -249,14 +250,14 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
2D8A74D67D9E30AC4A02CD87 /* 📦 Copy Pods Resources */ = {
2D8A74D67D9E30AC4A02CD87 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Copy Pods Resources";
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -264,14 +265,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example OSX/Pods-Example OSX-resources.sh\"\n";
showEnvVarsInLog = 0;
};
74B172EE250EEE4E3EB42085 /* 📦 Embed Pods Frameworks */ = {
74B172EE250EEE4E3EB42085 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Embed Pods Frameworks";
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -279,14 +280,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example OSX/Pods-Example OSX-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
7F0DCE50C224DEA28E978AA5 /* 📦 Check Pods Manifest.lock */ = {
7F0DCE50C224DEA28E978AA5 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Check Pods Manifest.lock";
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -294,14 +295,14 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
B2EA9311BFC6F98696DCF43F /* 📦 Check Pods Manifest.lock */ = {
B2EA9311BFC6F98696DCF43F /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Check Pods Manifest.lock";
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -309,14 +310,14 @@
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
BE397A7E64678C9CCD379BF7 /* 📦 Embed Pods Frameworks */ = {
BE397A7E64678C9CCD379BF7 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Embed Pods Frameworks";
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -324,14 +325,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example iOS/Pods-Example iOS-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
CA49EA506B324CD052DC997F /* 📦 Copy Pods Resources */ = {
CA49EA506B324CD052DC997F /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "📦 Copy Pods Resources";
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@ -407,8 +408,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -454,8 +457,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -475,6 +480,7 @@
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
};
name = Release;
@ -483,6 +489,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 103C458DB709116CF3A71CAA /* Pods-Example iOS.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = "Example iOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -490,6 +497,7 @@
PRODUCT_BUNDLE_IDENTIFIER = de.mathiaskoehnke.Example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Example-Bridging-Header.h";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
@ -497,6 +505,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 8B4DABDA781BE033312A356A /* Pods-Example iOS.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = "Example iOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -504,6 +513,7 @@
PRODUCT_BUNDLE_IDENTIFIER = de.mathiaskoehnke.Example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Example-Bridging-Header.h";
SWIFT_VERSION = 3.0;
};
name = Release;
};
@ -511,6 +521,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 6611650B8D155042CC566BB5 /* Pods-Example OSX.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
@ -520,6 +531,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "de.mathiaskoehnke.Example-OSX";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_VERSION = 3.0;
};
name = Debug;
};
@ -527,6 +539,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = E524A5A77547899C72AB259A /* Pods-Example OSX.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
@ -536,6 +549,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "de.mathiaskoehnke.Example-OSX";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_VERSION = 3.0;
};
name = Release;
};

View File

@ -17,3 +17,10 @@ target 'Example OSX' do
import_pods
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '3.0'
end
end
end

View File

@ -1,6 +1,6 @@
PODS:
- hpple (0.2.0)
- WKZombie (0.9.4):
- WKZombie (0.9.5):
- hpple (= 0.2.0)
DEPENDENCIES:
@ -12,8 +12,8 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
hpple: 3b765f96fc2cd56ad1a49aef6f7be5cb2aa64b57
WKZombie: 18d87bc1929a53f0ec70d4d859f9e69871d03634
WKZombie: b6192e2e334ec68e3069ed5622d369f72ea43d09
PODFILE CHECKSUM: d45dbdf0b486677d4fa53af69a6fdd24430a4ac5
PODFILE CHECKSUM: d530690e373e4c270897769971faa34622df5672
COCOAPODS: 1.0.0
COCOAPODS: 1.0.1

View File

@ -8,7 +8,7 @@
[<img align="left" src="https://raw.githubusercontent.com/mkoehnke/WKZombie/develop/Resources/Logo.png" hspace="30" width="140px">](#logo)
WKZombie is an **iOS/OSX web-browser without a graphical user interface**. It was developed as an experiment in order to familiarize myself with **using functional concepts** written in Swift (>= 2.2).
WKZombie is an **iOS/OSX web-browser without a graphical user interface**. It was developed as an experiment in order to familiarize myself with **using functional concepts** written in **Swift 3** _(Swift 2.2 for versions < 1.0)_.
It incorporates [WebKit](https://webkit.org) (WKWebView) for rendering and [hpple](https://github.com/topfunky/hpple) (libxml2) for parsing the HTML content. In addition, it can take snapshots and has rudimentary support for parsing/decoding [JSON elements](#json-elements). **Chaining asynchronous actions makes the code compact and easy to use.**
@ -83,15 +83,15 @@ The following snippet demonstrates how you would use WKZombie to **collect all P
```ruby
open(url)
>>* get(by: .Id("accountname"))
>>* get(by: .id("accountname"))
>>> setAttribute("value", value: user)
>>* get(by: .Id("accountpassword"))
>>* get(by: .id("accountpassword"))
>>> setAttribute("value", value: password)
>>* get(by: .Name("form2"))
>>* get(by: .name("form2"))
>>> submit
>>* get(by: .Contains("href", "/account/"))
>>> click(then: .Wait(2.5))
>>* getAll(by: .Contains("class", "row-"))
>>* get(by: .contains("href", "/account/"))
>>> click(then: .wait(2.5))
>>* getAll(by: .contains("class", "row-"))
=== myOutput
```
@ -108,8 +108,8 @@ or
```ruby
func myOutput(result: Result<[HTMLTableColumn]>) {
switch result {
case .Success(let value): // handle success
case .Error(let error): // handle error
case .success(let value): // handle success
case .error(let error): // handle error
}
}
```
@ -123,8 +123,8 @@ let action : Action<HTMLPage> = browser.open(url)
action.start { result in
switch result {
case .Success(let page): // process page
case .Error(let error): // handle error
case .success(let page): // process page
case .error(let error): // handle error
}
}
```
@ -140,13 +140,13 @@ There are currently a few *Actions* implemented, helping you visit and navigate
The returned WKZombie Action will load and return a HTML or JSON page for the specified URL.
```ruby
func open<T : Page>(url: NSURL) -> Action<T>
func open<T : Page>(url: URL) -> Action<T>
```
Optionally, a *PostAction* can be passed. This is a special wait/validation action, that is performed after the page has finished loading. See [PostAction](#special-parameters) for more information.
```ruby
func open<T : Page>(then: PostAction) -> (url: NSURL) -> Action<T>
func open<T : Page>(then: PostAction) -> (url: URL) -> Action<T>
```
### Get the current Website
@ -236,7 +236,7 @@ The following code shows another way to execute JavaScript, that is e.g. value o
```ruby
browser.open(url)
>>> browser.get(by: .Id("div"))
>>> browser.get(by: .id("div"))
>>> browser.map { $0.objectForKey("onClick")! }
>>> browser.execute
>>> browser.inspect
@ -265,7 +265,7 @@ let image : UIImage? = link.fetchedContent()
Fetched data can be converted into types, that implement the _HTMLFetchableContent_ protocol. The following types are currently supported:
- UIImage / NSImage
- NSData
- Data
__Note:__ See the OSX example for more info on how to use this.
@ -297,7 +297,7 @@ Secondly, adding the `>>*` operator will trigger the snapshot event:
```ruby
open(url)
>>* get(by: .Id("element"))
>>* get(by: .id("element"))
=== myOutput
```
**Note: This operator only works with the WKZombie shared instance.**
@ -307,7 +307,7 @@ Alternatively, one can use the *snap* command:
```ruby
browser.open(url)
>>> browser.snap
>>> browser.get(by: .Id("element"))
>>> browser.get(by: .id("element"))
=== myOutput
```
@ -322,27 +322,27 @@ Some *Actions*, that incorporate a (re-)loading of webpages (e.g. [open](#open-a
PostAction | Description
------------------------- | -------------
**Wait** (Seconds) | The time in seconds that the action will wait (after the page has been loaded) before returning. This is useful in cases where the page loading has been completed, but some JavaScript/Image loading is still in progress.
**Validate** (Javascript) | The action will complete if the specified JavaScript expression/script returns 'true' or a timeout occurs.
**wait** (Seconds) | The time in seconds that the action will wait (after the page has been loaded) before returning. This is useful in cases where the page loading has been completed, but some JavaScript/Image loading is still in progress.
**validate** (Javascript) | The action will complete if the specified JavaScript expression/script returns 'true' or a timeout occurs.
### 2. SearchType
In order to find certain HTML elements within a page, you have to specify a *SearchType*. The return type of [get()](#find-html-elements) and [getAll()](#find-html-elements) is generic and determines which tag should be searched for. For instance, the following would return all links with the class *book*:
```ruby
let books : Action<HTMLLink> = browser.getAll(by: .Class("book"))(page: htmlPage)
let books : Action<HTMLLink> = browser.getAll(by: .class("book"))(page: htmlPage)
```
The following 6 types are currently available and supported:
SearchType | Description
------------------------------ | -------------
**Id** (String) | Returns an element that matches the specified id.
**Name** (String) | Returns all elements matching the specified value for their *name* attribute.
**Text** (String) | Returns all elements with inner content, that *contain* the specified text.
**Class** (String) | Returns all elements that match the specified class name.
**Attribute** (String, String) | Returns all elements that match the specified attribute name/value combination.
**Contains** (String, String) | Returns all elements with an attribute containing the specified value.
**id** (String) | Returns an element that matches the specified id.
**name** (String) | Returns all elements matching the specified value for their *name* attribute.
**text** (String) | Returns all elements with inner content, that *contain* the specified text.
**class** (String) | Returns all elements that match the specified class name.
**attribute** (String, String) | Returns all elements that match the specified attribute name/value combination.
**contains** (String, String) | Returns all elements with an attribute containing the specified value.
**XPathQuery** (String) | Returns all elements that match the specified XPath query.
## Operators
@ -390,7 +390,7 @@ The following example shows how to press a button that is embedded in an iframe:
browser.open(startURL())
>>> browser.get(by: .XPathQuery("//iframe[@name='button_frame']"))
>>> browser.swap
>>> browser.get(by: .Id("button"))
>>> browser.get(by: .id("button"))
>>> browser.press
=== myOutput
```
@ -469,10 +469,10 @@ As mentioned above, WKZombie as rudimentary support for JSON documents.
For parsing and decoding JSON, the following methods and protocols are available:
#### Parsing
The returned WKZombie Action will parse NSData and create a JSON object.
The returned WKZombie Action will parse Data and create a JSON object.
```ruby
func parse<T: JSON>(data: NSData) -> Action<T>
func parse<T: JSON>(data: Data) -> Action<T>
```
#### Decoding

View File

@ -1,5 +1,4 @@
#!/bin/bash
bash Scripts/setup-framework.sh
xcodebuild -workspace WKZombie.xcworkspace -scheme WKZombie -sdk iphonesimulator build test
xcodebuild -workspace WKZombie.xcworkspace -scheme WKZombie -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6s,OS=10.0' build test

View File

@ -39,7 +39,7 @@ class Tests: XCTestCase {
}
func testExecute() {
let expectation = expectationWithDescription("JavaScript Done.")
let expectation = self.expectation(description: "JavaScript Done.")
browser.open(startURL())
>>> browser.execute("document.title")
@ -48,18 +48,18 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
func testInspect() {
let expectation = expectationWithDescription("Inspect Done.")
let expectation = self.expectation(description: "Inspect Done.")
var originalPage : HTMLPage?
browser.open(startURL())
>>> browser.map { originalPage = $0 as HTMLPage }
>>> browser.inspect
=== { (result: HTMLPage?) in
if let result = result, originalPage = originalPage {
if let result = result, let originalPage = originalPage {
XCTAssertEqual(result.data, originalPage.data)
} else {
XCTAssert(false)
@ -68,14 +68,14 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
func testButtonPress() {
let expectation = expectationWithDescription("Button Press Done.")
let expectation = self.expectation(description: "Button Press Done.")
browser.open(startURL())
>>> browser.get(by: .Name("button"))
>>> browser.get(by: .name("button"))
>>> browser.press
>>> browser.execute("document.title")
=== { (result: JavaScriptResult?) in
@ -83,14 +83,14 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
func testFormSubmit() {
let expectation = expectationWithDescription("Form Submit Done.")
let expectation = self.expectation(description: "Form Submit Done.")
browser.open(startURL())
>>> browser.get(by: .Id("test_form"))
>>> browser.get(by: .id("test_form"))
>>> browser.submit
>>> browser.execute("document.title")
=== { (result: JavaScriptResult?) in
@ -98,11 +98,11 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
func testFormWithXPathQuerySubmit() {
let expectation = expectationWithDescription("Form XPathQuery Submit Done.")
let expectation = self.expectation(description: "Form XPathQuery Submit Done.")
browser.open(startURL())
>>> browser.get(by: .XPathQuery("//form[1]"))
@ -113,14 +113,14 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
func testDivOnClick() {
let expectation = expectationWithDescription("DIV OnClick Done.")
let expectation = self.expectation(description: "DIV OnClick Done.")
browser.open(startURL())
>>> browser.get(by: .Id("onClick_div"))
>>> browser.get(by: .id("onClick_div"))
>>> browser.map { $0.objectForKey("onClick")! }
>>> browser.execute
>>> browser.inspect
@ -130,14 +130,14 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
func testDivHref() {
let expectation = expectationWithDescription("DIV Href Done.")
let expectation = self.expectation(description: "DIV Href Done.")
browser.open(startURL())
>>> browser.get(by: .Id("href_div"))
>>> browser.get(by: .id("href_div"))
>>> browser.map { "window.location.href='\($0.objectForKey("href")!)'" }
>>> browser.execute
>>> browser.inspect
@ -147,11 +147,11 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
func testUserAgent() {
let expectation = expectationWithDescription("UserAgent Test Done.")
let expectation = self.expectation(description: "UserAgent Test Done.")
browser.userAgent = "WKZombie"
browser.open(startURL())
@ -161,11 +161,11 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
func testSnapshot() {
let expectation = expectationWithDescription("Snapshot Test Done.")
let expectation = self.expectation(description: "Snapshot Test Done.")
var snapshots = [Snapshot]()
@ -176,7 +176,7 @@ class Tests: XCTestCase {
browser.open(startURL())
>>> browser.snap
>>> browser.get(by: .Name("button"))
>>> browser.get(by: .name("button"))
>>> browser.press
>>> browser.snap
=== { (result: HTMLPage?) in
@ -184,11 +184,11 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
func testSwap() {
let expectation = expectationWithDescription("iframe Button Test Done.")
let expectation = self.expectation(description: "iframe Button Test Done.")
browser.open(startURL())
>>> browser.get(by: .XPathQuery("//iframe[@name='button_frame']"))
@ -201,16 +201,16 @@ class Tests: XCTestCase {
expectation.fulfill()
}
waitForExpectationsWithTimeout(20.0, handler: nil)
waitForExpectations(timeout: 20.0, handler: nil)
}
//========================================
// MARK: Helper Methods
//========================================
private func startURL() -> NSURL {
let bundle = NSBundle(forClass: self.dynamicType)
let testPage = bundle.URLForResource("HTMLTestPage", withExtension: "html")!
private func startURL() -> URL {
let bundle = Bundle(for: type(of: self))
let testPage = bundle.url(forResource: "HTMLTestPage", withExtension: "html")!
return testPage
}
}

View File

@ -29,9 +29,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var viewController : UIViewController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.backgroundColor = .whiteColor()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.backgroundColor = .white
viewController = UIViewController(nibName: nil, bundle: nil)
window?.rootViewController = viewController
window?.makeKeyAndVisible()

View File

@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "WKZombie"
s.version = "0.9.5"
s.version = "1.0.0.rc.1"
s.summary = "WKZombie is a Swift library for iOS/OSX to browse websites without the need of User Interface or API."
s.description = <<-DESC
@ -26,4 +26,6 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.dependency 'hpple', '0.2.0'
s.pod_target_xcconfig = { 'SWIFT_VERSION' => '3.0' }
end

View File

@ -390,11 +390,12 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0730;
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = "Mathias Köhnke";
TargetAttributes = {
BF1F915E1CB6AF9300A58A7F = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0800;
TestTargetID = BF455D9A1CEFBF99006D97D0;
};
BF1F917C1CB6D83400A58A7F = {
@ -402,9 +403,12 @@
};
BF455D9A1CEFBF99006D97D0 = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 0800;
ProvisioningStyle = Automatic;
};
BFAECCAB1CB69C0200C87F3B = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0800;
};
};
};
@ -562,6 +566,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = de.mathiaskoehnke.WKZombieTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HostApplication.app/HostApplication";
};
name = Debug;
@ -574,6 +579,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = de.mathiaskoehnke.WKZombieTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HostApplication.app/HostApplication";
};
name = Release;
@ -581,7 +587,7 @@
BF1F918F1CB6D83400A58A7F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@ -601,13 +607,14 @@
PRODUCT_NAME = WKZombie;
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Debug;
};
BF1F91901CB6D83400A58A7F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@ -627,17 +634,21 @@
PRODUCT_NAME = WKZombie;
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Release;
};
BF455DAB1CEFBF99006D97D0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "$(SRCROOT)/Tests/HostApplication/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = de.mathiaskoehnke.HostApplication;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
@ -645,11 +656,14 @@
BF455DAC1CEFBF99006D97D0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "$(SRCROOT)/Tests/HostApplication/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 9.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = de.mathiaskoehnke.HostApplication;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
@ -668,8 +682,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -697,6 +713,7 @@
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@ -717,8 +734,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -738,6 +757,8 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
@ -748,6 +769,7 @@
BFAECCC11CB69C0200C87F3B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@ -766,12 +788,14 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
BFAECCC21CB69C0200C87F3B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@ -790,6 +814,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "";
SWIFT_VERSION = 3.0;
};
name = Release;
};

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -32,7 +32,7 @@
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFF0629F1CB6A0F500C060E6"
BlueprintIdentifier = "BF1F915E1CB6AF9300A58A7F"
BuildableName = "Tests.xctest"
BlueprintName = "Tests"
ReferencedContainer = "container:WKZombie.xcodeproj">