I never went to school for this. No bootcamp. No senior engineer mentorship. Just curiosity, late nights, and the stubborn belief that I could ship something people would actually pay for. Three months after launch, Pwnagotchi Companion hit $1,700+ CAD in revenue and 300+ paid downloads with a 5.0-star rating. Here's how I did it — and what I learned along the way.

The Idea: Solving a Real Problem

It started with Pwnagotchi — an open-source Wi-Fi auditing device built on Raspberry Pi. The community was growing, but the device itself had no official interface. Users had to SSH into a command line or jury-rig third-party tools just to see their handshake counts.

That was the problem: there was no user-friendly iOS app. No way to check your stats on the go, no remote control, no visual dashboard. Everyone was asking for one. So I decided to build it.

THE GOAL
Build a native iOS app that connects to a Pwnagotchi via WebSocket, displays real-time stats, and lets users control the device from their phone — all in a polished SwiftUI interface that wouldn't feel like a hack.

Architecture: SwiftUI + WebSocket + Bluetooth

I chose SwiftUI because it was the future of iOS development, and honestly, I wanted to learn it properly. That meant dealing with its state management quirks and ObservableObject proliferation, but the result was clean, declarative code that was easy to iterate on.

The app architecture broke into three main pieces:

  • WebSocket client for real-time data sync with the Pwnagotchi's Python backend plugin
  • Core Bluetooth to send commands directly to the Raspberry Pi Zero W (no internet required)
  • SwiftData (previously CoreData) for local caching of stats and handshake history

The WebSocket layer was the trickiest. The Pwnagotchi's existing plugin was written in Python and spitting out JSON blobs every second. I needed to parse that in real-time, update the UI without blocking the main thread, and handle reconnections gracefully when the Pi lost WiFi.

// Simplified WebSocket manager
@MainActor
class WebSocketManager: ObservableObject {
    @Published var handshakes: Int = 0
    @Published var isConnected: Bool = false

    private var webSocketTask: URLSessionWebSocketTask?
    private let url: URL

    func connect() {
        webSocketTask = URLSession.shared.webSocketTask(with: url)
        webSocketTask?.resume()
        listen()
    }

    private func listen() {
        webSocketTask?.receive { [weak self] result in
            switch result {
            case .success(.string(let json)):
                self?.handleIncoming(json)
                self?.listen() // Keep listening
            case .success(.data):
                self?.listen()
            case .failure:
                DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                    self?.connect() // Reconnect
                }
            }
        }
    }
}

That listen() recursion took me a while to get right — initial implementations kept dropping connections or piling up memory. The fix was making sure every receive call immediately schedules the next one.

The App Store Gauntlet

Submitting to the App Store was my first major stress test. I'd never done this before, and there's a lot of unwritten rules that don't show up in Apple's guidelines.

Here's what tripped me up:

  • Cryptocurrency miners: The Pwnagotchi ecosystem overlaps with crypto. I had to explicitly state in metadata and binary that the app does not mine, harvest, or transact cryptocurrency.
  • Hacking tools clause: Apple's 5.1.1 clause worries about "exactly accurate" hacking tool representations. I made sure the app was marketed as a companion for a device you already own, not a tool for unauthorized network access.
  • Bluetooth permissions: Apple wants to know exactly what you're doing with Bluetooth. I added NSBluetoothAlwaysUsageDescription and explained the device control use case clearly.

It took two rejections and one expedited appeal (via Apple's "App Review Board") before it got approved. The first rejection was vague. The second cited the hacking tools clause. I wrote a detailed appeal explaining what Pwnagotchi actually is (a learning platform for Wi-Fi security) and how my app just provides a UI for it. That did it.

APPEAL EMAIL EXCERPT
"Pwnagotchi is an educational tool used to learn Wi-Fi security in controlled environments (like home networks you own). The app is purely a companion interface — it does not enable unauthorized access to any network. All functionality requires physical possession of the hardware device."

Revenue & Reality Check

I priced it at $7.99 CAD. Not too cheap (devalues the work), not too expensive (limits adoption). Here's the actual numbers, tracked manually in a Google Sheet:

METRIC NUMBER
Paid Downloads 300+
Revenue (gross) $2,200+ CAD
App Store Rating 5.0★ (50+ ratings)
Time to first $100 4 days

That's not quit-your-job money, but for a niche hardware companion app? I'll take it. More importantly, it proved that I could ship something that strangers voluntarily paid for.

The 5.0-star rating was the real win. I replied to every single review — good or bad — and within two weeks of launch I'd fixed the three most-requested features. That visibility helped; new users saw "recently updated" and "developer responds" and felt confident buying.

Customer Support As a Feature

My support setup was primitive: $HOME/support/ folder with markdown files. But I responded within hours, not days. One user had a PyNumLib compatibility issue — I shipped a fix in 8 hours. Another wanted dark mode — added it in 3 days.

Shipping fast, communicating clearly, and actually listening turned users into advocates. Several brought friends into the Discord channel to thank me. That social proof mattered more than any ad spend.

What I'd Do Differently

Hindsight's 20/20. If I were doing it again:

  • Build an email list first — I announced on GitHub Issues and Discord, but a simple waitlist would have given me a launch-day boost.
  • Better screenshots — my initial screenshots were meh. Professional mockups would convert better.
  • App Store Optimization (ASO) — I didn't really understand keyword research until after launch. Did a huge disservice to discovery.

The Real Takeaway

The code was the easy part. The hard part was believing I could ship at all. The App Store review. The pricing decisions. The support messages at 11 PM. The fear that no one would buy it.

But I did it. And you can too. You don't need permission. You don't need a degree. You just need to build something real, solve an actual problem, and ship it.

FOUND THIS HELPFUL?
I write about iOS dev, full-stack engineering, and shipping real products. Follow along or hire me for your next project.