← Blog Build guide · iOS & iPadOS · SwiftUI

Building ClogBook: an Old School RuneScape companion

Everything ClogBook shows (hiscores, live Grand Exchange prices, collection-log progress) comes from free public APIs. Here's how to wire them into a SwiftUI dashboard, and where the sharp edges are.

OSRS Updated 20 Jul 2026 ~10 min read See it on the App Store →

The idea

Old School RuneScape is a grind, and half the fun is watching numbers go up. But the official game gives you no good way to check your stats, item prices, or collection-log progress away from the client. That gap is the app: a phone dashboard that answers "how am I doing?" in one glance.

The good news for anyone building an OSRS tool: almost all the data you need is public and free. You don't need to scrape, and you don't need a backend at all for a first version. The phone can talk to the APIs directly.

The stack

That's the whole dependency list. The interesting part is the three data sources.

Data source 1: Hiscores (player stats)

Jagex exposes a "lite" hiscores endpoint that returns plain CSV. No key, no auth:

GET https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=Zezima

You get back one line per skill, then one per activity/boss, each as rank,level,xp (or rank,score for activities). The catch: there are no labels. The rows come back in a fixed order (Overall, Attack, Defence, Strength, Hitpoints, and so on), and you map them by index against a hard-coded list you keep in the app.

Sharp edge: that ordering changes whenever Jagex adds a new skill or boss, and new rows get inserted in the middle, not appended. Keep your skill/activity list in one file, treat parsing defensively (a missing or -1 row means "unranked"), and be ready to ship an update when the game adds content.

func parseHiscores(_ csv: String) -> [Skill] {
    let rows = csv.split(separator: "\n")
    return zip(skillOrder, rows).map { name, row in
        let cols = row.split(separator: ",").map { Int($0) ?? -1 }
        return Skill(name: name, rank: cols[0], level: cols[1], xp: cols[2])
    }
}

Data source 2: Grand Exchange prices (the OSRS Wiki API)

For item prices, the community-run OSRS Wiki real-time prices API is the gold standard. It's free, no key, and it's what most tools use:

GET https://prices.runescape.wiki/api/v1/osrs/latest      // current buy/sell per item id
GET https://prices.runescape.wiki/api/v1/osrs/mapping     // id → name, icon, GE limit
GET https://prices.runescape.wiki/api/v1/osrs/timeseries?id=4151×tep=1h

Fetch /mapping once to turn item IDs into names and icons, then /latest for live prices, and /timeseries to draw a Swift Chart of an item's price over time.

Do this or get blocked: the Wiki asks every client to send a descriptive User-Agent that identifies your app and a contact. Set it on every request. Cache the mapping data (it rarely changes) and don't poll prices more than once a minute. Being a good API citizen is the difference between a working app and a banned IP.

var req = URLRequest(url: url)
req.setValue("ClogBook/1.0 (iOS app; contact: dadmadeanapp@icloud.com)",
             forHTTPHeaderField: "User-Agent")

Data source 3: the collection log

This is the one Jagex does not expose. The workaround the whole community uses: the RuneLite Collection Log plugin, which lets players sync their log to collectionlog.net. That site has a public API:

GET https://api.collectionlog.net/collectionlog/user/{username}

So collection-log tracking in the app works for any player who has installed the plugin and opened their log in-game at least once. That's a real product constraint, not a bug, so surface it in the UI ("sync your log in RuneLite to see it here") instead of showing a confusing empty state.

Putting it together

The architecture is boring on purpose: one @Observable view-model per tab, each owning an async load() that hits its API, decodes, caches to disk, and publishes to the view. Show cached data instantly on launch, then refresh in the background. Boring is what survives a Jagex content update at 11pm.

Rule of thumb for game companion apps: the game's data is someone else's, and it will change without warning. Parse defensively, cache aggressively, and keep every "magic list" (skill order, item ids) in one file you can patch fast.

Want to build your own?

The recipe generalizes to almost any live-service game: find the community APIs (there's usually a wiki or a tracker with an open endpoint), respect their rate limits and User-Agent rules, and let the client do the work so you can ship without running a server. Start with the one screen you personally check most (for me that was GE prices) and grow from there.

ClogBook is on the App Store if you want to see the finished version.