Live data from Hacker News

Show HN: I Made an iOS Podcast Player with Racket

defn.io

81–90 of 91 posts

Re: Show HN: I Made an iOS Podcast Player with Racket

#81

Earlier quoted context omitted.

satoshis, derivative of bitcoin I think? I have a poor understanding of it. Essentially it's a way to pay podcast producers directly. Streaming means it sends whatever you set per minute so you auto-support podcasts. Boosts are short messages with dollar values tied to it. The show has to support this though, not all podcasts do.

So it's a podcast client with cryptocurrency wallet functionality?

Yeah, based on Bitcoin lightning (which is quite different from normal bitcoin) meaning no transaction costs and very low energy use. Basically it happens off-chain until someone syncs-up (then it becomes a btc transaction). It makes payments in fractions of cents possible, ie I stream 10 sats/min which is about 60 Eur cent per minute (so 1 cent per second).

You can also send messages to creators and attach amounts (those are called boosts), so you'd send a short message and attach 10k sats (or whatever you want), which is about 10 euros/dollars.

Some podcasts I listen to use this to read messages on the show (the highest ones). It's fun and casual but remains highly technical for now, https://getalby.com/ takes away some friction (but you're still self hosing a lightning wallet using docker compose). Easiest I think is Fountain [0, 1], which has it all built in. But I never tried that, I use Castamatic [2] with Alby.

I have to say, it seems to divide the Linux podcast world a bit with some shows embracing this, and some just calling it crypto-bs. A well, people seem to enjoy it and creators get real value, enough to have to rely less on ads even. I think this is one of the babies we'd throw out if we banned crypto currencies (which I agree are 99.999% bs).

[0] https://apps.apple.com/us/app/fountain-podcast-player/id1576...

[1] https://play.google.com/store/apps/details?id=fm.fountain.ap...

[2] https://apps.apple.com/us/app/castamatic-podcast-player/id96...

Re: Show HN: I Made an iOS Podcast Player with Racket

#82
post #76
post #75

Earlier quoted context omitted.

> Database management > syncing with the backend server Do features like these, when implemented in Racket, consume more resources (battery, CPU, etc.) than if they were implemented using the native API equivalents (e.g., NSURLSession or whichever is more applicable)? In the larger context of cross-platform apps with a common core written in a non-native programming stack, I often wonder this about network and disk I…

Ultimately, you end up calling some system API for I/O, so the only difference is how efficient the implementations of those Frameworks are compared to the embedded language's implementations. On iOS, embedding Racket requires using an interpreted mode (as opposed to Racket's usual native compilation mode), so there is a small hit, but it's not one that is really noticeable in battery or CPU consumption. In fact, Pod…

That makes sense. Do you keep the in-memory data in Racket data structures? I imagine keeping the data fed to Swift UI in sync with data maintained by GC'd Racket would involve some work.

Have you used https://github.com/Bogdanp/Noise? I assume serde would take up memory on both sides (Racket and Swift).

Sorry for a barrage of questions. I am quite curious about how all of this works. I am mulling over using OCaml for what you've used Racket for.

Re: Show HN: I Made an iOS Podcast Player with Racket

#83
post #82
post #76

Earlier quoted context omitted.

Ultimately, you end up calling some system API for I/O, so the only difference is how efficient the implementations of those Frameworks are compared to the embedded language's implementations. On iOS, embedding Racket requires using an interpreted mode (as opposed to Racket's usual native compilation mode), so there is a small hit, but it's not one that is really noticeable in battery or CPU consumption. In fact, Pod…

That makes sense. Do you keep the in-memory data in Racket data structures? I imagine keeping the data fed to Swift UI in sync with data maintained by GC'd Racket would involve some work. Have you used https://github.com/Bogdanp/Noise ? I assume serde would take up memory on both sides (Racket and Swift). Sorry for a barrage of questions. I am quite curious about how all of this works. I am mulling over using OCaml f…

No worries! I like talking about this stuff.

Yes, the app uses Noise under the hood, and the way to think about it is a request-response model[1]. Swift makes an async request to the Racket backend, it constructs some data (either by querying SQLite, or making a request to the backend, etc.) and returns a response. If it doesn't retain any of the data, then it gets GC'd. The ser/de is relatively low overhead -- if you try the app and go to Settings -> Support and take a look at the logs after using it a little, that should give you an idea of how long the requests take. The lines that start with `#` refer to Swift->Racket RPCs. Here's an example from my logs:

    2025-01-28 13:04:32 +0000 [io.defn.NoiseBackend.Backend] [debug] #006381: waitForAllDownloads()
    2025-01-28 13:04:32 +0000 [io.defn.NoiseBackend.Backend] [debug] #006381: took 319µs to fulfill
    2025-01-28 13:04:32 +0000 [io.defn.Podcatcher.AppDelegate] [debug] didBecomeActive: finished downloading pending episodes
    2025-01-28 13:04:33 +0000 [io.defn.NoiseBackend.Backend] [debug] #006382: getStats()
    2025-01-28 13:04:33 +0000 [io.defn.NoiseBackend.Backend] [debug] #006382: took 2ms to fulfill
Some things on the Racket side are long running, like the download manager. It's like an actor that keeps track of what's being downloaded and the progress of each download. Whenever a download makes progress, it notifies the Swift side by making a callback from Racket->Swift. In this example, there is some duplication since both the Swift and Racket sides each have a view of the same data, but it's negligible.

