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
- SwiftUI for the UI. One codebase runs on iPhone and iPad, and the layout tools make a dashboard cheap to build.
- async/await + URLSession for networking. No third-party HTTP library; the standard library is plenty.
- Codable for JSON, plus a hand-rolled parser for the one CSV endpoint (below).
- Swift Charts for price history graphs. It ships with iOS 16+ and does line charts in a few lines.
- A tiny on-disk cache (just
Codablewritten to the caches directory). No database needed.
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.
-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.
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.