Developers SDK

VPN Unlimited SDK v1.9 for iOS

Getting started

Overview

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

KeepSolid Wise Overview

https://www.vpnunlimitedapp.com/en/info/specials/keepsolid-wise

Setting up Project to Work with SDK

Drag and drop VPNUnlimitedSDK.framework into your project.

You have to include the VPNUnlimitedSDKResource.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):

  • AdSupport.framework
  • MobileCoreServices.framework
  • SystemConfiguration.framework
  • NetworkExtension.framework (use "Optional" if you compile for OS version prior to iOS 8)
  • 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.

On iOS 8+ you should also turn on "Personal VPN" entitlement in "Capabilities" section of your project settings.

Note:

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

  • libcurl
  • libopenssl
  • libcares
  • libcrypto
  • xopenssl

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

Setup

Setting Shared Keychain Group ID

In order to properly control the number of devices that were assigned to a particular account, SDK requires access to the Shared Keychain. For this, please enable Keychain Sharing Entitlement in your project settings, and pass the shared keychain group ID to SDK. The shared keychain group ID has the following format: $(AppIdentifierPrefix).yourKeychainGroupName. AppIdentifierPrefix (also known as Team ID) can be found in the Apple's Member Center > Certificates, Identifiers & Profiles > App IDs section.

[[KSVPNUFacade defaultFacade] setSharedKeychainGroupID:@"A1B2C3D4E5.mySharedKeychain"];

Setting custom name for VPN profiles

You can set custom name for a downloaded VPN profile using NSString *profileName property of your instance of VPNUConfigurator class. If you do not do so before any calls to this instance, the default name ("VPN Unlimited") will be used.

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 KSVPNUFacadesingleton. Please check the documentation for this class: it is convenient for most applications.

SDK Initialization

The following initialization sequence must be performed before invoking any other API method.

[VPNURequest setApplicationBundleID:@"com.yourcompany.yourappname"];
[[KSVPNUFacade defaultFacade] setApplicationID:@"Your application ID"];
[[KSVPNUFacade defaultFacade] setApplicationVersion:@"Your application version"];
[[KSVPNUFacade defaultFacade] setApplicationSecret:@"Your application secret"];
[[VPNUStorage defaultStorage] setEncryptionKey:@"Long encryption key to be used throughout the app"];
[KSVPNUFacade defaultFacade].configurator.profileName = @"VPNUnlimitedSDKExample Profile";
[KSVPNUFacade defaultFacade].configurator.customProtocolProfileName = @"VPNUnlimitedSDKExample Profile Wise";

Registration, Authorization, Password Recovery

This method tries to register a new user with specified login and password synchronously and waits for the response.
If registration is successful, it automatically logs in the newly created user.

[[KSVPNUFacade defaultFacade] registerWithLogin:emailString

    andPassword:passwordString

completion:^(NSError *error){

if (error){

NSLog(@"Error: %@", error.localizedDescription);

}

else{

                                //do stuff

}

}];

This method tries to authorize a user with specified login and password. If authorization is successful it automatically sets the currentUserInfo property.

[[KSVPNUFacade defaultFacade] authorizeWithLogin:emailString

       andPassword:passwordString

        completion:^(NSError *error){

if (error){

NSLog(@"Error: %@", error.localizedDescription);

}else{

                                                //do stuff

}

}];

If authorization is successful, KSVPNUFacade instance is managing the newly created session and reconnects automatically when this session is expired.

Instructions are sent to the email of user specified in  login parameter.

[[KSVPNUFacade defaultFacade] recoveryPasswordForLogin:emailString completion:^(NSError *error){

        if (error){

            NSLog(@"Error: %@", error.localizedDescription);

       }else {

            //do stuff

       }

   }];

Obtaining User Information

Retrieves the currently logged-in user's account status

[[KSVPNUFacade defaultFacade] accountStatusWithCompletion:^(NSError *error, VPNUAccountStatus *accountStatus){

if (error){

NSLog(@"Error: %@", error.localizedDescription);

}else{

NSLog(@”%@”, [KSVPNUFacade defaultFacade].currentAccountStatus.country);

NSLog(@”%@”, [KSVPNUFacade defaultFacade].currentAccountStatus.currentRegion);

NSLog(@”%@”, [KSVPNUFacade defaultFacade].currentAccountStatus.realIP;);

NSLog(@”%@”, [KSVPNUFacade defaultFacade].currentAccountStatus.serverIP);

}

}];

Obtaining Server List

Retrieves a list of available VPN servers for currently logged-in user

[[KSVPNUFacade defaultFacade] serverListWithOptions:0 completion:^(NSError *error, NSArray<VPNUServerItem *> *serversList){

if (error){

        NSLog(@"Error: %@", error.localizedDescription);

}

else{

                NSLog(@"%@", serversList);

}

}];

