README.md
May 16, 2026 · View on GitHub
Object Parsing
Transform natural language strings into model objects
This package introduces a declarative approach to parsing natural language strings into Swift objects.
To use it, tag a model object with @ParsableObject and attempt to initialize an instance from a natural language string that contains its data:
import ObjectParsing
@ParsableObject
struct SalesRecord {
let quantity: Int
let date: Date
}
let record: SalesRecord = try "We sold 130 copies yesterday".parseObject()
Design
- ObjectParsing's API was inspired by the design of FoundationModels
@Generable- Although it uses a similar pattern, it is an order of magnitude faster, and is deterministic
- Typically tens to low-hundreds of microseconds per parse — fast enough to call on every keystroke
- Extensibility: declare your own string-based data types using string enums
Dependencies
This package depends on SoulverCore for parsing
- You can also use SoulverCore directly for parsing strings into model objects: just conform your model objects to the
ParsableObjectprotocol (a single initializer) - This package makes this a little easier, by adding the
@ParsableObjectmacro that generatesParsableObjectconformance for model objects automatically - It also includes handy higher-order primitive types (like places, flights, tags, etc.).
Limitations
- ObjectParsing is designed for structured parsing with known data types (dates, measurements, currencies, places, flight numbers, etc.) – anything that can be pre-declared at compile time or parsed deterministically by SoulverCore.
- If you need to extract data from strings based on their format, use regular expressions instead.
Requirements
- Xcode 16+
- Swift 6.2+
Installation
Add ObjectParsing to your project via the Swift Package Manager:
https://github.com/soulverteam/ObjectParsing
SoulverCore is included automatically as a dependency.
Usage
Tag a struct with @ParsableObject and then attempt to parse a new instance from a string using parseObject:
import ObjectParsing
@ParsableObject
struct Meeting {
let date: Date
let participantCount: Int?
}
let meeting: Meeting = try "tomorrow at 4pm, table for 5".parseObject()
// meeting.date → tomorrow at 4pm
// meeting.participantCount → 5
- Data is scanned in the order your properties are defined.
- Non-optional properties are required, and an error will be thrown if missing.
- Optional properties are set to
nilif not found in the input.
Unordered Parsing
Specify unordered in the macro to parse properties in any order:
import ObjectParsing
@ParsableObject(ordering: .unordered)
struct Meeting {
let date: Date
let participantCount: Int?
}
let meeting: Meeting = try "Table for 3, Thursday at 12:30".parseObject()
// meeting.date → Thursday at 12:30
// meeting.participantCount → 3
Nested Objects
The API is composable and supports nesting structures inside each other:
import ObjectParsing
@ParsableObject
struct Person {
let age: Int
let height: Double
let bankBalance: Decimal
let pace: Pace
}
@ParsableObject
struct Pace {
let distanceTravelled: Distance
let timeTaken: Time
}
let person: Person = try "I am 37 years old, 176 cm tall and have \$1000.00 in my bank account. I ran 650 metres, which took me 3 minutes 30 seconds".parseObject()
// Person(age: 37, height: 176.0, bankBalance: 1000, pace: Pace(distanceTravelled: 650.0 m, timeTaken: 3.5 min))
Custom Keywords
Define a String enum conforming to Keyword to create your own parsable types. Use aliases to declare synonyms (useful for singular/plural forms):
import ObjectParsing
enum BreakfastItem: String, Keyword {
case waffles
case eggs
var aliases: [String] {
switch self {
case .waffles: return ["waffle"]
case .eggs: return ["egg"]
}
}
}
@ParsableObject(keywordTypes: [BreakfastItem.self])
struct BreakfastOrder {
let quantity: Int?
let item: BreakfastItem
}
let order: BreakfastOrder = try "2 waffles".parseObject()
// order.quantity → 2
// order.item → .waffles
Keywords support multi-word phrases and are matched case-insensitively.
Array Parsing
Array properties will collect all matching values from the input:
import ObjectParsing
@ParsableObject
struct AverageList {
let numbers: [Int]
let average: Float
}
let list: AverageList = try "The average of 1, 2, and 4 is 2.3333333333".parseObject()
// list.numbers → [1, 2, 4]
// list.average → 2.3333333333
Parsable Data Types
ObjectParsing can parse a wide range of data types out-of-the-box.
Foundation Types
Int, Double, Decimal, Float, Bool, Date, DateInterval, URL
Measurement Types
Foundation's Measurement types (with convenient type aliases):
Distance, Length, Mass, Speed, Temperature, Area, Volume, Pressure, Energy, Power, Frequency, Angle, FileSize, Time
import ObjectParsing
@ParsableObject
struct RunLog {
var distance: Distance
var time: Time
}
let runLog: RunLog = try "I ran 5 km in 25 minutes".parseObject()
Higher-Order Primitives
| Type | Examples | Notes |
|---|---|---|
Money | "100 USD", "€200" | Amount, currency code & name |
Flight | "QF2", "qf 33" | Requires keywordTypes: [Airline.self] |
Airline | "Qantas", "QF" | 79 airlines with code & name aliases |
FlightDestination | "SYD", "London" | Resolves to airports with coordinates |
GeoSpot | "Paris", "Tokyo" | Name, coordinates & timezone |
IPAddress | "192.168.1.1", "8.8.8.8:53" | IPv4 with optional port |
DigitalContact | "john@example.com", "@handle" | Email or social handle |
TagSet | "#swift #ios" | Set of hashtag strings |
Timecode | "1:23:45", "45:30" | HH:MM:SS or MM:SS |
UnixTimestamp | "1633024800" | Auto-detects seconds vs milliseconds |
Percentage | "75%" | Decimal percentage value |
WeatherParameter | "temperature", "feels like" | 22 weather metrics |
Parsing Objects using only SoulverCore
You may prefer to skip this package and use SoulverCore directly to parse objects with ObjectParser. You won't get the convenience macro (@ParsableObject), but this avoids importing Swift Syntax — which is required by any package that uses macros.
Conform your types to ParsableObject and use ObjectParser directly:
import SoulverCore
struct SalesRecord: ParsableObject {
let quantity: Int
let date: Date
init(from parser: ObjectParser) throws {
self.quantity = try parser.scan(.number)
self.date = try parser.scan(.date)
}
}
let record: SalesRecord = try "We sold 130 copies yesterday".parseObject()
// record.quantity → 130
// record.date → yesterday's date
See the ObjectParser documentation for more details.
Sample App
A sample Mac app demonstrating how to parse natural language flight queries, weather queries, currency conversions, and time zones is included in the Demo App folder.
Related Libraries
- DateParsing — natural language date parsing with SoulverCore
- StringParsing — declarative data point extraction from strings with SoulverCore
Licence
SoulverCore is a commercially licensable, closed-source Swift framework. The standard licensing terms of SoulverCore do apply for its use in object parsing (see SoulverCore Licence). For personal (non-commercial) projects, you do not need a licence. So go ahead and use this great library in your personal projects!