# tadija/AEXML: a small Swift XML utility that tells you what it is not

> AEXML is an MIT licensed Swift library for reading and writing XML with plain arrays as the data model, subscripting by element name and building documents with a closure based builder. Its README is unusually clear that it is not a full featured parser but a simple, lightweight utility for casual XML handling, which makes it a good fit for a SOAP request and a bad fit for a document format you did not write.

**tadija/AEXML** — Swift minion for simple and lightweight XML parsing

- Repository: https://github.com/tadija/AEXML
- Stars: 1,036 · Forks: 202
- Language: Swift
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/tadija-aexml

## The admission in the first paragraph

AEXML describes itself as a Swift minion for simple and lightweight XML parsing, and the author says plainly that he made it for personal use but that you are free to use it or contribute.

The Intro says what it is not. This is not a full featured XML parser, but rather simple, lightweight and easy to use utility for casual XML handling.

That sentence is the most useful thing in the repository, because it tells you when to reach for it and when to walk away. A SOAP request, a small configuration file, a feed you control: those are the cases. An arbitrary document format from a third party, with entities, mixed content, a DTD, an unusual namespace scheme and a schema you have to satisfy: those are not.

The feature list is correspondingly short, and it is a complete description of the library's scope. It reads XML data, it writes an XML string, it is covered with unit tests, and it is covered with inline docs. Four items, no filler, and the tests and the inline documentation are the two claims that make a small library worth trusting, because they are the two that cost the author time rather than appearing for free.

## Reading is subscripting by element name

The usage example starts from an XML string and shows the whole surface of the reader, and the shape of it is what makes the library pleasant.

```swift
let xmlDoc = try AEXMLDocument(xml: data, options: options)

// prints the same XML structure as original
print(xmlDoc.xml)

// prints cats, dogs
for child in xmlDoc.root.children {
    print(child.name)
}

// prints Optional("Tinna") (first element)
print(xmlDoc.root["cats"]["cat"].value)

// prints Tinna (first element)
print(xmlDoc.root["cats"]["cat"].string)

// prints Tinna, Rose, Caesar
if let cats = xmlDoc.root["cats"]["cat"].all {
    for cat in cats {
        if let name = cat.value {
            print(name)
        }
    }
}
```

So the document is a subscriptable tree, `xmlDoc.root["cats"]["cat"]` walks down by element name, and the leaf accessors are `.value` for the optional text and `.string` for the same thing unwrapped.

There are also the aggregate accessors, `.all` for every match, `.all(withValue:)` to filter by text, `.all(withAttributes:)` to filter by attribute values, `.count` for the number of matches, and `.attributes["breed"]` for attribute lookup. The last example in the read section shows the error path, since `xmlDoc["NotExistingElement"].error` prints an optional wrapping the `elementNotFound` case of the library's own error enum, so a missing element is a value you can inspect rather than a crash.

That is the whole reader, and it is a complete reader for the documents it is aimed at.

## The optionals are the API's honesty

One detail in that example deserves a closer look, because it is a design decision rather than an implementation detail.

`xmlDoc.root["cats"]["cat"].value` returns an optional, while `.string` returns the unwrapped text. So the library distinguishes between the element being absent and the element being present with no content, which is exactly the distinction a parser of small XML documents has to get right. An element that exists but is empty, as in the example document where one cat has no content and no attributes, is not the same as an element that is not there at all.

The same care appears in the collections. `all` is an optional array, so a failed lookup gives you nil rather than an empty array that you might mistake for no matches, and `all(withValue:)` and `all(withAttributes:)` are optional for the same reason, and the attribute accessors in the example use force unwrapping in one place and optional binding in another, which is the difference between a programmer who knows the document and one who is being careful.

The write side returns a compact serialisation too, with `xmlCompact` printing a single line element, which is the form you want when you are putting a fragment into a larger document.

## Writing, and the closure based builder

The write example starts from a SOAP request that you could obviously build as a string, and the author's point is that you can also do it in a more structured and elegant way.

```swift
// create XML Document
let soapRequest = AEXMLDocument()
let attributes = ["xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance", "xmlns:xsd" : "http://www.w3.org/2001/XMLSchema"]
let envelope = soapRequest.addChild(name: "soap:Envelope", attributes: attributes)
let header = envelope.addChild(name: "soap:Header")
let body = envelope.addChild(name: "soap:Body")
header.addChild(name: "m:Trans", value: "234", attributes: ["xmlns:m" : "http://www.w3schools.com/transaction/", "soap:mustUnderstand" : "1"])
let getStockPrice = body.addChild(name: "m:GetStockPrice")
getStockPrice.addChild(name: "m:StockName", value: "AAPL")

// prints the same XML structure as original
print(soapRequest.xml)
```

The structure mirrors the document, which is the point. A builder that returns the element it created, so you can keep descending, reads as a description of the tree rather than a sequence of string operations.

There is a second form, using result builders, which the README points at a pull request for. A function marked with the document builder attribute returns a document, and the elements are nested in closures:

