my issue is simple I compute a primary route and I added it to Sygic MapView. I defined the startWay point and also the the end point. It works perfectly. But when I try to change to the alternative route when I started the navigation It can't recalculate perfectly the new route. I Used the
func navigation(_ navigation: SYNavigation, didUpdate positionInfo: SYPositionInfo?) {
to detect changes in my position and compute the route every time but I think is not the best choice:
func navigation(_ navigation: SYNavigation, didUpdate positionInfo: SYPositionInfo?) {
if (mapRoute != nil){
mapView.remove(mapRoute)
computeRoute(from: startPointMP, to: endPointMP)
}
}
func computeRoute(from fromCoordinate: SYGeoCoordinate, to toCoordinate:
SYGeoCoordinate) {
let startWaypoint = SYWaypoint(position: fromCoordinate, type: .start, name: "Begin")
let endWaypoint = SYWaypoint(position: toCoordinate, type: .end, name: "End")
let routingOptions = SYRoutingOptions()
routingOptions.transportMode = .unknown// For other options see SYTransportMode
routingOptions.routingType = .economic// For other options see SYRoutingType
routing.computeRoute(startWaypoint, to: endWaypoint, via: nil, with: routingOptions)
}
func routing(_ routing: SYRouting, didComputePrimaryRoute route: SYRoute?) {
SYNavigation.shared().start(with: route)
// You might want to put it also on the map
mapRoute = SYMapRoute(route: route!, type: .primary)
markerEnd = SYMapMarker(coordinate: endPointMP!, image: UIImage(named: "Arrive")!)
mapView.add(mapRoute)
mapView.add(markerEnd)
mapView.add(markerStart)
mapView.cameraMovementMode = .free
}
Related
I am making an app that activates a VPN connection based on OpenVPN, retrieves a certificate from the database, and opens a tunnel using NEPacketTunnelProvider and NetworkExtension.
I used the following repository, and now my VPN is working fine.
But the problem is that I want to allow only one app to use this VPN when enabled (WhatsApp precisely), and I want to restrict all other apps of using it.
On Android it's possible by giving the bundle identifier of the allowed apps to the PackageManager.
Can you please help me?
This is my PacketTunnelProvider class:
import NetworkExtension
import OpenVPNAdapter
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
class PacketTunnelProvider: NEPacketTunnelProvider {
lazy var vpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
return adapter
}()
let vpnReachability = OpenVPNReachability()
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
override func startTunnel(options: [String : NSObject]?, completionHandler: #escaping (Error?) -> Void) {
// There are many ways to provide OpenVPN settings to the tunnel provider. For instance,
// you can use `options` argument of `startTunnel(options:completionHandler:)` method or get
// settings from `protocolConfiguration.providerConfiguration` property of `NEPacketTunnelProvider`
// class. Also you may provide just content of a ovpn file or use key:value pairs
// that may be provided exclusively or in addition to file content.
// In our case we need providerConfiguration dictionary to retrieve content
// of the OpenVPN configuration file. Other options related to the tunnel
// provider also can be stored there.
print("started!")
guard
let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration
else {
fatalError()
}
guard let ovpnFileContent: Data = providerConfiguration["ovpn"] as? Data else {
fatalError()
}
let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnFileContent
// configuration.settings = [
// // Additional parameters as key:value pairs may be provided here
// ]
// Uncomment this line if you want to keep TUN interface active during pauses or reconnections
// configuration.tunPersist = true
// Apply OpenVPN configuration
let evaluation: OpenVPNConfigurationEvaluation
do {
evaluation = try vpnAdapter.apply(configuration: configuration)
} catch {
completionHandler(error)
return
}
// Provide credentials if needed
if !evaluation.autologin {
// If your VPN configuration requires user credentials you can provide them by
// `protocolConfiguration.username` and `protocolConfiguration.passwordReference`
// properties. It is recommended to use persistent keychain reference to a keychain
// item containing the password.
guard let username: String = protocolConfiguration.username else {
fatalError()
}
// Retrieve a password from the keychain
// guard let password: String = ... {
// fatalError()
// }
let credentials = OpenVPNCredentials()
credentials.username = username
// credentials.password = password
do {
try vpnAdapter.provide(credentials: credentials)
} catch {
completionHandler(error)
return
}
}
// Checking reachability. In some cases after switching from cellular to
// WiFi the adapter still uses cellular data. Changing reachability forces
// reconnection so the adapter will use actual connection.
vpnReachability.startTracking { [weak self] status in
guard status == .reachableViaWiFi else { return }
self?.vpnAdapter.reconnect(afterTimeInterval: 5)
}
// Establish connection and wait for .connected event
startHandler = completionHandler
vpnAdapter.connect(using: packetFlow)
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: #escaping () -> Void) {
stopHandler = completionHandler
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
vpnAdapter.disconnect()
}
}
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
// `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow`
// protocol if the tunnel is configured without errors. Otherwise send nil.
// `OpenVPNAdapterPacketFlow` method signatures are similar to `NEPacketTunnelFlow` so
// you can just extend that class to adopt `OpenVPNAdapterPacketFlow` protocol and
// send `self.packetFlow` to `completionHandler` callback.
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: #escaping (Error?) -> Void) {
// In order to direct all DNS queries first to the VPN DNS servers before the primary DNS servers
// send empty string to NEDNSSettings.matchDomains
networkSettings?.dnsSettings?.matchDomains = [""]
// Set the network settings for the current tunneling session.
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
}
// Process events returned by the OpenVPN library
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleEvent event: OpenVPNAdapterEvent, message: String?) {
switch event {
case .connected:
if reasserting {
reasserting = false
}
guard let startHandler = startHandler else { return }
startHandler(nil)
self.startHandler = nil
case .disconnected:
guard let stopHandler = stopHandler else { return }
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
stopHandler()
self.stopHandler = nil
case .reconnecting:
reasserting = true
default:
break
}
}
// Handle errors thrown by the OpenVPN library
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
// Handle only fatal errors
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool, fatal == true else {
return
}
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
if let startHandler = startHandler {
startHandler(error)
self.startHandler = nil
} else {
cancelTunnelWithError(error)
}
}
// Use this method to process any log message returned by OpenVPN library.
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
// Handle log messages
print(logMessage)
}
}
This is the function used in my VPN View Model to start a tunnel:
func configureVPN(serverAddress: String, username: String, password: String) {
var configData:Data = Data.init()
self.getCertificate{certificate in
configData = certificate!
guard
//If we want to read from a file
// let configData = self.readFile(name: "vtest2"),
let providerManager = self.providerManager
else {
return
}
self.providerManager?.loadFromPreferences { error in
if error == nil {
let tunnelProtocol = NETunnelProviderProtocol()
tunnelProtocol.username = username
tunnelProtocol.serverAddress = serverAddress
tunnelProtocol.providerBundleIdentifier = self.providerId // bundle id of the network extension target
tunnelProtocol.providerConfiguration = ["ovpn": configData]
tunnelProtocol.disconnectOnSleep = false
providerManager.protocolConfiguration = tunnelProtocol
providerManager.localizedDescription = "Slyfone Guard" // the title of the VPN profile which will appear on Settings
providerManager.isEnabled = true
providerManager.saveToPreferences(completionHandler: { (error) in
if error == nil {
providerManager.loadFromPreferences(completionHandler: { (error) in
do {
try providerManager.connection.startVPNTunnel(options: nil) // starts the VPN tunnel.
} catch let error {
print(error.localizedDescription)
}
})
}
})
}
}
}
}
As an engineer from Apple said:
The way to do it is to use Per-App VPN. See the Per-App VPN On Demand section in the NETunnelProviderManager documentation.
With NEPacketTunnelProvider on macOS (as of 10.15.4) you can set this up yourself with NEAppRule. A very generic example of setting up Safari to trigger the VPN would be:
var perAppManager = NETunnelProviderManager.forPerAppVPN()
/* ... */
NETunnelProviderManager.forPerAppVPN().loadFromPreferences(completionHandler: { error in
precondition(Thread.isMainThread)
/* ... */
let proto = (perAppManager.protocolConfiguration as? NETunnelProviderProtocol) ?? NETunnelProviderProtocol()
proto.serverAddress = "server.vpn.com"
proto.providerBundleIdentifier = "com.perapp-vpn.macOSPacketTunnel.PacketTunnelTest"
var appRules = [NEAppRule]()
let appRule = NEAppRule(signingIdentifier: "com.apple.Safari", designatedRequirement: "identifier \"com.apple.Safari\" and anchor apple")
appRule.matchDomains = ["example.com"]
appRules.append(appRule)
perAppManager.appRules = appRules
perAppManager.isOnDemandEnabled = true
perAppManager.protocolConfiguration = proto
perAppManager.isEnabled = true
perAppManager.localizedDescription = "Testing Per-App VPN"
self.perAppManager.saveToPreferences { saveError in
/* Proceed to connect */
}
})
That was a very generic case and forPerAppVPN() is only available on macOS. A more real-world case world case for iOS would be to create this process through MDM. That entire flow is explained in the documentation I mentioned previously. I would start by just creating a configuration profile in Configurator 2 and testing it out.
No idea if it works on OpenVPN
I am trying to add multiple styles to a Mapbox MGLMapView. I understand this is not directly possible as MGLMapView.style only takes a single styleURL so I have been trying to get around this by adding a layer to the style using a styleURL.
A little explanation of why:
As shown in the Mapbox offline maps docs you can only download an MGLTilePyramidOfflineRegion using a styleURL. However, later I want to be able to display multiple of these offline regions at the same time (regions with different URLs). The urls I am currently are for a satellite, hybrid, and property line. I would like to be able to show the property line map on top of either the satellite or hybrid map. Currently, I can only show one at a time changing the MGLMapView.style.styleURL.
What I have tried:
guard let layer = (notification.userInfo?["layer"])! as? Layer,
let url = layer.url,
let style = mapView.style
else { return }
switch layer.id {
case .initial, .satellite:
mapView.styleURL = url
break
case .property:
// 1)
let source = MGLRasterTileSource(identifier: "\(layer.identifier)Style", configurationURL: URL)
let layer = MGLRasterStyleLayer(identifier: "\(layer.identifier)Layer", source: source)
// 2)
let source = MGLVectorTileSource(identifier: "\(layer.identifier)Style", configurationURL: URL)
let layer = MGLLineStyleLayer(identifier: "\(layer.identifier)Layer", source: source)
// When testing either 1 or 2 is commented out
style.addSource(source)
style.insertLayer(layer, at: UInt(style.layers.count - 1))
}
Supporting code:
enum LayerType: Int64 {
case hybrid = 0
case satellite = 1
case property = 2
}
struct Layer {
var id: LayerType
var identifier: String
var name: String
var url: URL?
}
let layers = [
Layer(id: .hybrid, identifier: "hybrid", name: "Hybrid", url: URL(string: "mapbox://styles/XXX")),
Layer(id: .satellite, identifier: "sat", name: "Satellite", url: URL(string: "mapbox://styles/XXX")),
Layer(id: .property, identifier: "property", name: "Property", url: URL(string: "mapbox://styles/XXX"))
]
Is there any other way to show two downloaded maps at the same time when they have different URLs?
Have you tried to add these layers to your style in Mapbox Studio? At runtime you can toggle them on and off, as described in the below example:
https://docs.mapbox.com/ios/maps/examples/runtime-toggle-layer/
This would allow you to download the offline region that includes all layers.
I am trying to connect programmatically to a VPN. I got the code below on this website: http://blog.moatazthenervous.com/create-a-vpn-connection-with-apple-swift/
I am trying to develop a command line application for macOS to connect programatically to a VPN.
import Foundation
import NetworkExtension
class VPN {
let vpnManager = NEVPNManager.shared();
let userName: String;
let passString: String;
let secret: String;
let server: String;
init (_ user: String, _ pass: String, _ secret: String, _ server: String){
userName = user
passString = pass
self.secret = secret
self.server = server
}
private var vpnLoadHandler: (Error?) -> Void { return
{ (error:Error?) in
if ((error) != nil) {
print("Could not load VPN Configurations")
return;
}
print ("me! me! ")
let p = NEVPNProtocolIPSec()
p.username = self.userName
p.serverAddress = self.server
p.authenticationMethod = NEVPNIKEAuthenticationMethod.sharedSecret
let kcs = KeychainService();
kcs.save(key: "SHARED", value: self.secret )
kcs.save(key: "VPN_PASSWORD", value: self.passString)
p.sharedSecretReference = kcs.load(key: "SHARED")
p.passwordReference = kcs.load(key: "VPN_PASSWORD")
p.useExtendedAuthentication = true
p.disconnectOnSleep = false
p.localIdentifier = "uio"
self.vpnManager.protocolConfiguration = p
self.vpnManager.localizedDescription = "Contensi"
self.vpnManager.isEnabled = true
self.vpnManager.saveToPreferences(completionHandler: self.vpnSaveHandler)
} }
private var vpnSaveHandler: (Error?) -> Void { return
{ (error:Error?) in
if (error != nil) {
print("Could not save VPN Configurations")
return
} else {
do {
try self.vpnManager.connection.startVPNTunnel()
} catch let error {
print("Error starting VPN Connection \(error.localizedDescription)");
}
}
}
}
public func connectVPN() {
//For no known reason the process of saving/loading the VPN configurations fails.On the 2nd time it works
do {
self.vpnManager.loadFromPreferences(completionHandler: self.vpnLoadHandler)
// self.vpnManager.saveToPreferences(completionHandler: nil)
}
}
public func disconnectVPN() ->Void {
self.vpnManager.connection.stopVPNTunnel()
}
public func status() -> NEVPNStatus {
return vpnManager.connection.status
}
}
My problem is that I my completion handlers are not working. Take the function connectVPN(), for instance. It's completion handler is vpnLoadHandler. However, I never get that handler executed. See the print statement on the handler? Nothing ever gets printed.
Reading Apple's documentation, I know that I have to configure XCode with an entitlement in order to allow VPN - I haven't got the "capabilities"-tab, so I don't know how to add that entitlement. I am not sure if that's the problem, since the app compiles file and executes. I just don't get the completion handler to execute.
So, basically:
why isn't the handler being called? And what can I do to debug?
Could this be due the lack an entitlement to allow the configuration of a personal vpn in xcode? If so, how can I configure that?
Best,
Francis
UPDATE I actually managed to create the entitlements file, and I guess it has the proper settings to allow a personal vpn. So I guess that part is solved. But why isn't the completion handler called?
I am having difficulties with making this function pass through optional values, I am using an #escaping closure, but the issue arises where my code demands a certain parameter. So in this instance, I am trying to upload two images. However, the third parameter is giving me trouble. How can I make it so that a certain parameter(s) is optional/doesn't need to be called. And I can inject the data into a certain parameter if it does exist?
This is my code -
static func uploadImagesToFirebaseStorage(data: Data? = nil, secondData: Data? = nil, thirdData: Data? = nil, onSuccess: #escaping (_ imageURL: String, _ secondImageURL: String?, _ thirdImageURL: String?) -> Void) {
let firstPhotoIdString = NSUUID().uuidString
let secondPhotoIdString = NSUUID().uuidString
let thirdPhotoIdString = NSUUID().uuidString
let storageRef = Storage.storage().reference(forURL: Config.STORAGE_REF_ROOT ).child("posts").child(firstPhotoIdString)
storageRef.putData(data!, metadata: nil) { (metadata, error) in
if error != nil {
ProgressHUD.showError(error?.localizedDescription)
return
}
let secondStorageRef = Storage.storage().reference(forURL: Config.STORAGE_REF_ROOT ).child("posts").child(secondPhotoIdString)
secondStorageRef.putData(secondData!, metadata: nil) { (secondMetadata, error) in
if error != nil {
ProgressHUD.showError(error?.localizedDescription)
secondStorageRef.setValue(nil, forKey: secondPhotoIdString)
return
}
let thirdStorageRef = Storage.storage().reference(forURL: Config.STORAGE_REF_ROOT ).child("posts").child(thirdPhotoIdString)
thirdStorageRef.putData(thirdData!, metadata: nil) { (thirdMetadata, error) in
if error != nil {
ProgressHUD.showError(error?.localizedDescription)
return
}
if let firstPhotoURL = metadata?.downloadURL()?.absoluteString, let secondPhotoURL = secondMetadata?.downloadURL()?.absoluteString, let thirdPhotoURL = thirdMetadata?.downloadURL()?.absoluteString {
onSuccess(firstPhotoURL, secondPhotoURL, thirdPhotoURL)
}
}
}
}
}
I will do conditional checks to see if there is valid data to pass through but I want this type of outcome:
If value doesn't exist I do not need to include a certain parameter(s), after the conditional checks have been done
If it doesn't exist, forget about that parameter and carry on
I have found that if can make multiple instances of the same code and change the parameters, but I will need 20 instances, and as you can tell, that would not be efficient what so ever. So how can I have all parameters optional and pass data through without the code needing/demand every parameter?
I know a similar question has been asked, but I read it and didn't find it useful.
Thank you.
EDIT - More information
I am trying to upload images to Firebase. I have three parameters (for 3 images), however, I would like the function to be scalable in terms of it doesn't matter how many images I have, I can upload accordingly.
This is my call to the function:
static func uploadDataToServer(data: Data, secondData: Data? = nil, thirdData: Data? = nil, firstVideoURL: URL? = nil, secondVideoURL: URL? = nil, thirdVideoURL: URL? = nil, caption: String, onSuccess: #escaping () -> Void) {
if let secondImageData = secondData {
uploadImagesToFirebaseStorage(data: data, secondData: secondImageData) { (firstPhotoURL, secondPhotoURL, nil ) in
self.sendDataToDatabase(firstPhotoURL: firstPhotoURL, secondPhotoURL: secondPhotoURL, caption: caption, onSuccess: onSuccess)
}
}
}
}
In the above code, I am trying to upload two images, even thought I have three parameters hence why I set all but the first data AKA first image to nil. My issue is the code either expects a third image or it will crash because there is no third image. I inject the image from the ViewController class by passing the image (from the ImagePickerController) and using UIImageJPEGRepresentation().
I've been reading quite a few articles now about swift functions with closures, trailing closures and escaping functions. They all seem to give examples which is sufficiently different that I'm not understanding what Im doing wrong with my own function.
My main problem is with how to execute a trailing closure function.
I've created this function to upload an image to firebase. It takes two inputs and are supposed to return a string (imageURL). I do belive this function is ok.
func uploadImageToFirebaseAndReturnImageURL(directory: String, image: UIImage!, handler: #escaping(_ imageURL: (ImageURL)) -> ()) {
let imageName = NSUUID().uuidString // create unique image name
if let uploadData = UIImagePNGRepresentation(image) {
DB_STORE.child(directory).putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil {
print(error)
return
}
if let profileImageUrl = metadata?.downloadURL()?.absoluteString {
let d = ImageURL(imageURL: profileImageUrl)
handler (d)
}
return
})
}
}
My issue is how to execute this function correctly when it comes to the handler.
I want to first execute the function then when complete I want to get the imageURL and use this variable into another nested function that upload this variable(String) into a firebase database.
uploadImageToFirebaseAndReturnImageURL(directory: "profileImage", image: selectedImageFromPicker!, handler: { imageURL in
guard let uid = Auth.auth().currentUser.uid else { print("User is not logged in"); return }
DataService.instance.updateUserWithProfileImageURL(uid: uid, imageURL: imageURL)
print("")
}
What am I doing wrong?
To pass a trailing closure you need to end/close your function call and omit the closure argument label. For instance:
func foo(first: Int, second: Int, handler: (Int) -> Void) {
...
}
call syntax:
foo(first: 10, second: 20) { result in
/* trailing closure body */
}
By the way, you should simply your handler argument declaration from:
handler: #escaping (_ imageURL: (ImageURL)) -> ()
to this:
handler: #escaping (ImageURL) -> Void
Using Void or () is matter of style since they are logically the same. I prefer the former ;)