Swiftpack.co - Package - drmohundro/SWXMLHash


CocoaPods Carthage compatible CocoaPods Join the chat at https://gitter.im/drmohundro/SWXMLHash codebeat

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.



  • iOS 8.0+ / Mac OS X 10.9+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.0+


SWXMLHash can be installed using CocoaPods, Carthage, Swift Package Manager, or manually.


To install CocoaPods, run:

$ gem install cocoapods

Then create a Podfile with the following contents:

platform :ios, '10.0'

target 'YOUR_TARGET_NAME' do
  pod 'SWXMLHash', '~> 5.0.0'

Finally, run the following command to install it:

$ pod install


To install Carthage, run (using Homebrew):

$ 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:

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.

Swift Playground


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:

let xml = SWXMLHash.config {
              config in
              // set any config options here

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
    • 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)


All examples below can be found in the included specs.


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.

let xml = SWXMLHash.config {
              config in
              config.shouldProcessLazily = true

The above approach uses the new config method, but there is also a lazy method directly off of SWXMLHash.

let xml = SWXMLHash.lazy(xmlToParse)

Single Element Lookup



Will return "Foo".


Multiple Elements Lookup



The below will return "John".


Attributes Usage


    <book id="1"><author>Bob</author></book>
    <book id="123"><author>John</author></book>
    <book id="456"><author>Mark</author></book>

The below will return "123".

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".

xml["root"]["catalog"]["book"].withAttribute("id", "123")["author"].element?.text

Returning All Elements At Current Level



The all method will iterate over all nodes at the indexed level. The code below will return "Fiction, Non-fiction, Technical".

", ".join(xml["root"]["catalog"]["book"].all.map { elem in

You can also iterate over the all method:

for elem in xml["root"]["catalog"]["book"].all {

Returning All Child Elements At Current Level



The below will print "root", "catalog", "book", "genre", "title", and "date" (note the children method).

func enumerate(indexer: XMLIndexer) {
    for child in indexer.children {

enumerate(indexer: xml)

Filtering elements


    <book id=\"bk101\">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
    <book id=\"bk102\">
      <author>Ralls, Kim</author>
      <title>Midnight Rain</title>
    <book id=\"bk103\">
      <author>Corets, Eva</author>
      <title>Maeve Ascendant</title>

The following will return return "Midnight Rain". Filtering can be by any part of the XMLElement class or by index as well.

let subIndexer = xml!["root"]["catalog"]["book"]
    .filterAll { elem, _ in elem.attribute(by: "id")!.text == "bk102" }
    .filterChildren { _, index in index >= 1 && index <= 3 }


Error Handling

Using Swift 2.0's new error handling feature:

do {
    try xml!.byKey("root").byKey("what").byKey("header").byKey("foo")
} catch let error as IndexingError {
    // error is an IndexingError instance that you can deal with

Or using the existing indexing functionality:

switch xml["root"]["what"]["header"]["foo"] {
case .element(let elem):
    // everything is good, code away!
case .xmlError(let error):
    // error is an IndexingError instance that you can deal with

Note that error handling as shown above will not work with lazy loaded XML. The lazy parsing doesn't actually occur until the element or all method are called - as a result, there isn't any way to know prior to asking for an element if it exists or not.

Simple Type Conversion


  <elem>Monday, 23 January 2016 12:01:12 111</elem>

With the following implementation for Date element and attribute deserialization:

extension Date: XMLElementDeserializable, XMLAttributeDeserializable {
    public static func deserialize(_ element: XMLElement) throws -> Date {
        let date = stringToDate(element.text)

        guard let validDate = date else {
            throw XMLDeserializationError.typeConversionFailed(type: "Date", element: element)

        return validDate

    public static func deserialize(_ attribute: XMLAttribute) throws -> Date {
        let date = stringToDate(attribute.text)

        guard let validDate = date else {
            throw XMLDeserializationError.attributeDeserializationFailed(type: "Date", attribute: attribute)

        return validDate

    private static func stringToDate(_ dateAsString: String) -> Date? {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
        return dateFormatter.date(from: dateAsString)

The below will return a date value:

let dt: Date = try xml["root"]["elem"].value()

Complex Types Conversion


    <book isbn="0000000001">
      <title>Book A</title>
    <book isbn="0000000002">
      <title>Book B</title>
    <book isbn="0000000003">
      <title>Book C</title>

with Book struct implementing XMLIndexerDeserializable:

struct Book: XMLIndexerDeserializable {
    let title: String
    let price: Double
    let year: Int
    let amount: Int?
    let isbn: Int
    let category: [String]

    static func deserialize(_ node: XMLIndexer) throws -> Book {
        return try Book(
            title: node["title"].value(),
            price: node["price"].value(),
            year: node["year"].value(),
            amount: node["amount"].value(),
            isbn: node.value(ofAttribute: "isbn"),
            category : node["categories"]["category"].value()

The below will return an array of Book structs:

let books: [Book] = try xml["root"]["books"]["book"].value()
Types Conversion

You can convert any XML to your custom type by implementing XMLIndexerDeserializable for any non-leaf node (e.g. <book> in the example above).

For leaf nodes (e.g. <title> in the example above), built-in converters support Int, Double, Float, Bool, and String values (both non- and -optional variants). Custom converters can be added by implementing XMLElementDeserializable.

For attributes (e.g. isbn= in the example above), built-in converters support the same types as above, and additional converters can be added by implementing XMLAttributeDeserializable.

Types conversion supports error handling, optionals and arrays. For more examples, look into SWXMLHashTests.swift or play with types conversion directly in the Swift playground.


Does SWXMLHash handle URLs for me?

No - SWXMLHash only handles parsing of XML. If you have a URL that has XML content on it, I'd recommend using a library like AlamoFire to download the content into a string and then parsing it.

Does SWXMLHash support writing XML content?

No, not at the moment - SWXMLHash only supports parsing XML (via indexing, deserialization, etc.).

I'm getting an "Ambiguous reference to member 'subscript'" when I call .value().

.value() is used for deserialization - you have to have something that implements XMLIndexerDeserializable (or XMLElementDeserializable if it is a single element versus a group of elements) and that can handle deserialization to the left-hand side of expression.

For example, given the following:

let dateValue: Date = try! xml["root"]["date"].value()

You'll get an error because there isn't any built-in deserializer for Date. See the above documentation on adding your own deserialization support. In this case, you would create your own XMLElementDeserializable implementation for Date. See above for an example of how to add your own Date deserialization support.

I'm getting an EXC_BAD_ACCESS (SIGSEGV) when I call parse()

Chances are very good that your XML content has what is called a "byte order mark" or BOM. SWXMLHash uses NSXMLParser for its parsing logic and there are issues with it and handling BOM characters. See issue #65 for more details. Others who have run into this problem have just stripped the BOM out of their content prior to parsing.

How do I handle deserialization with a class versus a struct (such as with NSDate)?

Using extensions on classes instead of structs can result in some odd catches that might give you a little trouble. For example, see this question on StackOverflow where someone was trying to write their own XMLElementDeserializable for NSDate which is a class and not a struct. The XMLElementDeserializable protocol expects a method that returns Self - this is the part that gets a little odd.

See below for the code snippet to get this to work and note in particular the private static func value<T>() -> T line - that is the key.

extension NSDate: XMLElementDeserializable {
    public static func deserialize(_ element: XMLElement) throws -> Self {
        guard let dateAsString = element.text else {
            throw XMLDeserializationError.nodeHasNoValue

        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
        let date = dateFormatter.dateFromString(dateAsString)

        guard let validDate = date else {
            throw XMLDeserializationError.typeConversionFailed(type: "Date", element: element)

        // NOTE THIS
        return value(validDate)

    // AND THIS
    private static func value<T>(date: NSDate) -> T {
        return date as! T

Have a different question?

Feel free to shoot me an email, post a question on StackOverflow, or open an issue if you think you've found a bug. I'm happy to try to help!


See CHANGELOG for a list of all changes and their corresponding versions.


See CONTRIBUTING for guidelines to contribute back to SWXMLHash.


SWXMLHash is released under the MIT license. See LICENSE for details.


Stars: 1141



Official/full Swift 5.1 compatibility - 2019-08-29 02:49:27

Released on 2019-08-28.

  • Added official/full Swift 5.1 compatibility (via #216)
    • Note that API compatibility hasn't changed... it is a major version upgrade to support new language features.

Added Xcode 10.2 and Swift 5.0 support - 2019-03-29 19:06:55

Released on 2019-03-29.

  • Added Xcode 10.2, Swift 5.0 support (via #210)

Added RawRepresentable String support - 2019-03-06 18:21:41

Released on 2019-03-06.

  • Added support for using String backed RawRepresentables in place of Strings for revelant APIs (via #204)

Fix - issue with subsequent lazy parsing calls - 2018-12-11 22:03:39

Released on 2018-12-11.

  • Fix issue with lazy parsing and multiple calls (via #204 and #203)

Fix - cast warning with Swift 4.2 on Linux - 2018-11-26 15:11:53

Released on 2018-11-26.

  • Fix cast warning with Swift 4.2 on Linux (via #201)

Changed - Added swift_version to podspec - 2018-11-26 15:11:06

Released on 2018-10-15.

  • Add swift_version to SWXMLHash.podspec (via #198)

Changed - WATCHOS_DEPLOYMENT_TARGET set to 3.0 - 2018-11-26 15:09:58

Released on 2018-09-18.

  • Bump WATCHOS_DEPLOYMENT_TARGET to 3.0 (via [#193 (https://github.com/drmohundro/SWXMLHash/pull/193))

Changed - bumped SWIFT_VERSION to 4.0 - 2018-08-04 19:54:30

Released on 2018-08-04.

  • Bump SWIFT_VERSION to 4.0 (via #191)

Feature - innerXML added - 2018-05-07 14:04:08

Released on 2018-05-07.

  • Added innerXML to elements (via #188 and #168)

Feature - XML parsing error checking - 2018-03-10 22:13:11

Released on 2018-03-10.

  • Added XML validation config option (via #169 and #141)

Changed - silenced Swift 4.1 deprecation warnings with shim - 2018-03-08 15:16:16

Released on 2018-03-08.

  • Swift 4.1 deprecation warnings silenced (via #181)

Changed - Filter is now filterAll and filterChildren - 2018-02-21 20:50:26

Released on 2018-02-21.

  • Changed filter support to filterAll and filterChildren (via #178 and #170)

Fix - issue with filter and deserialization - 2018-02-21 20:49:49

Released on 2018-02-05.

  • Fix issue with filter and deserialization (via #177)

Feature - filter support - 2018-02-04 21:12:34

Released on 2018-02-04.

  • Added filter support (via #171 and #167)

Fix - issue with CDATA parsing in Linux - 2018-01-21 17:46:36

Released on 2018-01-21.

Fixed CDATA parsing in Linux (via #171 and #167)

Feature - userInfo support - 2018-01-15 18:56:31

Released on 2018-01-15.

Added userInfo support (via #171 and #167).

Fix - issue with XMLIndexer.byKey(_:) - 2017-11-10 14:29:38

Released on 2017-11-10.

  • Fix XMLIndexer.byKey(_:) bug (fixed in #165)

Fix - characters deprecation - 2017-11-09 20:32:04

Released on 2017-11-09.

  • Fix characters deprecation (issue #163 fixed in #164)

Fix - off by one error in byIndex - 2017-09-28 14:53:05

Released on 2017-09-28.

  • Fix off by one byIndex error (issue #155 fixed in #156)

Bug Fix - Interleaved XML Bug - 2017-09-24 21:09:27

Released on 2015-02-26.

  • Fixed bug with interleaved XML (issue #19)

Bug Fix - Mixed Content Bug - 2017-09-24 21:08:43

Released on 2015-02-23.

  • Fixed bug where mixed content wasn't supported (i.e. elements with both text and child elements).

Bug Fix - Element Order Bug - 2017-09-24 21:07:59

Released on 2015-02-09.

  • Fixed bug with children so that XML element order is preserved when enumerating XML child elements.
  • Only require Foundation.h instead of UIKit.h.

Feature - OSX Support - 2017-09-24 19:39:55

Released on 2015-01-25.

  • Added OSX target, should allow SWXMLHash to work in OSX as well as iOS.

Initial CocoaPods Release - 2017-09-24 19:39:12

Released on 2015-01-30.

  • Added children property to allow for enumerating all child elements.
  • CocoaPods support is live (see current docset on CocoaPods)

1.0 Released - 2017-09-24 19:36:29

Released on 2015-04-10.

  • Lazy loading support is available (issue #11)
    • Call .lazy instead of .parse
    • Performance can be drastically improved when doing lazy parsing.
  • See PR #26 for details on these:
    • Remove automatic whitespace trimming - that will be a responsibility of the caller.
    • Make umbrella header public.
    • Introduce shared schemes.
  • Xcode 6.3 and Swift 1.2 support.
  • Published version 1.0.0 CocoaPod.

Feature - configure Method Added - 2017-09-24 19:35:09

Released on 2015-06-20.

  • Add configure method off of SWXMLHash to allow for setting variable number of options.
    • At this time, the only options are shouldProcessLazily and shouldProcessNamespaces.
    • shouldProcessLazily provides the same parsing as directly calling lazy. I'm considering deprecating the top-level lazy method in favor of having it be set in configure, but I'm open to suggestions here (as well as to suggestions regarding the configure method in general).
    • shouldProcessNamespaces provides the functionality requested in issue #30.

Bug Fix - Code Signing Options - 2017-09-24 19:33:46

Released on 2015-08-03.

  • Changed code signing options on the project to not code sign for OSX and to target iOS Developer.

Swift 2.0 Release - 2017-09-24 19:32:59

Released on 2015-09-16.

  • Added Swift 2.0 / Xcode 7.0 support
    • While API parity should exist between v1 and v2, the library attempts to support the new error handling support in Swift 2.0 when you call the byIndex/byKey methods respectively (the subscript methods don't currently support throwing exceptions).
    • Note that the existing subscript methods can still be used, though.
  • Changed .Error to .XMLError - this is part of handling Swift 2.0's new error handling support.
    • The prior .Error case received an NSError type whereas the new .XMLError case receives an Error which is an ErrorType with various cases to show which part of the parsing threw an error (i.e. Attribute, AttributeValue, Key, Index, or Init).

Feature - watchOS Support - 2017-09-24 19:31:30

Released on 2015-09-22.

  • Added watchos deployment target for CocoaPods and watchOS support

Feature - tvOS Support - 2017-09-24 19:30:45

Released on 2015-10-21.

  • Added tvOS deployment target for CocoaPods and tvOS support