Saturday, June 7, 2014

Using RNCryptor Library with Apple’s new Swift language

Mix and match allows us to create mixed-language apps containing both Swift and Objective-C files that can communicate with each other.  This allows us to take existing Objective-C libraries and use them in our new Swift projects.  In this post I will show how you can integrate the RNCryptor library with your Swift project.

So lets get started.  You will need XCode beta 6 to work with Swift so if you have not already downloaded/installed it you will need to do so now.   You will also need to download the RNCryptor code, you can do that from GitHub here.   Once you have XCode 6 beta set up start a new project and select the Single View Application template.




On the next option screen, enter the name of your project and then select Swift as the language.



Finally select the location of your project and now you are ready to start.  The first thing you will want to do is to copy the RNCryptor source files to your project.  You can do this by highlighting the files to copy and then drag them into your project.



After you drop the files into your project, XCode will ask if you would like to configure an Objective-C bridging header, select Yes to create the header.  The bridging header will be named like this {project name}-Bridging-Header.h.

Now we will need to add two of the RNCryptor header files to the bridging header file, this will make the classes available to our Swift classes.  Add the following lines to you bridging header:

#import "RNEncryptor.h"
#import "RNDecryptor.h"

I ran into two problems adding RNCryptor to my Swift project.  The first problem was I could not access the constants defined in the RNCryptor.h header file even if I added it to the bridging header.  I am encrypting the data using the encryptData:withSettings:password:error: method from the RNEncryptor class.  This method takes an RNCryptorSettings object for the withSettings parameter however these settings are defined as constants in the RNCryptor header file which I was unable to access.  The second problem I had was trying to convert an NSData object to a NSString. 

I am unsure if there is a better “Swift” way to solve these problems but as a work around I created a MyRNEncryptor Objective-C class that extends the RNEncryptor class.  The MyRNEncryptor header file should contain the following code:

#import "RNEncryptor.h"

@interface MyRNEncryptor : RNEncryptor

+ (NSData *)encryptData:(NSData *)data password:(NSString *)password error:(NSError **)error;
+ (NSString *)stringFromData:(NSData *)data;

@end 

While the RNEncryptor implementation file should contain this code:

#import "MyRNEncryptor.h"

@implementation MyRNEncryptor

+ (NSData *)encryptData:(NSData *)data password:(NSString *)password error:(NSError **)error {
   
    return [self encryptData:data withSettings:kRNCryptorAES256Settings password:password error:error];
}

+ (NSString *)stringFromData:(NSData *)data {
    return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}

@end

The encryptData:password:error: method simply calls the encryptData:withSettings:password:error: method but adds the RNCryptorSettings.  This solves our first problem defined above.  The second function stringFromData: uses the NSString initWithData:encoding: constructor to convert the NSData object to a NSString.

Now we need to create our Encryption API class with Swift.  Create a new Cocoa Touch Class named MyCrypt and make sure that the language is set to Swift.



In the MyCrypt.swift class we will create two functions, one to encrypt a NSString object and the other to decrypt a NSData object.  We will also define one constant that will be our encryption key.  In a production application you would not want to define your encryption key as a constant like this since it is very easy for someone to get it but this post is written mainly to show how to integrate the RNCryptor with your Swift product so hardcoding our key makes the example a lot simpler to read and understand.  Add the following code the MyCrypt.swift file:

 let key = "MySecretPassword"
   
    func encryptString(estring: String) -> NSData {
        var edata = MyRNEncryptor.encryptData(estring.dataUsingEncoding(NSUTF8StringEncoding,         allowLossyConversion: true), password: key, error: nil)
       
        return edata
    }
   
    func decryptData(edata: NSData) -> String {
       
        var pdata = RNDecryptor.decryptData(edata, withPassword: key, error: nil)
        var pstring: String = MyRNEncryptor.stringFromData(pdata)
        return pstring
    }

The first line creates a constant key that contains our encryption key.  We then define a function called encryptString that accepts one String parameter and returns an NSData object.  This function calls our encryptData:password:error: method that we created in the MyRNEncryptor class.  This is where things get a little weird, if you ask me.  Normally if we were to call this method from an Objective-C class we would use this syntax:

NSError *error;
[MyRNEncryptor encryptData:myData password:myPass error:&error];

In the code above we use the following syntax to call our encryptData:password:error: method (Note: I simplified the above code, by taking out the NSString to NSData conversion, to make it easier to see how we are calling the Objective-C method):

MyRNEncryptor.encryptData(myData, password: key, error: nil)

It is pretty easy to see the difference between the two calls.  The way that you call functions in Swift is a lot closer to how other languages (like Java and C) however Apple did keep how Objective-C defines the parameters that you are sending.  Personally, I like how Apple did this but I know that others will not.  It is a matter of personal preference.

Now that we have our encryptString and decryptData API methods, lets open up our ViewController.swift file and try to use them.  Add the following code to the viewDidLoad function of the ViewController.swift file.

var crypt = MyCrypt()
var edata: NSData = crypt.encryptString("Hello from swift my little swifty")
println(edata)
var pdata: String = crypt.decryptData(edata)
println(pdata)

The first line creates an instance of the MyCrypt class.  We then encrypt the string “Hello from swift my little swifty” and print the NSData object to the screen.  We then decrypt the data and print the decrypted string. 


This post documents my first attempt to use an Objective-C library with Swift.  If anyone else has played with this and found better ways to do anything documented here, please leave a comment.

6 comments:

  1. Hello Jon

    Thanks for posting something valuable on here in regards to Swift and RNCryptor. I was banging my head against the wall. While I still haven't go it to work, it looks like there is hope.

    Would you mind attaching a hello world demonstrating this? I tried following your instructions and I couldn't get it to work. I tried it in iOS and OS X. If possible could you provide an OS X sample? I really would appreciate it.

    ReplyDelete
    Replies
    1. Nevermind, I'm a dumb ass. Tweaked it some for the OS X hello world project and I'm well now.

      THANK YOU VERY MUCH for your posting!

      Delete
    2. No problem. That seems to happen to me too, as soon as I post a question I seem to figure it out. Glad that the post helped you.

      Delete
  2. I want to download RNCryptor Library in Objective C.

    Anybody can give me a link?

    Thanks in advance!

    ReplyDelete
  3. Look at the github page here: https://github.com/RNCryptor/RNCryptor

    ReplyDelete
  4. This Library is working fine for latest Swift version?

    ReplyDelete