Post

Replies

Boosts

Views

Activity

Reply to Running Timer inside NetworkExtension
So I did some more testing. Used two types of timers: Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(doSomeStuff), userInfo: nil, repeats: true) and queue = DispatchQueue(label: "Timer", qos: .background) timerSource = DispatchSource.makeTimerSource(queue: queue) timerSource.schedule(deadline: .now() + .seconds(Int(nextRunTime.timeIntervalSinceNow)), repeating: 2 * 60, leeway: .seconds(10)) timerSource.setEventHandler { [weak self] in     self?.doSomeOtherStuff() } timerSource.resume() This is all happening inside NetworkExtension with VPN connected at the time of testing, so the process runs all the time, except when it doesn't :) Following are timestamps from syslog. I selected two of them (first one and the last) that are from the first Timer. Others are just logs from VPN library. As you can see in between there were some things going on, NE was woken up (unfreezed? don't know how to call that state) several times and traffic was flowing, extension was running then got to void again, then running, etc. Still the timer was not called. The second timer was set to be fired each two minutes, so it was called even less frequently than the first one. All in all it looks like timer does not respect the wall time and just uses some internal counter that ticks according to its runloop. And if between two runs OS decides to freeze the process, this time just disappears from the counter. Questions: Is there any timer or a mechanism like that that can call my code every now and then and in case the process was frozen in between would count time according to real time? Where can I find any documentation how this freezing/unfreezing works on iOS/ipadOS? Is there a way to detect that NE was frozen for some time and just got some time to run again?
May ’22
Reply to Logging from NetworkExtension
I was using .info, now changed to .debug. But I have found another "interesting" thing - some messages are visible when looking at them in real-time in console app, but then disappear when I get logs using sudo log collect --device-name .... The only difference is that they are thrown using additional abstraction in code. Example: func log1(message) { os_log(...) } func log2(message) { log1(message) } log1("message") // Is visible on-line and after retrieving log from device log2("message") // Is visible only while looking at it on-line, through console app connected to the phone, but NOT when using log collect PS. I know it's better to use os_log directly, but there are 3rd party libraries and other stuff involved, so we have what we have.
Mar ’22
Reply to Couldn’t communicate with a helper application
Sorry, I mean the App with GUI as in container app. What I wanted to say is that as far as I understand from documentation and example code, in the app I just run NSXPCConnection(machServiceName: machServiceName, options: []).remoteObjectProxyWithErrorHandler[...].myWorkingFunction() on an extension that was already installed and approved by user manually in System Preferences -> Security & Privacy, then it should just work. And indeed it works in 99% of the time. Until user restarts his computer and app gets Couldn’t communicate with a helper application error.
Sep ’21
Reply to Couldn’t communicate with a helper application
Since this is happening on reboot, have you added logging to your provider to make sure that it is active and setup to receive an XPC connection at the point you are expecting to communicate with the provider? From what I understood from documentation, there is nothing to do, because launchd should start it as soon as I start communicating with it using NSXPCConnection(machServiceName: machServiceName, options: []).remoteObjectProxyWithErrorHandler? I can add logs for sure, but I would like to know it inside the code, to make the app more robust instead of throwing wrong UI on users.
Sep ’21
Reply to List of system privileges
As I see there are more people interested in this topic in other threads, here is what works from inside the System Extension to save/load password to/from the Keychain: Save password: var query = [String: Any]() query[kSecAttrAccessGroup as String] = accessGroup query[kSecAttrService as String] = context query[kSecClass as String] = kSecClassGenericPassword query[kSecAttrAccount as String] = username query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock query[kSecValueData as String] = password.data(using: .utf8) let status = SecItemAdd(query as CFDictionary, nil) Load password: var query = [String: Any]() query[kSecAttrAccessGroup as String] = accessGroup query[kSecAttrService as String] = context query[kSecClass as String] = kSecClassGenericPassword query[kSecAttrAccount as String] = username query[kSecMatchLimit as String] = kSecMatchLimitOne query[kSecReturnData as String] = true var result: AnyObject? switch SecItemCopyMatching(query as CFDictionary, &result) { Password appears in the System keychain
Jun ’21