NEVPNManager saveToPreferences/loadFromPreferences callbacks never called - swift

I am trying to setup a IPSec VPN connection but when I try to run
manager.saveToPreferences
or
manager.loadFromPreferences
The callback functions are never called and so I cannot start the VPN Tunnel, and "123" is never printed. The program just ends. What am I doing wrong?
I am testing this in MacOS 10.13.4
import NetworkExtension
import Foundation
let manager = NEVPNManager.shared()
let p = NEVPNProtocolIPSec()
p.authenticationMethod = NEVPNIKEAuthenticationMethod.sharedSecret
p.remoteIdentifier = remoteID
p.localIdentifier = localID
KeychainWrapper.standard.set("SECRET", forKey: "SECRET")
p.sharedSecretReference = KeychainWrapper.standard.dataRef(forKey: "SECRET");
manager.protocolConfiguration = p
manager.onDemandRules = [NEOnDemandRuleConnect()]
manager.isOnDemandEnabled = true
manager.isEnabled = true
manager.saveToPreferences { completionHandler in
manager.loadFromPreferences { completionHandler in
print(123)
do {
try manager.connection.startVPNTunnel()
} catch (let exception) {
print(exception)
}
}
}
I am also having issues configuring the VPN, but this question is more about why those callbacks never get called.
Also, as a side note. When I run
do {
try manager.connection.startVPNTunnel()
} catch (let exception) {
print(exception)
}
outside of saveToPreferences and loadFromPreferences I get the following error:
Error Domain=NEVPNErrorDomain Code=1 "(null)"
Any help would be greatly appreciate, thanks.

Is this just in a .swift file by itself? Try putting the code in a simple application, like inside applicationDidFinishLaunching(). It probably won’t do anything without a running runloop

Related

Swift - How to wait for something without making the app hanging

I have a code like this:
print("Migration Execution: Successfully uninstalled MCAfee")
migrationInfoPicture.image = NSImage(named: "Unroll")
migrationInfoText.stringValue = NSLocalizedString("Unrolling from old server... Please wait!", comment: "Unrolling")
while(!readFile(path:logfilePath)!.contains("result: 2 OK")) {
searchLogForError(scriptPath: scriptOnePath)
}
print("Migration Execution: Successfully unrolled from old server")
migrationInfoText.stringValue = NSLocalizedString("Setting up MDM profile... Please wait!", comment: "Setting up MDM")
while(!readFile(path:logfilePath)!.contains("result: 3 OK")) {
searchLogForError(scriptPath: scriptOnePath)
}
It actually works in the background, reading from the file works and logging works but since the GUI will be hanging executing a while loop with a quickly completed task, the image and the text changes will not be visible.
Code for searchForLogError is:
func searchLogForError(scriptPath:String) {
if((readFile(path:logfilePath)!.filter { $0.contains("ERROR") }).contains("ERROR")) {
print("Migration abborted")
migrationInfoPicture.image = NSImage(named: "FatalError")
migrationInfoText.stringValue = NSLocalizedString("An error occured: \n", comment: "Error occurence") + readFile(path:logfilePath)!.filter { $0.contains("ERROR") }[0]
migrationWarningText.stringValue = NSLocalizedString("In order to get further help, please contact: mac.workplace#swisscom.com", comment: "Error support information")
self.view.window?.level = .normal
btnExitApplicationOutlet.isHidden = false
getScriptProcess(path:scriptPath).terminate()
return
}
}
How can I achieve a visible change of NSImage and NSLocalizedString while constantly looking for log file change without a hanging GUI (or even with a hanging GUI, but with enough time to change the visible elements between the while-loops)?
Polling file system resources is a horrible practice. Don't do that. There are dedicated APIs to observe file system resources for example DispatchSourceFileSystemObject
Create a property
var fileSystemObject : DispatchSourceFileSystemObject?
and two methods to start and stop the observer. In the closure of setEventHandler insert the code to read the file
func startObserver(at url: URL)
{
if fileSystemObject != nil { return }
let fileDescriptor : CInt = open(url.path, O_EVTONLY);
if fileDescriptor < 0 {
print("Could not open file descriptor"))
return
}
fileSystemObject = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fileDescriptor, eventMask: [.write, .rename], queue: .global())
if fileSystemObject == nil {
close(fileDescriptor)
print"Could not create Dispatch Source"))
return
}
fileSystemObject!.setEventHandler {
if self.fileSystemObject!.mask.contains(.write) {
// the file has been modified, do something
}
}
fileSystemObject!.setCancelHandler {
close(fileDescriptor)
}
fileSystemObject!.resume()
}
func stopObserver()
{
fileSystemObject?.cancel()
fileSystemObject = nil
}

