libp2p / libp2p/rust-libp2p

build(ios): error on iOS with libp2p 0.56 using quic feature and if-watch 3.2.1

Open
#6,161 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
5.6k
Forks
1.3k
Avg merge
8h 47m
Merged PRs (30d)
19

Description

Hello,

I tried to build an application with the latest libp2p version 0.56 including quic features on iOS, however, I have the below compiler errors, which seem to be related to the issue #5628 and #5679. I see that if-watch v3.2.1 was used in the compilation.

Undefined symbols for architecture arm64:
  "_SCDynamicStoreCreateRunLoopSource", referenced from:
      system_configuration::dynamic_store::SCDynamicStore::create_run_loop_source::hc606d8191d1eee96 in libapp.a[1330](system_configuration-ef8f09e6c16c7e34.system_configuration.a183a244f0552131-cgu.0.rcgu.o)
  "_SCDynamicStoreCreateWithOptions", referenced from:
      system_configuration::dynamic_store::SCDynamicStore::create::h7b8d644247fa5cbe in libapp.a[1330](system_configuration-ef8f09e6c16c7e34.system_configuration.a183a244f0552131-cgu.0.rcgu.o)
  "_SCDynamicStoreSetNotificationKeys", referenced from:
      std::sys::backtrace::__rust_begin_short_backtrace::h50d2f1a25ee701a8 in libapp.a[1329](if_watch-7a230dd48f870c3b.if_watch.56977769d1be1092-cgu.0.rcgu.o)
  "_kSCDynamicStoreUseSessionKeys", referenced from:
      system_configuration::dynamic_store::SCDynamicStoreBuilder$LT$T$GT$::create_store_options::h88a4075b4fa8049f in libapp.a[1329](if_watch-7a230dd48f870c3b.if_watch.56977769d1be1092-cgu.0.rcgu.o)

I am not experienced with iOS internals, but from the errors above I suspect that i.e. SCDynamicStore does not exist in the iOS world.

I checked with Claude LLM the apple.rs module in if-watch and it suggested to patch it this way (if it makes sense), practically using the fallback polling on iOS which is also described in the if-watch README.md file.

--- a/src/apple.rs
+++ b/src/apple.rs
@@ -1,11 +1,17 @@
 use crate::{IfEvent, IpNet, Ipv4Net, Ipv6Net};
+#[cfg(target_os = "macos")]
 use core_foundation::array::CFArray;
+#[cfg(target_os = "macos")]
 use core_foundation::runloop::{kCFRunLoopCommonModes, CFRunLoop};
+#[cfg(target_os = "macos")]
 use core_foundation::string::CFString;
 use fnv::FnvHashSet;
 use futures::channel::mpsc;
 use futures::stream::{FusedStream, Stream};
 use if_addrs::IfAddr;
 use std::collections::VecDeque;
 use std::io::Result;
 use std::pin::Pin;
 use std::task::{Context, Poll};
+#[cfg(target_os = "macos")]
 use system_configuration::dynamic_store::{
     SCDynamicStore, SCDynamicStoreBuilder, SCDynamicStoreCallBackContext,
 };
@@ -40,7 +46,12 @@ pub struct IfWatcher {
 impl IfWatcher {
     pub fn new() -> Result<Self> {
         let (tx, rx) = mpsc::channel(1);
-        std::thread::spawn(|| background_task(tx));
+        
+        #[cfg(target_os = "macos")]
+        std::thread::spawn(move || background_task_macos(tx));
+        
+        #[cfg(not(target_os = "macos"))]
+        std::thread::spawn(move || background_task_ios(tx));
+        
         let mut watcher = Self {
             addrs: Default::default(),
             queue: Default::default(),
@@ -119,6 +130,7 @@ fn ifaddr_to_ipnet(addr: IfAddr) -> IpNet {
     }
 }
 
+#[cfg(target_os = "macos")]
 fn callback(_store: SCDynamicStore, _changed_keys: CFArray<CFString>, info: &mut mpsc::Sender<()>) {
     match info.try_send(()) {
         Err(err) if err.is_disconnected() => CFRunLoop::get_current().stop(),
@@ -126,7 +138,8 @@ fn callback(_store: SCDynamicStore, _changed_keys: CFArray<CFString>, info: &mu
     }
 }
 
-fn background_task(tx: mpsc::Sender<()>) {
+#[cfg(target_os = "macos")]
+fn background_task_macos(tx: mpsc::Sender<()>) {
     let store = SCDynamicStoreBuilder::new("global-network-watcher")
         .callback_context(SCDynamicStoreCallBackContext {
             callout: callback,
@@ -140,4 +153,24 @@ fn background_task(tx: mpsc::Sender<()>) {
     let source = store.create_run_loop_source();
     let run_loop = CFRunLoop::get_current();
     run_loop.add_source(&source, unsafe { kCFRunLoopCommonModes });
     CFRunLoop::run_current();
-}
\ No newline at end of file
+}
+
+#[cfg(not(target_os = "macos"))]
+fn background_task_ios(mut tx: mpsc::Sender<()>) {
+    // On iOS, tvOS, and watchOS, we use a polling approach
+    // Poll every 2 seconds for network interface changes
+    loop {
+        std::thread::sleep(std::time::Duration::from_secs(2));
+        
+        // Send a notification to trigger a resync
+        match tx.try_send(()) {
+            Ok(_) => {},
+            Err(err) => {
+                if err.is_disconnected() {
+                    // Receiver has been dropped, exit the thread
+                    break;
+                }
+                // If the channel is full, skip this notification
+            }
+        }
+    }
+}

Any feedback appreciated! Thanks

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with if-watch's src/apple.rs and README.md, then compare the iOS linker errors against the macOS-specific system_configuration code and the fallback behavior described in the issue. Verify the chosen change by building libp2p 0.56 with quic for iOS and confirming the unresolved SCDynamicStore symbols no longer occur.

Written by the indexing model from the issue text.

Assessment

Tech stack
ios, rust
Domain
build-system, mobile-dev, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.