Skip to content

Commit

Permalink
Allow to get title, load url request, go back and forward, reload page
Browse files Browse the repository at this point in the history
Rename currentURL to url
  • Loading branch information
phimage committed Aug 21, 2016
1 parent 7763fef commit c5342b2
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 25 deletions.
3 changes: 3 additions & 0 deletions Cartfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
github "tid-kijyun/Kanna" ~> 1.1.0
github "kodlian/Eki"
github "Thomvis/BrightFutures"
2 changes: 1 addition & 1 deletion Erik.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Pod::Spec.new do |s|

# ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
s.name = "Erik"
s.version = "1.0.3"
s.version = "1.1.0"
s.summary = "A headless browser written in Swift"
s.description = <<-DESC
Erik is an headless browser based on WebKit and HTML parser Kanna.
Expand Down
34 changes: 31 additions & 3 deletions Erik/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,22 @@ public class Document : Node {
super.init(rawValue: rawValue, selectors: [])
}

var title: String? { return (rawValue as? HTMLDocument)?.title }
var head: Element? {
public var title: String? { return (rawValue as? HTMLDocument)?.title }
public var head: Element? {
guard let doc = self.rawValue as? HTMLDocument, element = doc.head else {
return nil
}
return Element(rawValue: element, selectors: ["head"])
}
var body: Element? {
public var body: Element? {
guard let doc = self.rawValue as? HTMLDocument, element = doc.body else {
return nil
}
return Element(rawValue: element, selectors: ["body"])
}
}

// HTML Node
public class Node {
var layoutEngine: LayoutEngine?

Expand All @@ -82,6 +83,7 @@ public class Node {
self.rawValue = rawValue
}

// Select elements using css selector
public func querySelectorAll(selector: String) -> [Element] {
let selectors = self.selectors + [selector]
return rawValue.css(selector).map {
Expand All @@ -91,6 +93,7 @@ public class Node {
}
}

// Select an element using css selector
public func querySelector(selector: String) -> Element? {
guard let element = rawValue.at_css(selector) else {
return nil
Expand All @@ -101,20 +104,45 @@ public class Node {
return elem
}

// Get all children element
public var elements: [Element] {
return querySelectorAll("*")
}

// Get first child element
public var firstChild: Element? {
return querySelectorAll(":first-child").first
}

// Get last child element
public var lastChild: Element? {
return querySelectorAll(":last-child").first
}

}

extension Node {

// Fill value of selected child
public func type(selector: String, value: String, key: String = "value") -> Element? {
if let element = self.querySelector(selector) {
element[key] = value
return element
}
return nil
}

// Click on selected child
public func click(selector: String) -> Element? {
if let element = self.querySelector(selector) {
element.click()
return element
}
return nil
}

}

extension Node: CustomStringConvertible {
public var description: String {
return self.toHTML ?? ""
Expand Down
129 changes: 117 additions & 12 deletions Erik/Erik.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,29 @@ SOFTWARE.
import Foundation
import WebKit

// MARK: Error
public enum ErikError: ErrorType {
// Error provided by javascript
case JavaScriptError(message: String)
// A timeout occurs
case TimeOutError
// No content returned
case NoContent
// HTML is not parsable
case HTMLNotParsable(html: String)
// Invalid url submited (NSURL init failed)
case InvalidURL(urlString: String)
}

// MARK: Erik class

// Instance of headless browser
public class Erik {

public var layoutEngine: LayoutEngine
public var htmlParser: HTMLParser

// Init the headless browser
public init(webView: WKWebView? = nil) {
if let view = webView {
self.layoutEngine = WebKitLayoutEngine(webView: view)
Expand All @@ -38,22 +56,77 @@ public class Erik {
self.htmlParser = KanaParser.instance
}

@available(*, deprecated=1.1, obsoleted=2.0, message="Use url")
public var currentURL: NSURL? {
return layoutEngine.url
}

// Get current url
public var url: NSURL? {
return layoutEngine.url
}

// Get current title
public var title: String? {
return layoutEngine.title
}

// Go to specific url
public func visitURL(URL: NSURL, completionHandler: ((Document?, ErrorType?) -> Void)?) {
layoutEngine.browseURL(URL) {[unowned self] (object, error) -> Void in
self.publishContent(object, error: error, completionHandler: completionHandler)
}
}

public var currentURL: NSURL? {
return layoutEngine.currentURL
// Go to specific url
public func visitURL(urlString: String, completionHandler: ((Document?, ErrorType?) -> Void)?) {
if let url = NSURL(string: urlString) {
visitURL(url, completionHandler: completionHandler)
} else {
completionHandler?(nil, ErikError.InvalidURL(urlString: urlString))
}
}

// Go to specific url using url request
public func loadURLRequest(URLRequest: NSURLRequest, completionHandler: ((Document?, ErrorType?) -> Void)?) {
layoutEngine.browseURL(URLRequest) {[unowned self] (object, error) -> Void in
self.publishContent(object, error: error, completionHandler: completionHandler)
}
}

// Get current content
public func currentContent(completionHandler: ((Document?, ErrorType?) -> Void)?) {
layoutEngine.currentContent {[unowned self] (object, error) -> Void in
self.publishContent(object, error: error, completionHandler: completionHandler)
}
}

// Navigates to the previous loaded page.
public func goBack() {
layoutEngine.goBack()
}

// Navigates to the next page ie. the one loaded before `goBack`
public func goForward() {
layoutEngine.goForward()
}

// A Boolean value indicating whether browser can go back
public var canGoBack: Bool {
return layoutEngine.canGoBack
}

// A Boolean value indicating whether browser can go forward
public var canGoForward: Bool {
return layoutEngine.canGoForward
}

// Reloads the current page
public func reload() {
layoutEngine.reload()
}

// MARK: private
private func publishContent(object: AnyObject?, error: ErrorType?, completionHandler: ((Document?, ErrorType?) -> Void)?) {
guard let html = object as? String else {
completionHandler?(nil, ErikError.NoContent)
Expand All @@ -74,32 +147,45 @@ public class Erik {
completionHandler?(doc, error)
}


}

// MARK: javascript
extension Erik: JavaScriptEvaluator {

public func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject?, ErrorType?) -> Void)?) {
self.layoutEngine.evaluateJavaScript(javaScriptString, completionHandler: completionHandler)
}
}

public enum ErikError: ErrorType {
case JavaScriptError(message: String)
case TimeOutError
case NoContent
case HTMLNotParsable(html: String)
}

// MARK: static
// MARK: Erik static
extension Erik {
// Shared instance used for static functions
public static let sharedInstance = Erik()

public static func visitURL(URL: NSURL, completionHandler: ((Document?, ErrorType?) -> Void)?) {
Erik.sharedInstance.visitURL(URL, completionHandler: completionHandler)
}


public static func visitURL(urlString: String, completionHandler: ((Document?, ErrorType?) -> Void)?) {
Erik.sharedInstance.visitURL(urlString, completionHandler: completionHandler)
}

public static func loadURLRequest(URLRequest: NSURLRequest, completionHandler: ((Document?, ErrorType?) -> Void)?) {
Erik.sharedInstance.loadURLRequest(URLRequest, completionHandler: completionHandler)
}

@available(*, deprecated=1.1, obsoleted=1.2, message="Use url")
public static var currentURL: NSURL? {
return Erik.sharedInstance.currentURL
return Erik.sharedInstance.url
}

public static var url: NSURL? {
return Erik.sharedInstance.url
}

public static var title: String? {
return Erik.sharedInstance.title
}

public static func currentContent(completionHandler: ((Document?, ErrorType?) -> Void)?) {
Expand All @@ -109,6 +195,25 @@ extension Erik {
public static func evaluateJavaScript(javaScriptString: String, completionHandler: ((AnyObject?, ErrorType?) -> Void)?) {
Erik.sharedInstance.evaluateJavaScript(javaScriptString, completionHandler: completionHandler)
}

public static func goBack() {
Erik.sharedInstance.goBack()
}
public static func goForward() {
Erik.sharedInstance.goForward()
}

public static var canGoBack: Bool {
return Erik.sharedInstance.canGoBack
}

public static var canGoForward: Bool {
return Erik.sharedInstance.canGoForward
}

public static func reload() {
Erik.sharedInstance.reload()
}

}

2 changes: 1 addition & 1 deletion Erik/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
47 changes: 44 additions & 3 deletions Erik/LayoutEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,18 @@ public protocol JavaScriptEvaluator {

public protocol URLBrowser {
func browseURL(URL: NSURL, completionHandler: ((AnyObject?, ErrorType?) -> Void)?)
var currentURL: NSURL? {get}
func browseURL(URLRequest: NSURLRequest, completionHandler: ((AnyObject?, ErrorType?) -> Void)?)
var url: NSURL? {get}
var title: String? {get}
func currentContent(completionHandler: ((AnyObject?, ErrorType?) -> Void)?)

func goBack()
func goForward()

var canGoBack: Bool { get }
var canGoForward: Bool { get }
func reload()

func clear()
}
public typealias LayoutEngine = protocol<URLBrowser,JavaScriptEvaluator>
Expand Down Expand Up @@ -66,14 +75,46 @@ extension WebKitLayoutEngine {

public func browseURL(URL: NSURL, completionHandler: ((AnyObject?, ErrorType?) -> Void)?) {
let request = NSURLRequest(URL: URL)
webView.loadRequest(request)
self.browseURL(request, completionHandler: completionHandler)
}

public func browseURL(URLRequest: NSURLRequest, completionHandler: ((AnyObject?, ErrorType?) -> Void)?) {
webView.loadRequest(URLRequest)
self.currentContent(completionHandler)
}


@available(*, deprecated=1.1, obsoleted=2.0, message="Use url")
public var currentURL: NSURL? {
return self.webView.URL
}

public var url: NSURL? {
return self.webView.URL
}

public var title: String? {
return self.webView.title
}

public func goBack() {
self.webView.goBack()
}
public func goForward() {
self.webView.goForward()
}

public var canGoBack: Bool {
return self.webView.canGoBack
}

public var canGoForward: Bool {
return self.webView.canGoForward
}

public func reload() {
self.webView.reload()
}

public func currentContent(completionHandler: ((AnyObject?, ErrorType?) -> Void)?) {
handleLoadRequestCompletion {
self.handleHTML(completionHandler)
Expand Down
2 changes: 1 addition & 1 deletion ErikOSX/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
Loading

0 comments on commit c5342b2

Please sign in to comment.