MetaWear : CLI application on MACOSX

I am attempting to follow the MetaWear guide on starting a sample application, located here . The problem that I am quickly running into is that I am getting unexpected crashes. Here is how my code is structured
:
Lastly, my Podfile contains the following:
platform :osx, '10.12.6'
target 'meta-wear' do
use_frameworks!
pod 'MetaWear', '~> 2.9'
end
When I run the application, I get a Thread exception as follows on line 5 of the first image:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
While I am certainly a new Swift developer ( noob ), I have no idea why I am unable to reproduce their guide.
Xcode: 9.0
macOS Sierra Version 10.12.6 ( This is where I want to run this command line application )
Update after adding an infinite loop
I updated the main.swift class to ahve the following:
import Foundation
let runLoop = RunLoop.current;
let distantFuture = Date.distantFuture;
print("### we are in the create");
let starter = MetaWearStarter();
print("### we are after the create");
while (runLoop.run(mode: RunLoopMode.defaultRunLoopMode, before: distantFuture)){
print("### listening for a metawear device");
}
I created a class called MetaWearStarter.swift as follows:
import Foundation
import MetaWear
class MetaWearStarter : NSObject {
override init() {
super.init();
print("### we are in the init");
startConnection();
}
func startConnection() {
print("##### connection call was made");
let manager = MBLMetaWearManager.shared();
maanger.startScanForMetaWears() { array in
print("### connection scan was complete")
// Hooray! We found a MetaWear board, so stop scanning for more
MBLMetaWearManager.shared().stopScan()
// Connect to the board we found
if let device = array.first {
device.connectAsync().success() { _ in
print("#### we connected to a device");
}.failure() { error in
print("### unable to connect");
}
}
}
}
}
I get the previous error on this line:
let manager = MBLMetaWearManager.shared();
And my output never makes it past that line:
### we are in the create
### we are in the init
##### connection call was made
An infinite loop to keep the runloop running is not a good habit.
Add a completion handler to your class and stop the runloop on completion.
The usual way to handle the run loop in a CLI is this:
import Foundation
import MetaWear
class MetaWearStarter {
let manager = MBLMetaWearManager.shared()
func startConnection(completion: #escaping (String)->()) {
print("##### connection call was made");
manager.startScanForMetaWears() { array in
print("### connection scan was complete")
// Hooray! We found a MetaWear board, so stop scanning for more
manager.stopScan()
// Connect to the board we found
if let device = array.first {
device.connectAsync().success() { _ in
completion("#### we connected to a device")
}.failure() { error in
completion("### unable to connect, error: \(error.localizedDescription)")
}
} else {
completion("#### no device found")
}
}
}
}
let starter = MetaWearStarter()
let runLoop = RunLoop.current
starter.startConnection { (result) in
print(result)
CFRunLoopStop(runLoop.getCFRunLoop())
}
runLoop.run()
exit(EXIT_SUCCESS)

CKContainer.discoverAllIdentities always fails

