Welcome back! In the previous chapter, we built the macOS Node. We gave OpenClaw "hands" to control your desktop computer.
But there is a limitation: desktops don't move. If you are in the kitchen, in the garden, or on the bus, you can't talk to your computer.
In this chapter, we are going to build the iOS Node. This turns your iPhone or iPad into a portable remote control for your entire OpenClaw system.
The Gateway is the brain, and it usually stays in one place (on a server or your home computer). The iOS Node is a "satellite" device. It allows you to send commands to the brain, and receive notifications from the brain, no matter where you are standing in your house.
The Central Use Case: You are sitting on your couch. You realize you forgot to start a data processing script on your computer. instead of walking to your desk, you open the OpenClaw app on your phone, tap "Start Script," and the phone sends the signal to the Gateway to do the work.
The iOS Node is located in apps/ios/. It shares many similarities with the macOS Node, but with a few mobile-specific twists.
We use Apple's modern toolkit to build the buttons and text on the screen. It is declarative, meaning we just describe what the screen should look like, and the phone draws it.
This is the most important concept for mobile development. On your computer, localhost means "this computer." But on your phone, localhost means "the phone itself."
To talk to the Gateway, the phone must use your computer's Local IP Address (like 192.168.1.5), not localhost.
Unlike a computer, phones aggressively sleep apps to save battery. The iOS Node needs to handle connecting quickly when you open the app and disconnecting gracefully when you lock the screen.
To run this, you need a Mac with Xcode installed. You can run the app on a real iPhone or the virtual Simulator on your screen.
Navigate to the iOS folder.
# In your terminal or Finder
open apps/ios/OpenClaw.xcodeproj
We need to tell the iPhone where the Gateway lives. Remember, we cannot use localhost here!
Find your computer's IP address (System Settings -> Wi-Fi -> Details). Let's assume it is 192.168.1.5.
Open the file Config.swift in Xcode:
struct Config {
// REPLACE 'localhost' with your computer's IP!
// Example: "ws://192.168.1.5:8080"
static let gatewayUrl = "ws://192.168.1.5:8080"
}
What happens when you tap a button on the screen? It's a journey from your finger to the server.
Here is the sequence of events when you use the app as a remote.
The iOS app is built using SwiftUI for the visuals and URLSession for the networking.
1. The Visuals (ContentView.swift):
This file defines what you see. We create a simple button that triggers an action.
import SwiftUI
struct ContentView: View {
// We connect this view to our logic engine
@ObservedObject var client = WebSocketClient()
var body: some View {
VStack {
Text("Status: \(client.status)")
// A button to send a command
Button("Run Script") {
client.sendMessage("START_SCRIPT")
}
}
}
}
Explanation:
VStack: Arranges items vertically (Status on top, Button below).Text(...): Shows if we are connected or offline.Button(...): When tapped, it calls sendMessage on our client.
2. The Logic (WebSocketClient.swift):
This is the engine that actually talks to the Gateway.
import Foundation
class WebSocketClient: ObservableObject {
var webSocket: URLSessionWebSocketTask?
@Published var status = "Disconnected"
func connect() {
// Use the IP address from Config
let url = URL(string: Config.gatewayUrl)!
webSocket = URLSession.shared.webSocketTask(with: url)
webSocket?.resume() // Turn it on
self.status = "Connected"
}
}
Explanation:
ObservableObject: This allows the UI to update automatically when the status changes.URLSession: The standard Apple way to handle networking.resume(): Starts the connection.3. Sending a Message: When the user taps the button, we need to send data.
func sendMessage(_ text: String) {
// Create a text message object
let message = URLSessionWebSocketTask.Message.string(text)
// Send it through the pipe
webSocket?.send(message) { error in
if let error = error {
print("Sending failed: \(error)")
}
}
}
Explanation:
Message object.send() fires it off to the Gateway.In this chapter, we expanded our network to mobile devices.
apps/ios/.localhost.Now you can control your OpenClaw system from your pocket! But what if you don't have an iPhone? What if you use the other major mobile operating system?
Generated by Code IQ