How to Connect to Selected VPN Server?

Configuring VPN Connection

VPN Unlimited SDK uses Apple's NEVPNManager class. The setupWithServerItem: method will download the general VPN profile and ask user to install it into his or hers system. This will be performed only once, if user accepts the downloaded profile.

[[KSVPNUFacade defaultFacade] setupVPNWithServerItem:serverItem completion:^(NSError *error, BOOL successfullyInstalled){

if (error){

        NSLog(@"Error: %@", error.localizedDescription);

}

else{

if (alreadyInstalled){

NSLog(@”successfully reconfigured existing VPN configuration”);

}

else{

NSLog(@”successfully created new VPN configuration”);

}

}

}];

Connecting to VPN Server

Now when VPN is successfully set up you may connect to it.

[[KSVPNUFacade defaultFacade] connectToVPNWithCompletion:^(NSError *error){

if (error){

NSLog(@"Error: %@", error.localizedDescription);

}

else{

        NSLog(@”now you are connected to VPN server”);

}

}];

The completion of this method returns after VPN tunnel connection process is finished 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.

Working with Purchases

Enrolling into paid services is performed in two steps. At first use user has to perform In-App Purchase. At second AppStore receipt must be validated on our servers. The most important thing that you have to keep in mind is that application itself is entirely responsible for validation.

Application should store receipt and all auxiliary data in persistent until validation is actually occur on server.

        getting personal servers regions list

Retrieves a list of available personal VPN servers for currently logged-in user

[[KSVPNUFacade defaultFacade] personalServerRegionsList:^(NSError *error, NSDictionary *result){

       if (error){

                NSLog(@"Error: %@", error.localizedDescription);

       }

else{

NSArray *servers=[result objectForKey:@"servers"];

if (servers){

        servers=[servers arrayByRemovingDuplicates];

NSLog(@"%@", servers);

}

}

}];

        getting personal IPs regions list

Retrieves a list of available personal IP servers for currently logged-in user

[[KSVPNUFacade defaultFacade] personalIPServersList:^(NSError *error, NSDictionary *result){

if (error){

                NSLog(@"Error: %@", error.localizedDescription);

       }

else{

NSArray *servers=[result objectForKey:@"servers"];

if (servers){

        servers=[servers arrayByRemovingDuplicates];

NSLog(@"%@", servers);

}

}

}];

        validating payment

Following information is required for validating purchase on server:

transaction receipt

purchase price locale

purchase region (only applicable for personal IPs and personal servers; pass empty for rest)

Purchase region can be retrieved from the data returned by personalServerRegionsList or personalIPServersList calls.

All this information has to be kept in persistent storage until validation request gets satisfiable response. Satisfiable responses are either absence of error (which means that purchase was successfully validated and applied by server) or error with one of the following codes:

341 - purchase was already validated

343 - purchase has invalid identifier; it was likely not registered in our data base

In all these cases purchase has to be removed from cache, in all other cases (that often are related to connection problems) validation has to be rescheduled within some reasonably small time interval.

Validates a provided purchase item on server

- (void)validatePayment:(SKPaymentTransaction *)transaction forProduct:(SKProduct*)product atRegion:(NSString*)region{

NSString *receiptString=@"";

#if TARGET_OS_IPHONE

        receiptString=[transaction transactionReceipt] base64EncodedString];

#else

NSString *appReceiptPath=[[[NSBundle mainBundle] appStoreReceiptURL] path];

receiptString=[[NSData dataWithContentsOfFile:appReceiptPath] base64EncodedString];

#endif

NSString *priceLocaleString=[[(SKProduct*)product priceLocale] objectForKey:NSLocaleCountryCode];

 CFUUIDRef uuid=CFUUIDCreate(kCFAllocatorDefault);

NSString *uuidString=(__bridge NSString*)CFUUIDCreateString(kCFAllocatorDefault, uuid);

CFRelease(uuid);

purchaseItem.receipt=receiptString;

purchaseItem.purchaseID=uuidString;

purchaseItem.priceLocale=priceLocaleString;

purchaseItem.purchaseCountryCode=region;

[[KSVPNUFacade defaultFacade] validatePurchaseItem:purchiseItem completion:^(NSError *error){

if (error){

switch(error.code){

case 341:

NSLog(@"Purchase was already validated.");

break;

case 343:

NSLog(@"Purchase is invalid. Probably it is not registered in our DB.");

break;

default:

NSLog(@"Error is not related to purchase itself. You likely have to perform validation once again.");

break;

}

}

else{

NSLog(@"Validation was successful. Now you can clean cached purchase info.");

}

}];

}

Other versions: 1.1 / 1.7 / 1.10 / 1.11 / 1.12 / 2.0 / 2.1.0