The CKContainer.discoverAllIdentities request always fails in my CloudKit app. It has continually failed over the course of several days.
A simplified version of the code that is failing (which results in the same error) is:
private func getContacts(completion: (([CKUserIdentity]?) -> Void)?) {
container.status(forApplicationPermission: .userDiscoverability) { [weak self] status, error in
if let error = error {
print(error)
}
switch status {
case .granted:
self?.discover(completion: completion)
default:
print("status not granted")
}
}
}
private func discover(completion: (([CKUserIdentity]?) -> Void)?) {
let op = CKDiscoverAllUserIdentitiesOperation()
op.qualityOfService = .userInitiated
op.discoverAllUserIdentitiesCompletionBlock = { error in
if let error = error {
print(error)
}
}
op.userIdentityDiscoveredBlock = { identity in
print(identity)
}
op.start()
}
It results in an error being passed to the op.discoverAllUserIdentitiesCompletionBlock. The description of the error in the log is:
<CKError 0x1c4a51a60: "Server Rejected Request" (15/2000); server message = "Internal server error"; uuid = F67453B9-712D-4E5E-9335-929123E3C978; container ID = "iCloud.com.huntermaximillionmonk.topdraw">
Previously, this operation would work, but only for certain iCloud users. Now it's not for both of my test users.
Problem:
This was a problem in iOS 11.0
Based on my testing:
This works ok in Xcode 9.2 / iOS 11.2.1 on the device (not simulator)
After resetting the simulator works for the first time, doesn't work subsequently, however on the device it works repeatedly.
Code:
let queue = OperationQueue()
func requestPermissions(for permissions: CKApplicationPermissions,
completionHandler: #escaping (CKApplicationPermissionStatus, Error?) -> ()) {
CKContainer.default().requestApplicationPermission(permissions) { status, error in
if let error = error {
print("Error for requesting \(permissions) - \(error)")
}
let statusMessage : String
switch status {
case .granted:
statusMessage = "Granted"
case .denied:
statusMessage = "Denied"
case .couldNotComplete:
statusMessage = "Could not complete"
case .initialState:
statusMessage = "Initial state"
}
print("Permission - \(statusMessage)")
completionHandler(status, error)
}
}
private func discoverAllUsers() {
let operation = CKDiscoverAllUserIdentitiesOperation()
operation.userIdentityDiscoveredBlock = { userIdentity in
print("userIdentity = \(userIdentity)")
}
operation.discoverAllUserIdentitiesCompletionBlock = { error in
if let error = error {
print("Discover all users Error: \(error) ")
}
else {
print("Discover all users completed successfully")
}
}
queue.addOperation(operation)
}
Edit:
Apple fixed this issue day after this answer was posted, coincidence?! I don't think so :)
This is not actually the answer to the question, but a fix that helped me to cross over this error. It will require you to change your app UI interaction and add ContactsUI framework to your project, moreover your user will be responsible for selecting a contact with iCloud related email.
Good news is that the method discoverUserIdentity is still works. So, you can use it to get CKUserIdentity from manually selected contact.
func addContact(_ contact:CNContact) {
var lookUpEmails = [CKUserIdentityLookupInfo]()
for email in contact.emailAddresses {
lookUpEmails.append(CKUserIdentityLookupInfo(emailAddress: (email.value as String)))
}
let checkUserOperation = CKDiscoverUserIdentitiesOperation()
checkUserOperation.userIdentityLookupInfos = lookUpEmails
checkUserOperation.userIdentityDiscoveredBlock = { [unowned self] (identity, info) -> Void in
if identity.hasiCloudAccount {
if let recordID = identity.userRecordID {
//do something with discovered user
}
checkUserOperation.cancel()
}
}
checkUserOperation.queuePriority = Operation.QueuePriority.high
CKContainer.default().add(checkUserOperation)
}
It might sound useless, but in my case, it helped me to solve the Server Rejected Request" (15/2000) error, to fix one of the features of my app and continue to use the other feature related code with less efforts than I thought.
I hope someone will find this helpful.
Just another data point on this that might help with the overall picture. I was still seeing this error on 11.2.5 when I used my own iCloud AppleID (with hundreds of contacts) while running a Test App that called discoverAllIdentitiesWithCompletionHandler. I'd get the dreaded
CKError 0x1c0051730: "Server Rejected Request" (15/2000); server message = "Internal server error".
When I switched to run the exact same code on my daughters iOS11.2.5 device (with just a handful of contacts) the code worked fine.
Leads me to believe there is some rate limiting going on when there are a lot of contacts with iOS11.
(P.S. No errors at all running on iOS10)

What is the best practice for terminating a Swift-Server using Perfect during server startup?

