mirror of
https://github.com/mkoehnke/WKZombie.git
synced 2024-09-11 10:37:00 +03:00
commit
8ed6434ce3
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)"
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)")
|
||||
|
@ -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?
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.")
|
||||
|
@ -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"/>
|
||||
|
@ -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
|
||||
|
@ -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:.
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
58
README.md
58
README.md
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
LastUpgradeVersion = "0800"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -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">
|
||||
|
Loading…
Reference in New Issue
Block a user