RSS Parser reads RSS, Atom and RDF feeds from Kotlin on every target
Kotlin Multiplatform library for parsing RSS, Atom, and RDF feeds on Android, iOS, macOS, tvOS, watchOS, JVM, JS, and Wasm.
At a glance
- What is it?
- RSS Parser is a Kotlin Multiplatform library that turns a feed URL or an XML string into a typed channel object, and it runs on Android, the JVM, iOS, macOS, tvOS, watchOS, JavaScript and WebAssembly. It deliberately does one thing, parsing: caching was dropped in the 6.x line, and the builder pattern is where platform specific HTTP clients plug in.
- Who is it for?
- RSS Parser fits a Kotlin Multiplatform team that needs one feed parsing implementation shared between a mobile app, a desktop client and a web target, and it does not fit a project that wanted the library to handle caching, since the 6.x line dropped that and leaves storage to the caller, or a Java-only codebase that wants the old callback API.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One parser, eight targets, coroutines for the async part
RSS Parser is a Kotlin Multiplatform library for parsing RSS, Atom and RDF feeds, and the target list in the description is the reason to look at it: Android, iOS, macOS, tvOS, watchOS, JVM, JS and Wasm.
The library is deliberately narrow in purpose. The README says you can fetch useful information from any RSS channel, whether it is a blog, a magazine, a YouTube channel or a podcast feed, but the mechanism is one function that takes a feed and hands back a data object. It does not manage a subscription list, it does not poll on a schedule, and it does not store anything.
All the asynchronous work goes through Coroutines, which is stated early in the usage section and shapes the whole API. A caller suspends on the parsing call rather than receiving a callback, and the result is a typed channel object.
The entry point is an RssParser instance, and it can be created directly in common code with no platform specific arguments, which is what makes the library usable from a shared module:
val rssParser: RssParser = RssParser()From there, a feed by URL and a feed as a string are both one line:
val rssChannel: RssChannel = rssParser.getRssChannel("https://www.rssfeed.com")val xmlString: String = "xml-string"
val rssChannel: RssChannel = rssParser.parse(xmlString)The library is Apache-2.0 licensed and published to Maven Central, and the API badge shows 21 or newer for Android.
The group id changed, and the old coordinates will not resolve
There is an important notice at the top of the installation section, and it catches people out.
Since February 2022 the library artifacts have been moved to Maven Central, and the group id changed from com.prof.rssparser to com.prof18.rssparser. The README asks you to make sure the Gradle dependency is added to your root build.gradle file, and shows the snippet:
allprojects {
repositories {
mavenCentral()
}
}That root level repository block is not decoration. A multi-module project where the repository is declared only in the application module will fail to resolve the dependency from a library module, which is a common enough failure that the README calls it out separately from the ordinary installation steps.
The dependency itself is added to the common source set when the project is multiplatform:
commonMain {
dependencies {
// ...
implementation("com.prof18.rssparser:rssparser:<latest-version>")
}
}and in an Android-only project the same coordinate goes into a plain dependencies block. The version placeholder is left in the README rather than pinned, so the version you use is the one you choose from Maven Central.
One detail worth planning around: the library is published as a normal Maven artifact rather than with a Kotlin specific variant for each target, which is why a single coordinate in commonMain is enough. If your build has strict dependency verification or a version catalog, the coordinate is the string to add there.
From 6.1.0 an empty item is null, not an empty object
The breaking changes section leads with a change that is easy to overlook and expensive to debug in production, because it turns a null property access into a null object.
If all the fields of a given class are null, then the class itself is now null. Before, the class was populated with all the fields as null, which the README says is not very clear.
The affected classes are listed explicitly: RssItem, RssImage, RawEnclosure, YoutubeChannelData, YoutubeItemData, ItunesChannelData, ItunesItemData and ItunesOwner. That is the entire set of optional sub-objects, and the ones most likely to be null in practice are the iTunes blocks on a plain RSS feed and the YouTube blocks on a feed that is not a YouTube feed.
The practical effect is that code which assumed a non-null RssItem and then checked item.itunesData is now a null check on the item itself. The change makes the model honest, since a feed with no enclosure really has no enclosure rather than an enclosure object full of nulls, but it is a source incompatible change in a patch-level version number, so it deserves a careful look in your code when you upgrade from an earlier 6.1.x.
The rest of the breaking changes are larger and belong to the major version, which is the next thing to read.
Version 6 made it multiplatform, and took away caching and the Java callbacks
From version 6.0 RSSParser became multiplatform, and the update brought three breaking changes.
The first is mechanical: some class names and packages have changed, and the README points to the migration guide for the details. The version 5 README is kept on a separate branch, so the old API documentation is still reachable.
The second is a design decision with real consequences. Caching of feeds is not supported anymore. The README gives the reasoning: after some consideration, the author decided that the aim of the library is not to provide a caching solution for RSS feeds, but just to do the parsing, and the user of the library should implement any caching or storing solution their project requires.
That is a defensible boundary for a parsing library, and it also means the library will not give you stale content for free. If your application was relying on the parser to avoid refetching a feed, that behaviour is now yours to build, and the caching layer, its invalidation policy and its storage are all decisions you have to make.
The third change is the one that catches Java users. Support for Java with the OnTaskCompleted and onError callbacks has been dropped. You can still use the library in a Java project, but you will need to write some Kotlin code to handle the coroutines, and the README starts describing how to transform a coroutine into a CompletableFuture for that purpose.
So the multiplatform rewrite was paid for with a loss of ergonomics for the Java-only case, which is the trade a Kotlin-first library makes.
The model covers iTunes podcast metadata and YouTube feed fields
The data model is the substance of the library, and the available data section is a long, explicit list of what you get back.
The channel object provides the title, description and link, the list of items, an image with its own title, url, link and description, the last build date and the update period. Then come two vendor specific blocks. The iTunes data on a channel covers author, categories, duration, explicit, image, keywords, news feed URL, owner, subtitle, summary and type. The YouTube data covers the channel ID.
Items carry title, author, description, content, image, link, publication date, categories, audio, source with a name and a URL, the GUID, video, the comments URL, and the raw enclosure with its URL, length and type. The iTunes block on an item adds author, duration, episode, episode type, explicit, image, keywords, subtitle, summary and season, and the YouTube block adds video ID, video title, video URL, thumbnail URL, description, views count and likes count.
Two observations. First, the iTunes vocabulary is the Apple podcast extension, so a feed that carries it is a podcast feed and the fields populate; a plain blog feed leaves the iTunes block null, which is exactly the case the 6.1.0 nullability change is about. Second, the YouTube fields, including the counts, are not part of RSS or Atom, so they are populated only for feeds that publish them.
The list is a contract you can code against, which is the main reason to prefer a typed model over a generic XML tree.
The builder is where your HTTP client goes
The direct RssParser() constructor works everywhere, but each platform has an optional builder for supplying the HTTP stack, and the differences between them are the interesting part of the multiplatform design.
On Android and the JVM, a custom OkHttpClient instance and a custom Charset can be provided. If no OkHttpClient is supplied the library creates one, and if no Charset is provided it is automatically inferred from the feed XML. That last behaviour is quietly useful, because many feeds are served with a charset declared inside the document rather than in the HTTP headers, and a client that only trusts the headers will mis-decode them.
val builder = RssParserBuilder(
callFactory = OkHttpClient(),
charset = Charsets.UTF_8,
)
val rssParser = builder.build() On iOS, a custom NSURLSession instance can be provided, and if none is supplied the library uses the shared NSURLSession:
val builder = RssParserBuilder(
nsUrlSession = NSURLSession(),
)
val rssParser = builder.build() On the web, on JS and Wasm, a custom Ktor HttpClient instance can be provided, and one is created if it is not:
val builder = RssParserBuilder(
httpClient = HttpClient(),
)
val rssParser = builder.build()So the transport stack is different on every target, OkHttp on the JVM and Android, NSURLSession on Apple platforms and Ktor on the web, and the builder is the seam. The corollary is that shared code cannot assume a particular client, and if you need timeouts, retries, a proxy or an interceptor, you supply the client rather than configuring the parser.
Gradle, Kotlin JS and a Java sample that needs Kotlin
The repository shows what a Kotlin Multiplatform library looks like in practice.
The build is Gradle with Kotlin DSL, with a root build.gradle.kts, a settings.gradle.kts and a gradle.properties, and the Gradle wrapper checked in with both the Unix and Windows launcher scripts. The library module itself is the rssparser directory, and there is a samples directory with three projects in it: samples/android, samples/java and samples/multiplatform. The Java sample exists precisely because of the callback removal, so it shows the coroutine bridging you need from Java code.
The JavaScript side has its own tooling in the tree, a kotlin-js-store directory and a karma.config.d directory. The first is the Yarn lock store that Kotlin/JS uses to pin npm dependencies for the JS target, and the second is the Karma configuration used to run browser tests against the compiled JavaScript output. That is a lot of machinery for one target, and it is what makes the wasmJS and JS support real rather than theoretical.
Maintenance is regular. The published versions are 6.1.8 on 2026-07-30, 6.1.7 on 2026-07-07 and 6.1.6 on 2026-06-28, and the last push was on 2026-10-13. The repository is not archived, the default branch is master, and there is a renovate.json at the top level for dependency updates, which is the usual choice for a multi-module Gradle project that would otherwise accumulate a long tail of version bumps.
Editorial conclusion
RSS Parser fits a Kotlin Multiplatform team that needs one feed parsing implementation shared between a mobile app, a desktop client and a web target, and it does not fit a project that wanted the library to handle caching, since the 6.x line dropped that and leaves storage to the caller, or a Java-only codebase that wants the old callback API. Before you upgrade across the major version, read the migration guide, because class names and packages changed, and note that from 6.1.0 a channel or item object whose fields are all null now comes back as null rather than as an empty object. The project is Apache-2.0 licensed, release 6.1.8 was published on 2026-07-30, and the last push was on 2026-10-13.
Frequently asked questions
How do I add RSS Parser to a Kotlin Multiplatform project?
Add mavenCentral() to the repositories block, then add the dependency to the common source set, as implementation("com.prof18.rssparser:rssparser:<latest-version>") inside commonMain. The README also asks you to add the repository to your root build.gradle file, because a repository declared only in the application module will not resolve the dependency in a library module.
Which platforms does RSS Parser support?
The library is a Kotlin Multiplatform library for parsing RSS, Atom and RDF feeds, and it supports Android, JVM, iOS, macOS, tvOS, watchOS, wasmJS and JS. The README also describes a section on using the library in a Java project.
Does RSS Parser cache feeds?
No. Caching of feeds is not supported since version 6.0. The README explains that the aim of the library is to do the parsing rather than to provide a caching solution, and that the user should implement any caching or storing solution their project requires.
What changed in RSS Parser 6.1.0?
If all the fields of RssItem, RssImage, RawEnclosure, YoutubeChannelData, YoutubeItemData, ItunesChannelData, ItunesItemData or ItunesOwner are null, then the class itself is now null. Previously the class was populated with all its fields as null, which the README says is not very clear.
How do I supply my own HTTP client to RSS Parser?
Use the platform specific RssParserBuilder. On Android and the JVM you can pass a callFactory with an OkHttpClient and a charset, on iOS an nsUrlSession with an NSURLSession, and on JS and Wasm an httpClient with a Ktor HttpClient. If you do not supply one, the library creates a default, and the charset on the JVM is inferred from the feed XML when it is not given.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/prof18-rss-parser)Was this useful?