I'm using Perfect server-side Swift, and have some conditions in the main.swift where the server should be terminated before actually starting up. Is there a best-practice for handling this termination?
Currently, I'm throwing an error because a return doesn't work-- because it's not in the context of a function (see This is the error I'm throwing and concerned about. below). Here's what I have so far:
//
// main.swift
//
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
enum ServerStartupError : Error {
case FailedUserControllerSetup
}
if !UserController.setup() {
// This is the error I'm throwing and concerned about.
throw ServerStartupError.FailedUserControllerSetup
}
let server = HTTPServer()
let serverRoutes = ServerRoutes()
serverRoutes.addRoutes(server: server)
server.serverPort = 8181
do {
// Launch the HTTP server.
try server.start()
} catch PerfectError.networkError(let err, let msg) {
print("Network error thrown: \(err) \(msg)")
}
Sorry, my fault, I misunderstood you! Please note that throw in the top level of main.swift is meaningless because the only process who will catch that error is the OS. Please try the code below:
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
if UserController.setup() {
let server = HTTPServer()
let serverRoutes = ServerRoutes()
serverRoutes.addRoutes(server: server)
server.serverPort = 8181
do {
// Launch the HTTP server.
try server.start()
} catch PerfectError.networkError(let err, let msg) {
print("Network error thrown: \(err) \(msg)")
} catch (let panic) {
print("panic: \(panic)")
} //end server starting
} else {
// here is the code that prompting user to finish the setup
// before running the server. The program will display this
// message and automatically exit the process (end of program)
print("Setup is not completed.")
}//end if
Alternatively, you can also use the exit() function to preform a quit from the main.swift, with importing Foundation (the standard library), as below:
//
// main.swift
//
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
import Foundation
if !UserController.setup() {
// This is the error I'm throwing and concerned about.
print("ServerStartupError.FailedUserControllerSetup")
exit(-1)
}//end if
let server = HTTPServer()
let serverRoutes = ServerRoutes()
serverRoutes.addRoutes(server: server)
server.serverPort = 8181
do {
// Launch the HTTP server.
try server.start()
} catch PerfectError.networkError(let err, let msg) {
print("Network error thrown: \(err) \(msg)")
} catch (let panic) {
print("Panic: \(panic)")
}
exit(0)
Both methods have pros and cons. The first one doesn't require anything else while the second one needs Foundation Library; Also I would say that the second one is more traditional and could provide an exit code, which allows you catch the error by wrapping it up in a customize boot loader or something. Unix / Linux uses exit code to check out the application process information and in some cases you can see these exit codes in system log, if available.

How to let IBM BlueSocket run on a GUI application and support multi connections

Recently, I found a pure swift socket server and client called IBM BlueSocket.
It is suitable for me that it does server-cleint communication.
It has a pretty simple sample. but I encountered some problems.
1. How to run it on a GUI application's run loop?
2. How to run it and support multi connections?
For what it's worth, I present the world's simplest chat client.
import Foundation
import Socket
// Very simplified chat client for BlueSocket - no UI, it just connects to the echo server,
// exchanges a couple of messages and then disconnects.
// You can run two instances of Xcode on your Mac, with the BlueSocketEchoServer running in one and
// this program running in the other. It has been tested running in the iPhone simulator, i.e.,
// under iOS, without problems.
// License: Public domain.
public class BlueSocketChatClient {
public func runClient() {
do {
let chatSocket = try Socket.create(family: .inet6)
try chatSocket.connect(to: "127.0.0.1", port: 1337)
print("Connected to: \(chatSocket.remoteHostname) on port \(chatSocket.remotePort)")
try readFromServer(chatSocket)
try chatSocket.write(from: "Hello to you too!")
try readFromServer(chatSocket)
try chatSocket.write(from: "Bye now!\n")
try chatSocket.write(from: "QUIT")
sleep(1) // Be nice to the server
chatSocket.close()
}
catch {
guard let socketError = error as? Socket.Error else {
print("Unexpected error ...")
return
}
print("Error reported:\n \(socketError.description)")
}
}
// This is very simple-minded. It blocks until there is input, and it then assumes that all the
// relevant input has been read in one go.
func readFromServer(_ chatSocket : Socket) throws {
var readData = Data(capacity: chatSocket.readBufferSize)
let bytesRead = try chatSocket.read(into: &readData)
guard bytesRead > 0 else {
print("Zero bytes read.")
return
}
guard let response = String(data: readData, encoding: .utf8) else {
print("Error decoding response ...")
return
}
print(response)
}
}
Bill Abt: If you can use this in any way you're welcome to it.
The sample has been recently updated and illustrates use of the GCD based Dispatch API to do multi-threading and supporting multiple connections. It also should give you a idea on how to run it on the main queue (which'll work for either a GUI or server application).