# SWXMLHash
[![CocoaPods](https://img.shields.io/cocoapods/p/SWXMLHash.svg)]()
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![CocoaPods](https://img.shields.io/cocoapods/v/SWXMLHash.svg)](https://cocoapods.org/pods/SWXMLHash)
[![Join the chat at https://gitter.im/drmohundro/SWXMLHash](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/drmohundro/SWXMLHash?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![codebeat](https://codebeat.co/badges/893cc640-c5d9-45b2-a3ff-426e6e6b7b80)](https://codebeat.co/projects/github-com-drmohundro-swxmlhash)
SWXMLHash is a relatively simple way to parse XML in Swift. If you're familiar
with `NSXMLParser`, this library is a simple wrapper around it. Conceptually, it
provides a translation from XML to a dictionary of arrays (aka hash).
The API takes a lot of inspiration from
[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON).
## Contents
- [Requirements](#requirements)
- [Installation](#installation)
- [Getting Started](#getting-started)
- [Configuration](#configuration)
- [Examples](#examples)
- [FAQ](#faq)
- [Changelog](#changelog)
- [Contributing](#contributing)
- [License](#license)
## Requirements
- iOS 8.0+ / Mac OS X 10.9+ / tvOS 9.0+ / watchOS 2.0+
- Xcode 8.0+
## Installation
SWXMLHash can be installed using [CocoaPods](http://cocoapods.org/),
[Carthage](https://github.com/Carthage/Carthage),
[Swift Package Manager](https://swift.org/package-manager/), or manually.
### CocoaPods
To install CocoaPods, run:
```bash
$ gem install cocoapods
```
Then create a `Podfile` with the following contents:
```ruby
platform :ios, '10.0'
use_frameworks!
target 'YOUR_TARGET_NAME' do
pod 'SWXMLHash', '~> 5.0.0'
end
```
Finally, run the following command to install it:
```bash
$ pod install
```
### Carthage
To install Carthage, run (using Homebrew):
```bash
$ brew update
$ brew install carthage
```
Then add the following line to your `Cartfile`:
```
github "drmohundro/SWXMLHash" ~> 5.0
```
### Swift Package Manager
Swift Package Manager requires Swift version 4.0 or higher. First, create a
`Package.swift` file. It should look like:
```swift
dependencies: [
.package(url: "https://github.com/drmohundro/SWXMLHash.git", from: "5.0.0")
]
```
`swift build` should then pull in and compile SWXMLHash for you to begin using.
### Manual Installation
To install manually, you'll need to clone the SWXMLHash repository. You can do
this in a separate directory or you can make use of git submodules - in this
case, git submodules are recommended so that your repository has details about
which commit of SWXMLHash you're using. Once this is done, you can just drop the
`SWXMLHash.swift` file into your project.
> NOTE: if you're targeting iOS 7, you'll have to install manually because
> embedded frameworks require a minimum deployment target of iOS 8 or OSX
> Mavericks.
## Getting Started
If you're just getting started with SWXMLHash, I'd recommend cloning the
repository down and opening the workspace. I've included a Swift playground in
the workspace which makes it easy to experiment with the API and the calls.
<img src="https://raw.githubusercontent.com/drmohundro/SWXMLHash/assets/swift-playground@2x.png" width="600" alt="Swift Playground" />
## Configuration
SWXMLHash allows for limited configuration in terms of its approach to parsing.
To set any of the configuration options, you use the `configure` method, like
so:
```swift
let xml = SWXMLHash.config {
config in
// set any config options here
}.parse(xmlToParse)
```
The available options at this time are:
- `shouldProcessLazily`
- This determines whether not to use lazy loading of the XML. It can
significantly increase the performance of parsing if your XML is large.
- Defaults to `false`
- `shouldProcessNamespaces`
- This setting is forwarded on to the internal `NSXMLParser` instance. It will
return any XML elements without their namespace parts (i.e. "\<h:table\>"
will be returned as "\<table\>")
- Defaults to `false`
- `caseInsensitive`
- This setting allows for key lookups to be case insensitive. Typically XML is
a case sensitive language, but this option lets you bypass this if
necessary.
- Defaults to `false`
- `encoding`
- This setting allows for explicitly specifying the character encoding when an
XML string is passed to `parse`.
- Defaults to `String.encoding.utf8`
- `userInfo`
- This setting mimics `Codable`'s `userInfo` property to allow the user to add
contextual information that will be used for deserialization.
- See
[Codable's userInfo docs](https://developer.apple.com/documentation/swift/encoder/2894907-userinfo)
- The default is [:]
- `detectParsingErrors`
- This setting attempts to detect XML parsing errors. `parse` will return an
`XMLIndexer.parsingError` if any parsing issues are found.
- Defaults to `false` (because of backwards compatibility and because many
users attempt to parse HTML with this library)
## Examples
All examples below can be found in the included
[specs](https://github.com/drmohundro/SWXMLHash/blob/master/Tests/).
### Initialization
```swift
let xml = SWXMLHash.parse(xmlToParse)
```
Alternatively, if you're parsing a large XML file and need the best performance,
you may wish to configure the parsing to be processed lazily. Lazy processing
avoids loading the entire XML document into memory, so it could be preferable
for performance reasons. See the error handling for one caveat regarding lazy
loading.
```swift
let xml = SWXMLHash.config {
config in
config.shouldProcessLazily = true
}.parse(xmlToParse)
```
The above approach uses the new config method, but there is also a `lazy` method
directly off of `SWXMLHash`.
```swift
let xml = SWXMLHash.lazy(xmlToParse)
```
### Single Element Lookup
Given:
```xml
<root>
<header>
<title>Foo</title>
</header>
...
</root>
```
Will return "Foo".
```swift
xml["root"]["header"]["title"].element?.text
```
### Multiple Elements Lookup
Given:
```xml
<root>
...
<catalog>
<book><author>Bob</author></book>
<book><author>John</author></book>
<book><author>Mark</author></book>
</catalog>
...
</root>
```
The below will return "John".
```swift
xml["root"]["catalog"]["book"][1]["author"].element?.text
```
### Attributes Usage
Given:
```xml
<root>
...
<catalog>
<book id="1"><author>Bob</author></book>
<book id="123"><author>John</author></book>
<book id="456"><author>Mark</author></book>
</catalog>
...
</root>
```
The below will return "123".
```swift
xml["root"]["catalog"]["book"][1].element?.attribute(by: "id")?.text
```
Alternatively, you can look up an element with specific attributes. The below
will return "John".
```swift
xml["root"]["catalog"]["book"].withAttribute("id", "123")["author"].element?.text
```
### Returning All Elements At Current Level
Given:
```xml
<root>
...
<catalog>
<book><genre>Fiction</genre></book>
<book><genre>Non-fiction</genre></book>
<book><genre>Technical</genre></book>
</catalog>
...
</root>
```
The `all` method will iterate over all nodes at the indexed level. The code
below will return "Fiction, Non-fiction, Technical".
```swift
", ".join(xml["root"]["catalog"]["book"].all.map { elem in
elem["genre"].element!.text!
})
```
You can also iterate over the `all` method:
```swift
for elem in xml["root"]["catalog"]["book"].all {
print(elem["genre"].element!.text!)
}
```
### Returning All Child Elements At Current Level
Given:
```xml
<root>
<catalog>
<book>
<genre>Fiction</genre>
<title>Book</title>
<date>1/1/2015</date>
</book>
</catalog>
</root>
```
The below will `print` "root", "catalog", "book", "genre", "title", and "date"
(note the `children` method).