```swift
@AEXMLDocumentBuilder
private func buildSoapEnvelope(
    for action: String,
    in serviceType: String,
    with parameters: [String: String] = [:]
) -> AEXMLDocument {
    AEXMLElement("s:Envelope", attributes: [
        "xmlns:s": "http://schemas.xmlsoap.org/soap/envelope/",
        "s:encodingStyle": "http://schemas.xmlsoap.org/soap/encoding/"
    ]) {
        AEXMLElement("s:Body") {
            AEXMLElement("s:\(action)", attributes: [
                "xmlns:u": serviceType
            ]) {
                for parameter in parameters {
                    AEXMLElement(
                        name: parameter.key,
                        value: parameter.value
                    )
                }
            }
        }
    }
}
```

That is a Swift result builder, so the nesting of the closures is checked by the compiler against the structure of the document, which is a real improvement over a method chain when the document is deeply nested.

## Three package managers, one library

The installation section covers the three ways the library is distributed, which is the older and more complete set for a Swift library, and each block is short.

For Swift Package Manager:

```swift
.package(url: "https://github.com/tadija/AEXML.git", from: "4.7.3")
```

For Carthage:

```ogdl
github "tadija/AEXML"
```

And for CocoaPods:

```ruby
pod 'AEXML'
```

The repository carries the artefacts for all three, with a `Package.swift` at the top level, an `AEXML.podspec`, and an `AEXML.xcodeproj/` directory.

The badges give the compatibility picture. Swift 5.9, the platforms iOS, watchOS, tvOS and macOS, and the package is marked compatible with CocoaPods, Carthage and Swift Package Manager. The watchOS and tvOS entries matter more than they look for a library like this, because an XML parser in a watch face or a TV app is a fairly common need and there are not many small libraries that support those platforms.

The tree also has a `Sources/` directory and a `Tests/` directory that the README points to for more examples, an `Example/` directory with an Xcode demo project, a `Resources/` directory, and a `CHANGELOG.md`.

## Where this is the wrong choice

The first wrong case is a document format you do not control. The library is explicitly a utility for casual XML handling, and the documentation does not describe entity handling, DTD support, mixed content models or a configurable parser, so a format with any of those is a poor fit.

The second is input you do not trust. A lightweight parser that does not describe its limits is not one to point at XML from an untrusted source, and the absence of any mention of entity expansion in the documentation is not a guarantee that the library is safe there.

The third is a project that needs XPath. The accessors are by element name, with filters on value and attributes, and there is no path language in the feature list, so a query like everything below a particular path is not something the library gives you.

The fourth is a large document. The model is a tree of element objects in memory, which is fine for the SOAP request this was written for and is not a streaming parser.

On the state of the project, the repository publishes no GitHub releases, and the version you get from Swift Package Manager is 4.7.3, so the `from` constraint in the manifest is the number to look at rather than a tag list. The `CHANGELOG.md` in the tree is the version history, and the last push to the repository was on 2026-10-15 with the repository not archived, so the work continues even though the release tagging is informal. The licence is MIT, with the licence file at the root, which for a library you might vendor into a commercial app is about as uncomplicated as it gets.

## Conclusion

Adopt AEXML if you need to read or write a small, well formed XML document in a Swift application, such as a SOAP request or a simple configuration file, since the API is readable, the types are honest about what can be missing, and the package installs with no dependencies. Do not adopt it as the parser for an XML format whose complexity you do not control, because the project describes itself as a utility for casual XML handling rather than a full featured parser, and the documentation does not describe entity handling, namespaces beyond literal names, or limits. Verify first that your documents do not rely on behaviour the library does not implement, that your Swift toolchain is 5.9 or newer on one of the Apple platforms it supports, and that the version you pin through Swift Package Manager matches the source you read, since the package is tagged 4.7.3 while the repository publishes no GitHub releases.

## FAQ

### What does AEXML do in Swift?

It reads XML data and writes XML strings. A document is parsed into a subscriptable tree, you walk it by element name with accessors for the value, the text, the attributes, the count and the matches filtered by value or attribute, and you build a document by adding children, either with a method chain or with the closure based result builder.

### Is AEXML a full featured XML parser?

No, and the README says so directly. It is not a full featured XML parser, but rather a simple, lightweight and easy to use utility for casual XML handling, written by the author for personal use.

### How do I install AEXML?

Through Swift Package Manager with `.package(url: "https://github.com/tadija/AEXML.git", from: "4.7.3")`, through Carthage with `github "tadija/AEXML"`, or through CocoaPods with `pod 'AEXML'`. The library needs Swift 5.9 and supports iOS, watchOS, tvOS and macOS.

### How do I find elements and their attributes in AEXML?

By subscripting the tree with the element name, for example `xmlDoc.root["cats"]["cat"]`. The leaf accessors give you the optional text with `value` and the unwrapped text with `string`, the attributes come from `attributes["breed"]`, and you can collect matches with `all`, `all(withValue:)` and `all(withAttributes:)`, and count them with `count`.

### How do I write XML with AEXML?

Create an `AEXMLDocument` and add children with `addChild(name:attributes:)` and `addChild(name:value:attributes:)`, where each call returns the child it created so you can keep descending. There is also a result builder form marked with the document builder attribute, which turns the document into nested closures that the compiler checks against the structure.

## Sources

- [Issues](https://github.com/tadija/AEXML/issues)
- [License: MIT](https://github.com/tadija/AEXML/blob/master/LICENSE)
- [README](https://github.com/tadija/AEXML/blob/master/README.md)
- [tadija/AEXML on GitHub](https://github.com/tadija/AEXML)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tadija-aexml