What's not as great from a memory use perspective is how large Racket's baseline memory use is. Loading the Racket runtime and all the app code takes up about 180MB of RAM, but then anything the app does is marginal on top of that (unless there's a bug, of course).

[1]: I did this precisely because, as you say, keeping keeping data in sync in memory between the two languages would be very hard, especially since the Racket GC is allowed to move values in memory. A value you grab at t0 might no longer be available at t1 if the Racket VM was given a chance to run between t0 and t1, so it's better to just let Racket run in its own thread and communicate with it via pipes. Probably, the same would be true for OCaml.

Re: Show HN: I Made an iOS Podcast Player with Racket

#84
post #83
post #82

Earlier quoted context omitted.

That makes sense. Do you keep the in-memory data in Racket data structures? I imagine keeping the data fed to Swift UI in sync with data maintained by GC'd Racket would involve some work. Have you used https://github.com/Bogdanp/Noise ? I assume serde would take up memory on both sides (Racket and Swift). Sorry for a barrage of questions. I am quite curious about how all of this works. I am mulling over using OCaml f…

No worries! I like talking about this stuff. Yes, the app uses Noise under the hood, and the way to think about it is a request-response model[1]. Swift makes an async request to the Racket backend, it constructs some data (either by querying SQLite, or making a request to the backend, etc.) and returns a response. If it doesn't retain any of the data, then it gets GC'd. The ser/de is relatively low overhead -- if yo…

Thank you!

In my PoC (https://github.com/jbhoot/poc-ocaml-logic-native-ui) - a tiny hello world CLI on macOS, that has a Swift "frontend" and OCaml "backend" - I followed a similar model:

- Both sides pass messages to each other. Both of them talk through C ABI. My model was synchronous though. Async is certainly better. - I used the protobuf binary protocol for message passing. Faster and probably more efficient than, say, JSON. But both sides may have copies of the same data for this reason.

I've written down my approach, which roughly aligns with yours, in the project's README.

What I wanted to do was for OCaml side to allocate data in memory, and for Swift to access the same memory through some commonly agreed upon protocol (protobuf itself maybe?). But I haven't yet explored how difficult this could be with GC coming in play. I think OCaml does have a few tricks to tell GC to not collect objects being shared through C ABI (https://ocaml.org/manual/5.3/intfc.html#s:c-gc-harmony), but I haven't looked into this enough to be sure.

Your projects will sure help me figure out the concepts!

Re: Show HN: I Made an iOS Podcast Player with Racket

#85
post #84
post #83

Earlier quoted context omitted.

No worries! I like talking about this stuff. Yes, the app uses Noise under the hood, and the way to think about it is a request-response model[1]. Swift makes an async request to the Racket backend, it constructs some data (either by querying SQLite, or making a request to the backend, etc.) and returns a response. If it doesn't retain any of the data, then it gets GC'd. The ser/de is relatively low overhead -- if yo…

Thank you! In my PoC ( https://github.com/jbhoot/poc-ocaml-logic-native-ui ) - a tiny hello world CLI on macOS, that has a Swift "frontend" and OCaml "backend" - I followed a similar model: - Both sides pass messages to each other. Both of them talk through C ABI. My model was synchronous though. Async is certainly better. - I used the protobuf binary protocol for message passing. Faster and probably more efficient t…

Nice! Yeah, using protobufs seems reasonable. Re. GC, Racket has support for "freezing" values in place to prevent the GC from moving them, but freezing too many values can impact the GC's operation so I'd watch out for that if that's possible in OCaml.

Re: Show HN: I Made an iOS Podcast Player with Racket

#86
post #43

Bug report: go to Discover, serach for Mike Duncan, tap on Revolutions, exception pops up

Thanks! I'm not able to reproduce this on my end, but if you send me the exception (here or via e-mail), I'd be happy to take another look!

Hmmm it didn’t work yesterday but I didn’t take a screenshot. Works today! Guess it was a network issue

Re: Show HN: I Made an iOS Podcast Player with Racket

#88
post #40

The app looks really good! Based on the title I thought it’d be something you made most as a testbed for Racket so I was surprised to see the app itself actually looks great :D I tried looking through your blog but couldn’t find anything except the 40 minute YouTube video for your other app. It sounds like both the UI and the audio-related code are in Swift? What code ends up actually being in Racket then?

That's right, the UI and the Audio Engine bits are in Swift, because it's easier to interface with those Frameworks directly from Swift (and not fight the platform). Everything else (the Database management & the models, the download manager, ID3 parsing, parsing release notes, syncing with the backend server, etc.) is implemented in Racket and is portable.

Thanks for the reply! That makes a lot of sense.

Re: Show HN: I Made an iOS Podcast Player with Racket

#89
post #85
post #84

Earlier quoted context omitted.

Thank you! In my PoC ( https://github.com/jbhoot/poc-ocaml-logic-native-ui ) - a tiny hello world CLI on macOS, that has a Swift "frontend" and OCaml "backend" - I followed a similar model: - Both sides pass messages to each other. Both of them talk through C ABI. My model was synchronous though. Async is certainly better. - I used the protobuf binary protocol for message passing. Faster and probably more efficient t…

Nice! Yeah, using protobufs seems reasonable. Re. GC, Racket has support for "freezing" values in place to prevent the GC from moving them, but freezing too many values can impact the GC's operation so I'd watch out for that if that's possible in OCaml.

Makes sense. Thanks for the tip!

Re: Show HN: I Made an iOS Podcast Player with Racket

#90
post #71

Doesn't work! Clicking discover just shows a messagebox with a stack trace[1] - so I have no way of adding a podcast to try out the app. [1] Error string::1: bytes->jsexpr: bad input starting #"error code: 502" context..: .../syntax/readerr.rkt:15:2: -raise- read-error ../private/arrow-val-first.rkt:486:18 .../private/backend.rkt:45:9: get-trending-podcasts .../noise-serde-lib/backend.rkt:69:22

Sorry about that! Looks like the server got OOMKilled at one point and failed to recover. It should be back up now and I'll work on a fix.

oh cool! It works now :) Nice work
Post reply on HN