{"id":1752,"url":"https://github.com/mkoehnke/WKZombie","last_synced_at":"2025-08-06T14:31:36.692Z","repository":{"id":43642853,"uuid":"47003121","full_name":"mkoehnke/WKZombie","owner":"mkoehnke","description":"WKZombie is a Swift framework for iOS/OSX to navigate within websites and collect data without the need of User Interface or API, also known as Headless browser. It can be used to run automated tests / snapshots and manipulate websites using Javascript.","archived":false,"fork":false,"pushed_at":"2020-09-28T11:16:00.000Z","size":2647,"stargazers_count":1163,"open_issues_count":26,"forks_count":100,"subscribers_count":30,"default_branch":"master","last_synced_at":"2024-04-28T21:32:15.712Z","etag":null,"topics":["browser","headless","ios","osx","swift","testing"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mkoehnke.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-11-27T23:29:28.000Z","updated_at":"2024-04-23T22:13:40.000Z","dependencies_parsed_at":"2022-11-29T13:20:14.931Z","dependency_job_id":null,"html_url":"https://github.com/mkoehnke/WKZombie","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkoehnke%2FWKZombie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkoehnke%2FWKZombie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkoehnke%2FWKZombie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkoehnke%2FWKZombie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mkoehnke","download_url":"https://codeload.github.com/mkoehnke/WKZombie/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":215780273,"owners_count":15929791,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser","headless","ios","osx","swift","testing"],"created_at":"2024-01-05T20:15:54.953Z","updated_at":"2024-08-17T16:31:11.360Z","avatar_url":"https://github.com/mkoehnke.png","language":"Swift","funding_links":[],"categories":["Parsing","Libs","Swift","Network [🔝](#readme)"],"sub_categories":["Other Parsing","Network","Other free courses"],"readme":"# WKZombie\n[![Twitter: @mkoehnke](https://img.shields.io/badge/contact-@mkoehnke-blue.svg?style=flat)](https://twitter.com/mkoehnke)\n[![Version](https://img.shields.io/cocoapods/v/WKZombie.svg?style=flat)](http://cocoadocs.org/docsets/WKZombie)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![SPM compatible](https://img.shields.io/badge/SPM-compatible-orange.svg?style=flat)](https://github.com/apple/swift-package-manager)\n[![License](https://img.shields.io/cocoapods/l/WKZombie.svg?style=flat)](http://cocoadocs.org/docsets/WKZombie)\n[![Platform](https://img.shields.io/cocoapods/p/WKZombie.svg?style=flat)](http://cocoadocs.org/docsets/WKZombie)\n[![Build Status](https://travis-ci.org/mkoehnke/WKZombie.svg?branch=master)](https://travis-ci.org/mkoehnke/WKZombie)\n\n[\u003cimg align=\"left\" src=\"https://raw.githubusercontent.com/mkoehnke/WKZombie/master/Resources/Documentation/Logo.png\" hspace=\"30\" width=\"140px\"\u003e](#logo)\n\nWKZombie 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 4**.\n\nIt 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.**\n\n## Use Cases\nThere are many use cases for a Headless Browser. Some of them are:\n\n* Collect data without an API\n* Scraping websites\n* Automating interaction of websites\n* Manipulation of websites\n* Running automated tests / snapshots\n* etc.\n\n## Example\nThe following example is supposed to demonstrate the WKZombie functionality. Let's assume that we want to **show all iOS Provisioning Profiles in the Apple Developer Portal**.\n\n#### Manual Web-Browser Navigation\n\nWhen using a common web-browser (e.g. Mobile Safari) on iOS, you would typically type in your credentials, sign in and navigate (via links) to the *Provisioning Profiles* section:\n\n\u003cimg src=\"https://raw.githubusercontent.com/mkoehnke/WKZombie/master/Resources/Documentation/WKZombie-Web-Demo.gif\" /\u003e\n\n#### Automation with WKZombie\n\nThe same navigation process can be reproduced **automatically** within an iOS/OSX app linking WKZombie *Actions*. In addition, it is now possible to manipulate or display this data in a native way with *UITextfield*, *UIButton* and a *UITableView*.\n\n\u003cimg src=\"https://raw.githubusercontent.com/mkoehnke/WKZombie/master/Resources/Documentation/WKZombie-Simulator-Demo.gif\" /\u003e\n\n**Take a look at the iOS/OSX demos in the `Example` directory to see how to use it.**\n\n# Getting Started\n\n## iOS / OSX\n\nThe best way to get started is to look at the sample project. Just run the following commands in your shell and you're good to go:\n\n```bash\n$ cd Example\n$ pod install\n$ open Example.xcworkspace\n```\n\n__Note:__ You will need CocoaPods 1.0 beta4 or higher.\n\n## Command-Line\n\nFor a Command-Line demo, run the following commands inside the `WKZombie` root folder:\n\n```ogdl\n$ swift build -Xcc -I/usr/include/libxml2 -Xlinker -lxml2\n\n$ .build/debug/Example \u003cappleid\u003e \u003cpassword\u003e\n```\n\n\n# Usage\nA WKZombie instance equates to a web session. Top-level convenience methods like *WKZombie.open()* use a shared instance, which is configured with the default settings.\n\nAs such, the following three statements are equivalent:\n\n```ruby\nlet action : Action\u003cHTMLPage\u003e = open(url)\n```\n\n```ruby\nlet action : Action\u003cHTMLPage\u003e = WKZombie.open(url)\n```\n\n```ruby\nlet browser = WKZombie.sharedInstance\nlet action : Action\u003cHTMLPage\u003e = browser.open(url)\n```\n\nApplications can also create their own WKZombie instance:\n\n```ruby\nself.browser = WKZombie(name: \"Demo\")\n```\n\nBe sure to keep `browser` in a stored property for the time of being used.\n\n### a. Chaining Actions\n\nWeb page navigation is based on *Actions*, that can be executed **implicitly** when chaining actions using the [`\u003e\u003e\u003e`](#operators) or [`\u003e\u003e*`](#operators) (for snapshots) operators. All chained actions pass their result to the next action. The [`===`](#operators) operator then starts the execution of the action chain.\n\nThe following snippet demonstrates how you would use WKZombie to **collect all Provisioning Profiles** from the Developer Portal and **take snapshots of every page**:\n\n```ruby\n    open(url)\n\u003e\u003e* get(by: .id(\"accountname\"))\n\u003e\u003e\u003e setAttribute(\"value\", value: user)\n\u003e\u003e* get(by: .id(\"accountpassword\"))\n\u003e\u003e\u003e setAttribute(\"value\", value: password)\n\u003e\u003e* get(by: .name(\"form2\"))\n\u003e\u003e\u003e submit\n\u003e\u003e* get(by: .contains(\"href\", \"/account/\"))\n\u003e\u003e\u003e click(then: .wait(2.5))\n\u003e\u003e* getAll(by: .contains(\"class\", \"row-\"))\n=== myOutput\n```\n\nIn order to output or process the collected data, one can either use a closure or implement a custom function taking the result as parameter:\n\n```ruby\nfunc myOutput(result: [HTMLTableColumn]?) {\n  // handle result\n}\n```\n\nor\n\n```ruby\nfunc myOutput(result: Result\u003c[HTMLTableColumn]\u003e) {\n  switch result {\n  case .success(let value): // handle success\n  case .error(let error): // handle error\n  }\n}\n```\n\n### b. Manual Actions\n\n*Actions* can also be started manually by calling the *start()* method:\n\n```ruby\nlet action : Action\u003cHTMLPage\u003e = browser.open(url)\n\naction.start { result in\n    switch result {\n    case .success(let page): // process page\n    case .error(let error):  // handle error\n    }\n}\n```\n\nThis is certainly the less complicated way, but you have to write a lot more code, which might become confusing when you want to execute *Actions* successively.  \n\n\n## Basic Action Functions\nThere are currently a few *Actions* implemented, helping you visit and navigate within a website:\n\n### Open a Website\n\nThe returned WKZombie Action will load and return a HTML or JSON page for the specified URL.\n\n```ruby\nfunc open\u003cT : Page\u003e(url: URL) -\u003e Action\u003cT\u003e\n```\n\nOptionally, 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.\n\n```ruby\nfunc open\u003cT : Page\u003e(then: PostAction) -\u003e (url: URL) -\u003e Action\u003cT\u003e\n```\n\n### Get the current Website\n\nThe returned WKZombie Action will retrieve the current page.\n\n```ruby\nfunc inspect\u003cT: Page\u003e() -\u003e Action\u003cT\u003e\n```\n\n### Submit a Form\n\nThe returned WKZombie Action will submit the specified HTML form.\n\n```ruby\nfunc submit\u003cT : Page\u003e(form: HTMLForm) -\u003e Action\u003cT\u003e\n```\n\nOptionally, a *PostAction* can be passed. See [PostAction](#special-parameters) for more information.\n\n```ruby\nfunc submit\u003cT : Page\u003e(then: PostAction) -\u003e (form: HTMLForm) -\u003e Action\u003cT\u003e\n```\n\n### Click a Link / Press a Button\n\nThe returned WKZombie Actions will simulate the interaction with a HTML link/button.\n\n```ruby\nfunc click\u003cT: Page\u003e(link : HTMLLink) -\u003e Action\u003cT\u003e\nfunc press\u003cT: Page\u003e(button : HTMLButton) -\u003e Action\u003cT\u003e\n```\n\nOptionally, a *PostAction* can be passed. See [PostAction](#Special- Parameters) for more information.\n\n```ruby\nfunc click\u003cT: Page\u003e(then: PostAction) -\u003e (link : HTMLLink) -\u003e Action\u003cT\u003e\nfunc press\u003cT: Page\u003e(then: PostAction) -\u003e (button : HTMLButton) -\u003e Action\u003cT\u003e\n```\n\n**Note: HTMLButton only works if the \"onClick\" HTML-Attribute is present. If you want to submit a HTML form, you should use [Submit](#submit-a-form) instead.**\n\n### Find HTML Elements\n\nThe returned WKZombie Action will search the specified HTML page and return the first element matching the generic HTML element type and passed [SearchType](#special-parameters).\n\n```ruby\nfunc get\u003cT: HTMLElement\u003e(by: SearchType\u003cT\u003e) -\u003e (page: HTMLPage) -\u003e Action\u003cT\u003e\n```\n\nThe returned WKZombie Action will search and return all elements matching.\n\n```ruby\nfunc getAll\u003cT: HTMLElement\u003e(by: SearchType\u003cT\u003e) -\u003e (page: HTMLPage) -\u003e Action\u003c[T]\u003e\n```\n\n\n### Set an Attribute\n\nThe returned WKZombie Action will set or update an existing attribute/value pair on the specified HTMLElement.\n```ruby\nfunc setAttribute\u003cT: HTMLElement\u003e(key: String, value: String?) -\u003e (element: T) -\u003e Action\u003cHTMLPage\u003e\n```\n\n### Execute JavaScript\n\nThe returned WKZombie Actions will execute a JavaScript string.\n\n```ruby\nfunc execute(script: JavaScript) -\u003e (page: HTMLPage) -\u003e Action\u003cJavaScriptResult\u003e\nfunc execute(script: JavaScript) -\u003e Action\u003cJavaScriptResult\u003e\n```\n\nFor example, the following example shows how retrieve the title of the currently loaded website using the *execute()* method:\n\n```ruby\n    browser.inspect\n\u003e\u003e\u003e browser.execute(\"document.title\")\n=== myOutput\n\nfunc myOutput(result: JavaScriptResult?) {\n  // handle result\n}\n```\n\nThe following code shows another way to execute JavaScript, that is e.g. value of an attribute:\n\n```ruby\n    browser.open(url)\n\u003e\u003e\u003e browser.get(by: .id(\"div\"))\n\u003e\u003e\u003e browser.map { $0.objectForKey(\"onClick\")! }\n\u003e\u003e\u003e browser.execute\n\u003e\u003e\u003e browser.inspect\n=== myOutput\n\nfunc myOutput(result: HTMLPage?) {\n  // handle result\n}\n```\n\n### Fetching\n\nSome HTMLElements, that implement the _HTMLFetchable_ protocol (e.g. _HTMLLink_ or _HTMLImage_), contain attributes like _\"src\"_ or _\"href\"_, that link to remote objects or data.\nThe following method returns a WKZombie Action that can conveniently download this data:\n\n```ruby\nfunc fetch\u003cT: HTMLFetchable\u003e(fetchable: T) -\u003e Action\u003cT\u003e\n```\n\nOnce the _fetch_ method has been executed, the data can be retrieved and __converted__. The following example shows how to convert data, fetched from a link, into an UIImage:\n\n```ruby\nlet image : UIImage? = link.fetchedContent()\n```\n\nFetched data can be converted into types, that implement the _HTMLFetchableContent_ protocol. The following types are currently supported:\n\n- UIImage / NSImage\n- Data\n\n__Note:__ See the OSX example for more info on how to use this.\n\n### Transform\n\nThe returned WKZombie Action will transform a WKZombie object into another object using the specified function *f*.\n\n```ruby\nfunc map\u003cT, A\u003e(f: T -\u003e A) -\u003e (element: T) -\u003e Action\u003cA\u003e\n```\n\nThis function transforms an object into another object using the specified function *f*.\n\n```ruby\nfunc map\u003cT, A\u003e(f: T -\u003e A) -\u003e (object: T) -\u003e A\n```\n\n## Taking Snapshots\n\nTaking snapshots is **available for iOS**. First, a *snapshotHandler* must be registered, that will be called each time a snapshot has been taken:\n\n```ruby\nWKZombie.sharedInstance.snapshotHandler = { snapshot in\n    let image = snapshot.image\n}\n```\n\nSecondly, adding the `\u003e\u003e*` operator will trigger the snapshot event:\n\n```ruby\n    open(url)\n\u003e\u003e* get(by: .id(\"element\"))\n=== myOutput\n```\n**Note: This operator only works with the WKZombie shared instance.**\n\nAlternatively, one can use the *snap* command:\n\n```ruby\n    browser.open(url)\n\u003e\u003e\u003e browser.snap\n\u003e\u003e\u003e browser.get(by: .id(\"element\"))\n=== myOutput\n```\n\nTake a look at the **iOS example for more information** of how to take snapshots.\n\n\n## Special Parameters\n\n### 1. PostAction\n\nSome *Actions*, that incorporate a (re-)loading of webpages (e.g. [open](#open-a-website), [submit](#submit-a-form), etc.), have *PostActions* available. A *PostAction* is a wait or validation action, that will be performed after the page has finished loading:\n\nPostAction                | Description\n------------------------- | -------------\n**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.\n**validate** (Javascript) | The action will complete if the specified JavaScript expression/script returns 'true' or a timeout occurs.\n\n### 2. SearchType\n\nIn 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*:\n\n```ruby\nlet books : Action\u003cHTMLLink\u003e = browser.getAll(by: .class(\"book\"))(page: htmlPage)\n```\n\nThe following 6 types are currently available and supported:\n\nSearchType                     | Description\n------------------------------ | -------------\n**id** (String)                | Returns an element that matches the specified id.\n**name** (String)              | Returns all elements matching the specified value for their *name* attribute.\n**text** (String)              | Returns all elements with inner content, that *contain* the specified text.\n**class** (String)             | Returns all elements that match the specified class name.\n**attribute** (String, String) | Returns all elements that match the specified attribute name/value combination.\n**contains** (String, String)  | Returns all elements with an attribute containing the specified value.\n**XPathQuery** (String)        | Returns all elements that match the specified XPath query.\n\n## Operators\n\nThe following Operators can be applied to *Actions*, which makes chained *Actions* easier to read:\n\nOperator    | iOS | OSX | Description\n:----------:|:---:|:---:| ---------------\n`\u003e\u003e\u003e`       | x   | x   | This Operator equates to the *andThen()* method. Here, the left-hand side *Action* will be started and the result is used as parameter for the right-hand side *Action*. **Note:** If the right-hand side *Action* doesn't take a parameter, the result of the left-hand side *Action* will be ignored and not passed.\n`\u003e\u003e*`       | x   |     | This is a convenience operator for the _snap_ command. It is equal to the `\u003e\u003e\u003e` operator with the difference that a snapshot will be taken after the left Action has been finished. **Note: This operator throws an assert if used with any other than the shared instance.**\n`===`       | x   | x   | This Operator starts the left-hand side *Action* and passes the result as **Optional** to the function on the right-hand side.\n\n## Authentication\n\nOnce in a while you might need to handle authentication challenges e.g. *Basic Authentication* or *Self-signed Certificates*. WKZombie provides an `authenticationHandler`, which is invoked when the internal web view needs to respond to an authentication challenge.\n\n### Basic Authentication\n\nThe following example shows how Basic Authentication could be handled:\n\n```ruby\nbrowser.authenticationHandler = { (challenge) -\u003e (URLSession.AuthChallengeDisposition, URLCredential?) in\n\treturn (.useCredential, URLCredential(user: \"user\", password: \"passwd\", persistence: .forSession))\n}\n\n```\n\n### Self-signed Certificates\n\nIn case of a self-signed certificate, you could use the authentication handler like this:\n\n```ruby\nbrowser.authenticationHandler = { (challenge) -\u003e (URLSession.AuthChallengeDisposition, URLCredential?) in\n\treturn (.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))\n}\n\n```\n\n\n## Advanced Action Functions\n\n### Batch\n\nThe returned WKZombie Action will make a bulk execution of the specified action function *f* with the provided input elements. Once all actions have finished executing, the collected results will be returned.\n\n```ruby\nfunc batch\u003cT, U\u003e(f: T -\u003e Action\u003cU\u003e) -\u003e (elements: [T]) -\u003e Action\u003c[U]\u003e\n```\n\n### Collect\n\nThe returned WKZombie Action will execute the specified action (with the result of the previous action execution as input parameter) until a certain condition is met. Afterwards, it will return the collected action results.\n\n```ruby\nfunc collect\u003cT\u003e(f: T -\u003e Action\u003cT\u003e, until: T -\u003e Bool) -\u003e (initial: T) -\u003e Action\u003c[T]\u003e\n```\n\n### Swap\n\n**Note:** Due to a XPath limitation, WKZombie can't access elements within an `iframe` directly. The swap function can workaround this issue by switching web contexts.\n\nThe returned WKZombie Action will swap the current page context with the context of an embedded `\u003ciframe\u003e`.\n\n```ruby\nfunc swap\u003cT: Page\u003e(iframe : HTMLFrame) -\u003e Action\u003cT\u003e\nfunc swap\u003cT: Page\u003e(then postAction: PostAction) -\u003e (iframe : HTMLFrame) -\u003e Action\u003cT\u003e\n```\n\nThe following example shows how to press a button that is embedded in an iframe:\n\n```ruby\n    browser.open(startURL())\n\u003e\u003e\u003e browser.get(by: .XPathQuery(\"//iframe[@name='button_frame']\"))\n\u003e\u003e\u003e browser.swap\n\u003e\u003e\u003e browser.get(by: .id(\"button\"))\n\u003e\u003e\u003e browser.press\n=== myOutput\n```\n\n## Test / Debug\n\n### Dump\n\nThis command is useful for **debugging**. It prints out the current state of the WKZombie browser represented as *DOM*.\n\n```ruby\nfunc dump()\n```\n\n### Clear Cache and Cookies\n\nClears the cache/cookie data (such as login data, etc).\n\n```ruby\nfunc clearCache()\n```\n\n### Logging\n\nWKZombie logging can be enabled or disabled by setting the following _Logger_ variable:\n\n```ruby\nLogger.enabled = false\n```\n\n### User Agent\n\nThe user agent of WKZombie can be changed by setting the following variable:\n\n```ruby\nbrowser.userAgent = \"Mozilla/5.0 (iPhone; CPU iPhone OS 9_0 like Mac OS X) AppleWebKit/601.1.32 (KHTML, like Gecko) Mobile/13A4254v\"\n```\n\n### Timeout\n\nAn operation is cancelled if the time it needs to complete exceeds the time specified by this property. The default value is 30 seconds.\n\n```ruby\nbrowser.timeoutInSeconds = 15.0\n```\n\n### Load Media Content\n\nThis value is 'true' by default. If set '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. \n\n```ruby\nbrowser.loadMediaContent = false\n```\n\n### Show Network Activity\n\nIf set to ``true``, it will show the network activity indicator in the status bar. The default is ``true``.\n\n```ruby\nbrowser.showNetworkActivity = true\n```\n\n\n## HTML Elements\n\nWhen using WKZombie, the following classes are involved when interacting with websites:\n\n### HTMLPage\n\nThis class represents a **read-only** DOM of a website. It allows you to search for HTML elements using the [SearchType](#special-parameters) parameter.\n\n### HTMLElement\n\nThe *HTMLElement* class is a **base class for all elements in the DOM**. It allows you to inspect attributes or the inner content (e.g. text) of that element. Currently, there are 7 subclasses with additional element-specific methods and variables available:\n\n* HTMLForm\n* HTMLLink\n* HTMLButton\n* HTMLImage\n* HTMLTable\n* HTMLTableColumn\n* HTMLTableRow\n\n**Additional subclasses can be easily implemented and might be added in the future.**\n\n## JSON Elements\n\nAs mentioned above, WKZombie as rudimentary support for JSON documents.\n\n### Methods and Protocols\n\nFor parsing and decoding JSON, the following methods and protocols are available:\n\n#### Parsing\nThe returned WKZombie Action will parse Data and create a JSON object.\n\n```ruby\nfunc parse\u003cT: JSON\u003e(data: Data) -\u003e Action\u003cT\u003e\n```\n\n#### Decoding\nThe following methods return a WKZombie Action, that will take a *JSONParsable* (Array, Dictionary and JSONPage) and decode it into a Model object. This particular Model class has to implement the [*JSONDecodable*](#jsondecodable) protocol.\n\n```ruby\nfunc decode\u003cT : JSONDecodable\u003e(element: JSONParsable) -\u003e Action\u003cT\u003e\n```\n\n```ruby\nfunc decode\u003cT : JSONDecodable\u003e(array: JSONParsable) -\u003e Action\u003c[T]\u003e\n```\n\n#### JSONDecodable\nThis protocol must be implemented by each class, that is supposed to support JSON decoding.\nThe implementation will take a *JSONElement* (Dictionary\\\u003cString : AnyObject\\\u003e) and create an object instance of that class.\n\n```ruby\nstatic func decode(json: JSONElement) -\u003e Self?\n```\n\n\n### Example\n\nThe following example shows how to use JSON parsing/decoding in conjunction with WKZombie:\n\n```ruby\n    browser.open(bookURL)\n\u003e\u003e\u003e browser.decode\n=== myOutput\n```\n\n```ruby\nfunc myOutput(result: Book?) {\n  // handle result\n}\n```\n\n# Installation\n\n## [CocoaPods](http://cocoapods.org)\n\nTo integrate `WKZombie` into your Xcode project using CocoaPods, specify it in your `Podfile`:\n\n```ruby\nsource 'https://github.com/CocoaPods/Specs.git'\nuse_frameworks!\n\npod 'WKZombie'\n```\n\nThen, run the following command:\n\n```bash\n$ pod install\n```\n\n## [Carthage](http://github.com/Carthage/Carthage)\n\nTo integrate `WKZombie` into your Xcode project using Carthage, specify it in your `Cartfile`:\n\n```ogdl\ngithub \"mkoehnke/WKZombie\"\n```\n\n## [Swift Package Manager](http://github.com/apple/swift-package-manager)\n\nTo build `WKZombie` using the Swift Package Manager, add it as dependency to your `Package.swift` file and run the following command:\n\n```ogdl\nswift build -Xcc -I/usr/include/libxml2 -Xlinker -lxml2\n```\n\n# FAQ\n\n## How can I use WKZombie and [Alamofire](https://github.com/Alamofire/Alamofire) in the same project?\n\nWhen using Alamofire and WKZombie in the same project, you might encounter a collision issue with keyword `Result` like this:\n\n```\n'Result' is ambiguous for type lookup in this context\n```\n\nThis is due to the fact, that both modules use the same name for their result enum type. The type can be disambiguated using the following syntax in that particular file:\n\n```ruby\nimport enum WKZombie.Result\n```\n\nFrom this point on, Result unambiguously refers to the one in the WKZombie module.\n\nIf this would still be ambiguous or sub-optimal in some files, you can create a Swift file to rename imports using typealiases:\n\n\n```ruby\nimport enum WKZombie.Result\ntypealias WKZombieResult\u003cT\u003e = Result\u003cT\u003e\n```\n\nFor more information, take a look at the solution found [here](https://stackoverflow.com/questions/37892621/how-can-i-disambiguate-a-type-and-a-module-with-the-same-name).\n\n\n# Contributing\n\nSee the CONTRIBUTING file for how to help out. You'll need to run\n\n```bash\n$ Scripts/setup-framework.sh\n```\n\nin the root WKZombie directory to set up a buildable framework project (`WKZombie.xcworkspace`).\n\n# TODOs\n* More Unit Tests\n* More examples\n* Replace hpple with more 'Swifty' implementation\n* More descriptive errors\n\n# Author\nMathias Köhnke [@mkoehnke](http://twitter.com/mkoehnke)\n\n# More Resources\n* [A list of (almost) all headless web browsers in existence](https://github.com/dhamaniasad/HeadlessBrowsers)\n\n# Attributions\n* [Efficient JSON in Swift with Functional Concepts and Generics — Tony DiPasquale](https://robots.thoughtbot.com/efficient-json-in-swift-with-functional-concepts-and-generics)\n* [Back to the Futures — Javier Soto](https://speakerdeck.com/javisoto/back-to-the-futures)\n\n# License\nWKZombie is available under the MIT license. See the LICENSE file for more info.\n\n# Recent Changes\nThe release notes can be found [here](https://github.com/mkoehnke/WKZombie/releases).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkoehnke%2FWKZombie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmkoehnke%2FWKZombie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkoehnke%2FWKZombie/lists"}