Developers SDK

VPN Unlimited SDK v2.0.1 for macOS

Getting started

Overview

VPN Unlimited SDK for OS X is shipped as a static framework. It is written in Objective-C and C++ and is available for OS X 10.7 and later.

Setup

Drag and drop VPNUnlimitedSDK.framework into your project.
You have to include the VPNUnlimitedSDKResourceOSX.bundle file (drag and drop too).

In order to use it you have to add -ObjC linker flag in XCode (Target -> Build Settings -> Linker -> Other Linker Flags). You also have to add the following additional frameworks and libraries in XCode (Target -> Build Phases -> Link Binary With Libraries):

  • SystemConfiguration.framework
  • CFNetwork.framework
  • libc++.dylib
  • libz.dylib
  • libresolv.dylib

After setup is done you should be able to use all the classes from SDK by including it with #import <VPNUnlimitedSDK/VPNUnlimitedSDK.h> directive.

Note:

Current version of VPN Unlimited SDK uses the following libraries under the hood:

  • libcurl
  • libopenssl
  • libcares
  • libcrypto

If your project uses one of these libraries and you get conflicts please contact VPN Unlimited team for further investigation.

Xcode 7.x+ and App Transport Security

VPNUnlimited SDK uses local HTTP server to download VPN profiles (which does not use NEVPNManager class). You must include Allow Arbitrary Loads flag in your project's Info.plist file if you use Xcode 7.x+. See Apple's documentation on this issue.

App Sandbox Mode

For the same reason as in previous paragraph you must set Incoming Connections (Server), Outgoing Connections (Client) checkboxes when using Apple's App Sandbox mode. You also must add com.apple.security.temporary-exception.apple-events exception key to your entitlement with value of com.apple.Safari as described here.

Using App ID and App secret

Use the following setup code in your AppDelegate's application:didFinishLaunchingWithOptions: method:

[VPNURequest setApplicationID:@"<#YOUR_APPLICATION_ID#>"];
[VPNURequest setApplicationSecret:@"<#YOUR_APPLICATION_SECRET#>"];
Classes

This SDK is designed in classical OOP style. We provide a handful of classes which you can use in any desired combination. All network calls are synchronous, so you should never call them from main thread. This limitation is highlighted by the fact, that you actually can perform several steps in a row serially on secondary thread and come back to main thread with a desired result.

Here is the complete list of classes:

  • KSVPNUFacade
  • VPNUTransport
  • VPNURequest
  • VPNUAuthorizer
  • VPNUAccount
  • VPNUAccountStatus
  • VPNUAccountUserInfo
  • VPNUAccountDeviceStatistics
  • VPNUAccountDevice
  • VPNUServersListAccessor
  • VPNUServerItem
  • VPNUConfigurator
  • VPNUPurchaseManager
  • VPNUPurchaseItem
  • VPNUOnDemandConnectionManager
  • VPNUStorage

You can see the complete documentation for each class at Reference page.

Here we just briefly describe the standard workflow.

VPNUTransport and its dependers

The VPNUTransport is the main networking class in SDK. It is responsible for communicating with our server as well as dealing with active session. A lot of classes which involve network communication are dependant on this class, namely:

  • VPNUAuthorizer
  • VPNUAccount
  • VPNUServersListAccessor
  • VPNUConfigurator
  • VPNUPurchaseManager

All these classes have a designated initializer initWithTransport:. Their instances have to be instantiated using this constructor, otherwise they just will not work. Here is a basic example of how you can use it:

// In your view controller declaration, e.g.:
@interface ViewController ()
@property VPNUTransport *transport;
@property VPNUAuthorizer *authorizer;
@end
// Then, initialize transport somewhere, for example in `viewDidAppear`:
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // 1. Set up transport
    self.transport = [[VPNUTransport alloc] init];

    // 2. Set up authorizer
    self.authorizer = [[VPNUAuthorizer alloc] initWithTransport:self.transport];
}

Now you can perform all needed actions with authorizer object.

Multithreading

As stated previously, all network calls in this SDK are performed synchronously. That means that you should perform them all on a non-main thread, otherwise it would block it and never come back. Every method which falls into this category is explicitly documented.

In order to achieve asynchronous behavior you can use any multithreading technique you like. We recommend Grand Central Dispatch. When an operation is finished you can move back to main thread. You can stay on secondary thread and perform more networking actions serially, if you desire.

KSVPNUFacade

KSVPNUFacade is a class which serves as a 'facade' to other VPN Unlimited SDK classes. It hides the complexity of VPN Unlimited SDK by encapsulating most of the needed features.

If you do not want to construct API requests and VPN configurator by yourself you can use KSVPNUFacade singleton. Please check the documentation for this class: it is convenient for most applications.

FAQ

In every example in the FAQ section it is implicitly considered that the code is executing on a secondary thread using this syntax:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Sample code from FAQ
});

It is also considered that transport property is set up before any calls:

self.transport = [[VPNUTransport alloc] init];
How to set a custom name for downloaded profile?

First, contact our representative and supply a unique string for profile name. This can be your company or product name, e.g. "My Cool VPN". This string will then be used on our servers when generating a profile.

Then don't forget to set the same string for your VPNUConfigurator instance:

self.configurator.profileName = @"My Cool VPN";

Or using facade:

[KSVPNUFacade defaultFacade].profileName = @"My Cool VPN";

This action must be done before any other calls to VPNUConfigurator instance, e.g. in your application:didFinishLaunchingWithOptions: method. So this step is a part of your initial setup.

How do I provide a custom profile download page?

