From 7642cbb5b78a30ed70736c47a48d62987fbb396e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 13 Dec 2025 23:37:02 +0000 Subject: [PATCH] iOS: show local IP in settings --- apps/ios/Sources/Settings/SettingsTab.swift | 54 ++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/apps/ios/Sources/Settings/SettingsTab.swift b/apps/ios/Sources/Settings/SettingsTab.swift index 5e7cc12272..fc6f54b5cb 100644 --- a/apps/ios/Sources/Settings/SettingsTab.swift +++ b/apps/ios/Sources/Settings/SettingsTab.swift @@ -20,6 +20,7 @@ struct SettingsTab: View { @StateObject private var connectStatus = ConnectStatusStore() @State private var connectingBridgeID: String? @State private var didAutoConnect = false + @State private var localIPAddress: String? var body: some View { NavigationStack { @@ -29,6 +30,16 @@ struct SettingsTab: View { Text(self.instanceId) .font(.footnote) .foregroundStyle(.secondary) + LabeledContent("IP", value: self.localIPAddress ?? "—") + .contextMenu { + if let ip = self.localIPAddress { + Button { + UIPasteboard.general.string = ip + } label: { + Label("Copy", systemImage: "doc.on.doc") + } + } + } } Section("Voice") { @@ -83,7 +94,10 @@ struct SettingsTab: View { .accessibilityLabel("Close") } } - .onAppear { self.discovery.start() } + .onAppear { + self.discovery.start() + self.localIPAddress = Self.primaryIPv4Address() + } .onDisappear { self.discovery.stop() } .onChange(of: self.discovery.bridges) { _, newValue in if self.didAutoConnect { return } @@ -235,4 +249,42 @@ struct SettingsTab: View { } return bridges.first } + + private static func primaryIPv4Address() -> String? { + var addrList: UnsafeMutablePointer? + guard getifaddrs(&addrList) == 0, let first = addrList else { return nil } + defer { freeifaddrs(addrList) } + + var fallback: String? + var en0: String? + + for ptr in sequence(first: first, next: { $0.pointee.ifa_next }) { + let flags = Int32(ptr.pointee.ifa_flags) + let isUp = (flags & IFF_UP) != 0 + let isLoopback = (flags & IFF_LOOPBACK) != 0 + let name = String(cString: ptr.pointee.ifa_name) + let family = ptr.pointee.ifa_addr.pointee.sa_family + if !isUp || isLoopback || family != UInt8(AF_INET) { continue } + + var addr = ptr.pointee.ifa_addr.pointee + var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + let result = getnameinfo( + &addr, + socklen_t(ptr.pointee.ifa_addr.pointee.sa_len), + &buffer, + socklen_t(buffer.count), + nil, + 0, + NI_NUMERICHOST) + guard result == 0 else { continue } + let len = buffer.prefix { $0 != 0 } + let bytes = len.map { UInt8(bitPattern: $0) } + guard let ip = String(bytes: bytes, encoding: .utf8) else { continue } + + if name == "en0" { en0 = ip; break } + if fallback == nil { fallback = ip } + } + + return en0 ?? fallback + } }