You can supply a path to your custom HTML page by setting customProfileDownloadHTMLPath property of your VPNUConfigurator instance:

self.configurator.customProfileDownloadHTMLPath = absolutePathToYourHTMLPage;

Or using facade:

[KSVPNUFacade defaultFacade].configurator.customProfileDownloadHTMLPath = absolutePathToYourHTMLPage;

This action must be done before any other calls to VPNUConfigurator instance, e.g. in your application:didFinishLaunchingWithOptions: method. So this step is a part of your initial setup.

Bear in mind that you must provide @PROFILE_URL@ string somewhere in your HTML code as this string will then be replaced with real profile URL. Please consider the following example of your page:

<html>
<body>
<div>Download of your "My Cool VPN" profile will begin shortly...</div>
<iframe id="download_profile" src="@PROFILE_URL@" style="display:none;"></iframe>
</body>
</html>
How to log in with existing user?

First, set up VPNUAuthorizer instance.

self.authorizer = [[VPNUAuthorizer alloc] initWithTransport:self.transport];

Then, you can log in using user's credentials:

NSString *login = @"login@some.domain";
NSString *password = @"password";
NSError *error;
VPNUAccountUserInfo *userInfo = [self.authorizer authorizeWithLogin:login andPassword:password error:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        NSLog(@"Logged in user:%@", userInfo.name);
    }
});

If authorization is successful, the used VPNUTransport instance is managing the newly created session and reconnects automatically when this session is expired. From now on, you have to pass the same VPNUTransport instance to all other object that depend on it.

How to register a new user?

First, set up VPNUAuthorizer instance.

self.authorizer = [[VPNUAuthorizer alloc] initWithTransport:self.transport];

Then, you can register new user using user's credentials. registerWithLogin: will automatically log in a newly created user.

NSString *login = @"login@some.domain";
NSString *password = @"password";
NSError *error;
VPNUAccountUserInfo *userInfo = [self.authorizer registerWithLogin:login andPassword:password error:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        NSLog(@"Registered and Logged in user:\n%@", userInfo.name);
    }
});
How to log out a logged in user?

You have to be logged in in order to perform this operation successfully. See two previous answers.

NSError *error;
BOOL signedOut = [self.authorizer signOutWithError:&error];
dispatch_async(dispatch_get_main_queue(), ^{
    if (!signedOut && error) {
        NSLog(@"Error: %@", error);
    } else {
        NSLog(@"Logged out");
    }
});
How to retrieve account status of logged in user?

Assuming you are logged in:

VPNUAccount *account = [[VPNUAccount alloc] initWithTransport:self.transport];
NSError *error;
VPNUAccountStatus *accountStatus = [account statusWithError:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        NSLog(@"Will expire on:\n%@", accountStatus.timeLeftString);
    }
});
How to monitor VPN connection status?

You can call loadPreferences of your VPNUConfigurator instance if you want to retrieve connectionStatus property (incl. KVO) or receive a VPNUConnectionStatusDidChangeNotification notification.
Attention: call this method asynchronously before any call to configurator.

NSError *error = [self.configurator loadPreferences];
dispatch_async(dispatch_get_main_queue(), ^{
	[self addConnectionStatusKVO];
});

You can also use facade for this:

[[KSVPNUFacade defaultFacade] loadPreferencesWithCompletion:^(NSError *error){
    [self addConnectionStatusKVO];
}];
How to retrieve the list of available VPN servers?
VPNUServersListAccessor *serversListAccessor = [[VPNUServersListAccessor alloc] initWithTransport:self.transport];
NSError *error;
NSArray *servers = [serversListAccessor listWithError:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        NSLog(@"Available regions:");
        for (VPNUServerItem *listItem in servers) {
            NSLog(@"%@ (%@)", listItem.name, listItem.region);
        }

        //Selecting some server
        self.selectedServerItem = [servers firstObject];
    }
});
How to connect to selected VPN server?

First, set up VPNUConfigurator instance.

self.configurator = [[VPNUConfigurator alloc] initWithTransport:self.transport];

Then you can set up the selected server item (see previous question on how to retrieve it). A profile for selected server item is downloaded every time the setupWithServerItem: method is called.

BOOL alreadyInstalled;
NSError *error = [self.configurator setupWithServerItem:self.selectedServerItem alreadyInstalled:&alreadyInstalled];
dispatch_sync(dispatch_get_main_queue(), ^{
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        // Now you can connect to VPN
    }
});

You then can try to connect to a selected server. Please pay attention to the fact that this method returns after VPN tunnel connection process is started and does not signals about the actual success of the VPN connection. In order to obtain the connection status, use the connectionStatus property of VPNUConfigurator. This method blocks a calling thread.

NSError *error;
BOOL started = [self.configurator startVPNWithError:&error];
dispatch_sync(dispatch_get_main_queue(), ^{
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        NSLog(@"Connection started: %d", started);
    }
});
How to perform a custom API request?

If you have to perform a custom request to one of VPN Unlimited API servers you create an object of VPNURequest class with one of its factory methods, set up its parameters and feed it to your sole VPNUTransport instance as described below:

VPNURequest *request = [VPNURequest authRequestWithAction:kKSVPNUFacadeActionChangePassword];
[request setParameterObject:oldPassword forKey:@"password"];
[request setParameterObject:newPassword forKey:@"newpassword"];

NSError *error;
id responseObject = [self.transport sendRequest:request error:&error];
Other versions: 1.1 / 1.2 / 1.9 / 1.9.2 / 2.0 / 2.1.0 / 2.2.0