From 719eb543a8d08c4f536ea7933ffb3af0a8553e87 Mon Sep 17 00:00:00 2001
From: Chris Ballinger <chrisballinger@gmail.com>
Date: Fri, 8 Apr 2016 17:24:41 -0700
Subject: Xcode, podspec, wrapper

---
 .gitignore                                         |  12 +
 include/olm/olm.hh                                 |   8 +-
 javascript/README.md                               |   2 +-
 olm.podspec                                        |  98 ++++
 xcode/OLMKit/Info.plist                            |  26 +
 xcode/OLMKit/OLMAccount.h                          |  37 ++
 xcode/OLMKit/OLMAccount.m                          | 116 +++++
 xcode/OLMKit/OLMAccount_Private.h                  |  15 +
 xcode/OLMKit/OLMKit.h                              |  23 +
 xcode/OLMKit/OLMMessage.h                          |  24 +
 xcode/OLMKit/OLMMessage.m                          |  13 +
 xcode/OLMKit/OLMSerializable.h                     |  19 +
 xcode/OLMKit/OLMSession.h                          |  29 ++
 xcode/OLMKit/OLMSession.m                          |  30 ++
 xcode/OLMKit/OLMUtility.h                          |  15 +
 xcode/OLMKit/OLMUtility.m                          |  28 ++
 xcode/OLMKitTests/Info.plist                       |  24 +
 xcode/OLMKitTests/OLMKitTests.m                    |  41 ++
 xcode/Podfile                                      |   6 +
 xcode/Podfile.lock                                 |  14 +
 xcode/olm.xcodeproj/project.pbxproj                | 557 +++++++++++++++++++++
 .../xcshareddata/xcschemes/OLMKit.xcscheme         |  99 ++++
 .../xcshareddata/xcschemes/OLMKitTests.xcscheme    |  90 ++++
 xcode/olm.xcworkspace/contents.xcworkspacedata     |  10 +
 24 files changed, 1331 insertions(+), 5 deletions(-)
 create mode 100644 olm.podspec
 create mode 100644 xcode/OLMKit/Info.plist
 create mode 100644 xcode/OLMKit/OLMAccount.h
 create mode 100644 xcode/OLMKit/OLMAccount.m
 create mode 100644 xcode/OLMKit/OLMAccount_Private.h
 create mode 100644 xcode/OLMKit/OLMKit.h
 create mode 100644 xcode/OLMKit/OLMMessage.h
 create mode 100644 xcode/OLMKit/OLMMessage.m
 create mode 100644 xcode/OLMKit/OLMSerializable.h
 create mode 100644 xcode/OLMKit/OLMSession.h
 create mode 100644 xcode/OLMKit/OLMSession.m
 create mode 100644 xcode/OLMKit/OLMUtility.h
 create mode 100644 xcode/OLMKit/OLMUtility.m
 create mode 100644 xcode/OLMKitTests/Info.plist
 create mode 100644 xcode/OLMKitTests/OLMKitTests.m
 create mode 100644 xcode/Podfile
 create mode 100644 xcode/Podfile.lock
 create mode 100644 xcode/olm.xcodeproj/project.pbxproj
 create mode 100644 xcode/olm.xcodeproj/xcshareddata/xcschemes/OLMKit.xcscheme
 create mode 100644 xcode/olm.xcodeproj/xcshareddata/xcschemes/OLMKitTests.xcscheme
 create mode 100644 xcode/olm.xcworkspace/contents.xcworkspacedata

diff --git a/.gitignore b/.gitignore
index 378eac2..1a56246 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,13 @@
 build
+.DS_Store
+build
+*.mode1v3
+*.pbxuser
+project.xcworkspace
+xcuserdata
+.svn
+DerivedData
+*.orig
+*.xccheckout
+
+xcode/Pods/
diff --git a/include/olm/olm.hh b/include/olm/olm.hh
index 34d84fd..bee1ae4 100644
--- a/include/olm/olm.hh
+++ b/include/olm/olm.hh
@@ -25,9 +25,9 @@ extern "C" {
 static const size_t OLM_MESSAGE_TYPE_PRE_KEY = 0;
 static const size_t OLM_MESSAGE_TYPE_MESSAGE = 1;
 
-struct OlmAccount;
-struct OlmSession;
-struct OlmUtility;
+typedef struct OlmAccount OlmAccount;
+typedef struct OlmSession OlmSession;
+typedef struct OlmUtility OlmUtility;
 
 /** The size of an account object in bytes */
 size_t olm_account_size();
@@ -51,7 +51,7 @@ OlmSession * olm_session(
 );
 
 /** Initialise a utility object using the supplied memory
- *  The supplied memory must be at least olm_session_size() bytes */
+ *  The supplied memory must be at least olm_utility_size() bytes */
 OlmUtility * olm_utility(
     void * memory
 );
diff --git a/javascript/README.md b/javascript/README.md
index 5c2c96b..6902e36 100644
--- a/javascript/README.md
+++ b/javascript/README.md
@@ -22,4 +22,4 @@ Example:
 
     bob_session.create_inbound(bob, bob_message);
     var plaintext = bob_session.decrypt(message_1.type, bob_message);
-    bob.remove_one_time_keys(bob_session);
+    bob.remove_one_time_keys(bob_session);
\ No newline at end of file
diff --git a/olm.podspec b/olm.podspec
new file mode 100644
index 0000000..6e366d2
--- /dev/null
+++ b/olm.podspec
@@ -0,0 +1,98 @@
+#
+#  Be sure to run `pod spec lint olm.podspec' to ensure this is a
+#  valid spec and to remove all comments including this before submitting the spec.
+#
+#  To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
+#  To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
+#
+
+Pod::Spec.new do |s|
+
+  # ―――  Spec Metadata  ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
+  #
+  #  These will help people to find your library, and whilst it
+  #  can feel like a chore to fill in it's definitely to your advantage. The
+  #  summary should be tweet-length, and the description more in depth.
+  #
+
+  s.name         = "olm"
+  s.version      = "0.1.0"
+  s.summary      = "olm"
+
+  # This description is used to generate tags and improve search results.
+  #   * Think: What does it do? Why did you write it? What is the focus?
+  #   * Try to keep it short, snappy and to the point.
+  #   * Write the description between the DESC delimiters below.
+  #   * Finally, don't worry about the indent, CocoaPods strips it!
+  s.description  = <<-DESC
+                   DESC
+
+  s.homepage     = "http://EXAMPLE/olm"
+  # s.screenshots  = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
+
+
+  # ―――  Spec License  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
+  #
+  #  Licensing your code is important. See http://choosealicense.com for more info.
+  #  CocoaPods will detect a license file if there is a named LICENSE*
+  #  Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
+  #
+
+  s.license      = "MIT (example)"
+  # s.license      = { :type => "MIT", :file => "FILE_LICENSE" }
+
+
+  # ――― Author Metadata  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
+  #
+  #  Specify the authors of the library, with email addresses. Email addresses
+  #  of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
+  #  accepts just a name if you'd rather not provide an email address.
+  #
+  #  Specify a social_media_url where others can refer to, for example a twitter
+  #  profile URL.
+  #
+
+  s.author             = { "Chris Ballinger" => "chrisballinger@gmail.com" }
+  # Or just: s.author    = "Chris Ballinger"
+  # s.authors            = { "Chris Ballinger" => "chrisballinger@gmail.com" }
+  # s.social_media_url   = "http://twitter.com/Chris Ballinger"
+
+  # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
+  #
+  #  If this Pod runs only on iOS or OS X, then specify the platform and
+  #  the deployment target. You can optionally include the target after the platform.
+  #
+
+  # s.platform     = :ios
+  # s.platform     = :ios, "5.0"
+
+  #  When using multiple platforms
+  # s.ios.deployment_target = "5.0"
+  # s.osx.deployment_target = "10.7"
+  # s.watchos.deployment_target = "2.0"
+  # s.tvos.deployment_target = "9.0"
+
+
+  # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
+  #
+  #  Specify the location from where the source should be retrieved.
+  #  Supports git, hg, bzr, svn and HTTP.
+  #
+
+  s.source       = { :git => "http://EXAMPLE/olm.git", :tag => "0.0.1" }
+
+
+  # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
+  #
+  #  CocoaPods is smart about how it includes source code. For source files
+  #  giving a folder will include any swift, h, m, mm, c & cpp files.
+  #  For header files it will include any header in the folder.
+  #  Not including the public_header_files will make all headers public.
+  #
+
+  s.source_files  = ["include/olm/*.hh","src/*.cpp"]
+  s.public_header_files = "include/olm/olm.hh"
+
+  s.library = "c++"
+  s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(PODS_ROOT)/olm/include $(PODS_ROOT)/olm/lib $(PODS_ROOT)/../../include $(PODS_ROOT)/../../lib' }
+end
diff --git a/xcode/OLMKit/Info.plist b/xcode/OLMKit/Info.plist
new file mode 100644
index 0000000..d3de8ee
--- /dev/null
+++ b/xcode/OLMKit/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/xcode/OLMKit/OLMAccount.h b/xcode/OLMKit/OLMAccount.h
new file mode 100644
index 0000000..cfa7129
--- /dev/null
+++ b/xcode/OLMKit/OLMAccount.h
@@ -0,0 +1,37 @@
+//
+//  OLMAccount.h
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import <Foundation/Foundation.h>
+#import "OLMSerializable.h"
+
+@interface OLMAccount : NSObject <OLMSerializable>
+
+/** Creates new account */
+- (instancetype) initNewAccount;
+
+/** public identity keys. base64 encoded in "curve25519" and "ed25519" keys */
+- (NSDictionary*) identityKeys;
+
+/** signs message with ed25519 key for account */
+- (NSData*) signMessage:(NSData*)messageData;
+
+/** Public parts of the unpublished one time keys for the account */
+- (NSDictionary*) oneTimeKeys;
+
+/** Marks the current set of one time keys as being published. */
+- (void) markKeysAsPublished;
+
+/** The largest number of one time keys this account can store. */
+- (NSUInteger) maxOneTimeKeys;
+
+/** Generates a number of new one time keys. If the total number of keys stored
+ * by this account exceeds -maxOneTimeKeys then the old keys are
+ * discarded. */
+- (void) generateOneTimeKeys:(NSUInteger)numberOfKeys;
+
+@end
diff --git a/xcode/OLMKit/OLMAccount.m b/xcode/OLMKit/OLMAccount.m
new file mode 100644
index 0000000..58dd4ad
--- /dev/null
+++ b/xcode/OLMKit/OLMAccount.m
@@ -0,0 +1,116 @@
+//
+//  OLMAccount.m
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import "OLMAccount.h"
+#import "OLMAccount_Private.h"
+#import "OLMUtility.h"
+
+@import Security;
+
+@implementation OLMAccount
+
+- (void) dealloc {
+    olm_clear_account(_account);
+    free(_account);
+}
+
+- (BOOL) initializeAccountMemory {
+    size_t accountSize = olm_account_size();
+    _account = malloc(accountSize);
+    NSParameterAssert(_account != nil);
+    if (!_account) {
+        return NO;
+    }
+    _account = olm_account(_account);
+    NSParameterAssert(_account != nil);
+    if (!_account) {
+        return NO;
+    }
+    return YES;
+}
+
+- (instancetype) initNewAccount {
+    self = [super init];
+    if (!self) {
+        return nil;
+    }
+    BOOL success = [self initializeAccountMemory];
+    if (!success) {
+        return nil;
+    }
+    size_t randomLength = olm_create_account_random_length(_account);
+    size_t accountResult = olm_create_account(_account, (void*)[OLMUtility randomBytesOfLength:randomLength].bytes, randomLength);
+    if (accountResult == olm_error()) {
+        const char *error = olm_account_last_error(_account);
+        NSLog(@"error creating account: %s", error);
+        return nil;
+    }
+    return self;
+}
+
+- (size_t) maxOneTimeKeys {
+    return olm_account_max_number_of_one_time_keys(_account);
+}
+
+
+/** public identity keys */
+- (NSDictionary*) identityKeys {
+    size_t identityKeysLength = olm_account_identity_keys_length(_account);
+    uint8_t *identityKeysBytes = malloc(identityKeysLength);
+    if (!identityKeysBytes) {
+        return nil;
+    }
+    size_t result = olm_account_identity_keys(_account, identityKeysBytes, identityKeysLength);
+    if (result == olm_error()) {
+        const char *error = olm_account_last_error(_account);
+        NSLog(@"error getting id keys: %s", error);
+        free(identityKeysBytes);
+        return nil;
+    }
+    NSData *idKeyData = [NSData dataWithBytesNoCopy:identityKeysBytes length:identityKeysLength freeWhenDone:YES];
+    NSError *error = nil;
+    NSDictionary *keysDictionary = [NSJSONSerialization JSONObjectWithData:idKeyData options:0 error:&error];
+    if (error) {
+        NSLog(@"Could not decode JSON: %@", error.localizedDescription);
+    }
+    return keysDictionary;
+}
+
+- (NSDictionary*) oneTimeKeys {
+    size_t otkLength = olm_account_one_time_keys_length(_account);
+    uint8_t *otkBytes = malloc(otkLength);
+    if (!otkBytes) {
+        return nil;
+    }
+    size_t result = olm_account_one_time_keys(_account, otkBytes, otkLength);
+    if (result == olm_error()) {
+        const char *error = olm_account_last_error(_account);
+        NSLog(@"error getting id keys: %s", error);
+        free(otkBytes);
+    }
+    NSData *otk = [NSData dataWithBytesNoCopy:otkBytes length:otkLength freeWhenDone:YES];
+    NSError *error = nil;
+    NSDictionary *keysDictionary = [NSJSONSerialization JSONObjectWithData:otk options:0 error:&error];
+    if (error) {
+        NSLog(@"Could not decode JSON: %@", error.localizedDescription);
+    }
+    return keysDictionary;
+}
+
+
+- (void) generateOneTimeKeys:(NSUInteger)numberOfKeys {
+    size_t randomLength = olm_account_generate_one_time_keys_random_length(_account, numberOfKeys);
+    size_t result = olm_account_generate_one_time_keys(_account, numberOfKeys, (void*)[OLMUtility randomBytesOfLength:randomLength].bytes, randomLength);
+    if (result == olm_error()) {
+        const char *error = olm_account_last_error(_account);
+        NSLog(@"error generating keys: %s", error);
+    }
+}
+
+
+@end
diff --git a/xcode/OLMKit/OLMAccount_Private.h b/xcode/OLMKit/OLMAccount_Private.h
new file mode 100644
index 0000000..4eb3e2b
--- /dev/null
+++ b/xcode/OLMKit/OLMAccount_Private.h
@@ -0,0 +1,15 @@
+//
+//  OLMAccount_Private.h
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+@import olm;
+
+@interface OLMAccount()
+
+@property (nonatomic) OlmAccount *account;
+
+@end
\ No newline at end of file
diff --git a/xcode/OLMKit/OLMKit.h b/xcode/OLMKit/OLMKit.h
new file mode 100644
index 0000000..745af43
--- /dev/null
+++ b/xcode/OLMKit/OLMKit.h
@@ -0,0 +1,23 @@
+//
+//  OLMKit.h
+//  OLMKit
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import <UIKit/UIKit.h>
+
+//! Project version number for OLMKit.
+FOUNDATION_EXPORT double OLMKitVersionNumber;
+
+//! Project version string for OLMKit.
+FOUNDATION_EXPORT const unsigned char OLMKitVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <OLMKit/PublicHeader.h>
+
+
+#import "OLMAccount.h"
+#import "OLMSession.h"
+#import "OLMMessage.h"
+#import "OLMUtility.h"
\ No newline at end of file
diff --git a/xcode/OLMKit/OLMMessage.h b/xcode/OLMKit/OLMMessage.h
new file mode 100644
index 0000000..2b747fb
--- /dev/null
+++ b/xcode/OLMKit/OLMMessage.h
@@ -0,0 +1,24 @@
+//
+//  OLMMessage.h
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+typedef NS_ENUM(NSUInteger, OLMMessageType) {
+    OLMMessageTypeUnknown,
+    OLMMessageTypePreKey,
+    OLMMessageTypeMessage
+};
+
+@interface OLMMessage : NSObject
+
+@property (nonatomic, readonly, nonnull) NSString *message;
+@property (readonly) OLMMessageType type;
+
+- (nonnull instancetype) initWithMessage:(nonnull NSString*)message type:(OLMMessageType)type;
+
+@end
diff --git a/xcode/OLMKit/OLMMessage.m b/xcode/OLMKit/OLMMessage.m
new file mode 100644
index 0000000..ce732ec
--- /dev/null
+++ b/xcode/OLMKit/OLMMessage.m
@@ -0,0 +1,13 @@
+//
+//  OLMMessage.m
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import "OLMMessage.h"
+
+@implementation OLMMessage
+
+@end
diff --git a/xcode/OLMKit/OLMSerializable.h b/xcode/OLMKit/OLMSerializable.h
new file mode 100644
index 0000000..afacdaa
--- /dev/null
+++ b/xcode/OLMKit/OLMSerializable.h
@@ -0,0 +1,19 @@
+//
+//  OLMSerializable.h
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol OLMSerializable <NSObject>
+
+/** Initializes from encrypted serialized data. Will throw error if invalid key or invalid base64. */
+- (instancetype) initWithSerializedData:(NSData*)serializedData key:(NSData*)key error:(NSError**)error;
+
+/** Serializes and encrypts object data */
+- (NSData*) serializeDataWithKey:(NSData*)key;
+
+@end
diff --git a/xcode/OLMKit/OLMSession.h b/xcode/OLMKit/OLMSession.h
new file mode 100644
index 0000000..196900f
--- /dev/null
+++ b/xcode/OLMKit/OLMSession.h
@@ -0,0 +1,29 @@
+//
+//  OLMSession.h
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import <Foundation/Foundation.h>
+#import "OLMSerializable.h"
+#import "OLMAccount.h"
+
+@interface OLMSession : NSObject <OLMSerializable>
+
+- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey theirOneTimeKey:(NSData*)theirOneTimeKey;
+
+- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSData*)oneTimeKeyMessage;
+
+- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey oneTimeKeyMessage:(NSData*)oneTimeKeyMessage;
+
+- (NSData*) sessionIdentifier;
+
+- (BOOL) matchesInboundSession:(NSData*)oneTimeKeyMessage;
+
+- (BOOL) matchesInboundSessionFrom:(NSData*)theirIdentityKey oneTimeKeyMessage:(NSData *)oneTimeKeyMessage;
+
+- (void) removeOneTimeKeys;
+
+@end
diff --git a/xcode/OLMKit/OLMSession.m b/xcode/OLMKit/OLMSession.m
new file mode 100644
index 0000000..24a8b36
--- /dev/null
+++ b/xcode/OLMKit/OLMSession.m
@@ -0,0 +1,30 @@
+//
+//  OLMSession.m
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import "OLMSession.h"
+@import olm;
+
+@interface OLMSession()
+@property (nonatomic) OlmSession *session;
+@end
+
+@implementation OLMSession
+
+- (instancetype) initOutboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey theirOneTimeKey:(NSData*)theirOneTimeKey {
+    
+}
+
+- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account oneTimeKeyMessage:(NSData*)oneTimeKeyMessage {
+    
+}
+
+- (instancetype) initInboundSessionWithAccount:(OLMAccount*)account theirIdentityKey:(NSData*)theirIdentityKey oneTimeKeyMessage:(NSData*)oneTimeKeyMessage {
+    
+}
+
+@end
diff --git a/xcode/OLMKit/OLMUtility.h b/xcode/OLMKit/OLMUtility.h
new file mode 100644
index 0000000..0de9725
--- /dev/null
+++ b/xcode/OLMKit/OLMUtility.h
@@ -0,0 +1,15 @@
+//
+//  OLMUtility.h
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+@interface OLMUtility : NSObject
+
++ (NSData*) randomBytesOfLength:(NSUInteger)length;
+
+@end
diff --git a/xcode/OLMKit/OLMUtility.m b/xcode/OLMKit/OLMUtility.m
new file mode 100644
index 0000000..0148932
--- /dev/null
+++ b/xcode/OLMKit/OLMUtility.m
@@ -0,0 +1,28 @@
+//
+//  OLMUtility.m
+//  olm
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import "OLMUtility.h"
+
+@implementation OLMUtility
+
++ (NSData*) randomBytesOfLength:(NSUInteger)length {
+    uint8_t *randomBytes = malloc(length * sizeof(uint8_t));
+    NSParameterAssert(randomBytes != NULL);
+    if (!randomBytes) {
+        return nil;
+    }
+    int result = SecRandomCopyBytes(kSecRandomDefault, length, randomBytes);
+    if (result != 0) {
+        free(randomBytes);
+        return nil;
+    }
+    NSData *data = [NSData dataWithBytesNoCopy:randomBytes length:length freeWhenDone:YES];
+    return data;
+}
+
+@end
diff --git a/xcode/OLMKitTests/Info.plist b/xcode/OLMKitTests/Info.plist
new file mode 100644
index 0000000..ba72822
--- /dev/null
+++ b/xcode/OLMKitTests/Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/xcode/OLMKitTests/OLMKitTests.m b/xcode/OLMKitTests/OLMKitTests.m
new file mode 100644
index 0000000..944d11c
--- /dev/null
+++ b/xcode/OLMKitTests/OLMKitTests.m
@@ -0,0 +1,41 @@
+//
+//  OLMKitTests.m
+//  OLMKitTests
+//
+//  Created by Chris Ballinger on 4/8/16.
+//
+//
+
+#import <XCTest/XCTest.h>
+@import OLMKit;
+
+@interface OLMKitTests : XCTestCase
+
+@end
+
+@implementation OLMKitTests
+
+- (void)setUp {
+    [super setUp];
+    // Put setup code here. This method is called before the invocation of each test method in the class.
+}
+
+- (void)tearDown {
+    // Put teardown code here. This method is called after the invocation of each test method in the class.
+    [super tearDown];
+}
+
+- (void)testExample {
+    // This is an example of a functional test case.
+    // Use XCTAssert and related functions to verify your tests produce the correct results.
+    OLMAccount *alice = [[OLMAccount alloc] initNewAccount];
+    OLMAccount *bob = [[OLMAccount alloc] initNewAccount];
+    [bob generateOneTimeKeys:5];
+    NSDictionary *identityKeys = bob.identityKeys;
+    NSDictionary *oneTimeKeys = bob.oneTimeKeys;
+    NSParameterAssert(identityKeys != nil);
+    NSParameterAssert(oneTimeKeys != nil);
+}
+
+
+@end
diff --git a/xcode/Podfile b/xcode/Podfile
new file mode 100644
index 0000000..7bd8189
--- /dev/null
+++ b/xcode/Podfile
@@ -0,0 +1,6 @@
+# Uncomment this line to define a global platform for your project
+platform :ios, '8.0'
+# Uncomment this line if you're using Swift
+use_frameworks!
+
+pod 'olm', :path => '../olm.podspec'
\ No newline at end of file
diff --git a/xcode/Podfile.lock b/xcode/Podfile.lock
new file mode 100644
index 0000000..4639dc0
--- /dev/null
+++ b/xcode/Podfile.lock
@@ -0,0 +1,14 @@
+PODS:
+  - olm (0.1.0)
+
+DEPENDENCIES:
+  - olm (from `../olm.podspec`)
+
+EXTERNAL SOURCES:
+  olm:
+    :path: "../olm.podspec"
+
+SPEC CHECKSUMS:
+  olm: ba8c1a7ca04486ec1ad958ec9feef73c0939ae27
+
+COCOAPODS: 0.39.0
diff --git a/xcode/olm.xcodeproj/project.pbxproj b/xcode/olm.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..b1a62b0
--- /dev/null
+++ b/xcode/olm.xcodeproj/project.pbxproj
@@ -0,0 +1,557 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		D976E4411CB852E000F5C124 /* OLMKit.h in Headers */ = {isa = PBXBuildFile; fileRef = D976E4401CB852E000F5C124 /* OLMKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		D976E4481CB852E000F5C124 /* OLMKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D976E43E1CB852E000F5C124 /* OLMKit.framework */; };
+		D976E44D1CB852E000F5C124 /* OLMKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D976E44C1CB852E000F5C124 /* OLMKitTests.m */; };
+		D976E4571CB8536500F5C124 /* OLMAccount.h in Headers */ = {isa = PBXBuildFile; fileRef = D976E4551CB8536500F5C124 /* OLMAccount.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		D976E4581CB8536500F5C124 /* OLMAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = D976E4561CB8536500F5C124 /* OLMAccount.m */; };
+		D976E45B1CB8538300F5C124 /* OLMSession.h in Headers */ = {isa = PBXBuildFile; fileRef = D976E4591CB8538300F5C124 /* OLMSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		D976E45C1CB8538300F5C124 /* OLMSession.m in Sources */ = {isa = PBXBuildFile; fileRef = D976E45A1CB8538300F5C124 /* OLMSession.m */; };
+		D976E45F1CB8538E00F5C124 /* OLMUtility.h in Headers */ = {isa = PBXBuildFile; fileRef = D976E45D1CB8538E00F5C124 /* OLMUtility.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		D976E4601CB8538E00F5C124 /* OLMUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = D976E45E1CB8538E00F5C124 /* OLMUtility.m */; };
+		D976E4621CB860E200F5C124 /* OLMSerializable.h in Headers */ = {isa = PBXBuildFile; fileRef = D976E4611CB860E200F5C124 /* OLMSerializable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		D976E4651CB8674900F5C124 /* OLMMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D976E4631CB8674900F5C124 /* OLMMessage.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		D976E4661CB8674900F5C124 /* OLMMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D976E4641CB8674900F5C124 /* OLMMessage.m */; };
+		D976E4681CB8787D00F5C124 /* OLMAccount_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D976E4671CB8696100F5C124 /* OLMAccount_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		DE4C8FA4266B10FC69A1C762 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 235C5A0B708438C11BCE552C /* Pods.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		D976E4491CB852E000F5C124 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = D9679C4D1C87B53E007F67AA /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = D976E43D1CB852E000F5C124;
+			remoteInfo = OLMKit;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+		0A0809C67039D4BDCE9CE9AF /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
+		235C5A0B708438C11BCE552C /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		81B8A7B31F3BA6548ACC45DE /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
+		D976E43E1CB852E000F5C124 /* OLMKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OLMKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		D976E4401CB852E000F5C124 /* OLMKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OLMKit.h; sourceTree = "<group>"; };
+		D976E4421CB852E000F5C124 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		D976E4471CB852E000F5C124 /* OLMKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OLMKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		D976E44C1CB852E000F5C124 /* OLMKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OLMKitTests.m; sourceTree = "<group>"; };
+		D976E44E1CB852E000F5C124 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		D976E4551CB8536500F5C124 /* OLMAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OLMAccount.h; sourceTree = "<group>"; };
+		D976E4561CB8536500F5C124 /* OLMAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OLMAccount.m; sourceTree = "<group>"; };
+		D976E4591CB8538300F5C124 /* OLMSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OLMSession.h; sourceTree = "<group>"; };
+		D976E45A1CB8538300F5C124 /* OLMSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OLMSession.m; sourceTree = "<group>"; };
+		D976E45D1CB8538E00F5C124 /* OLMUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OLMUtility.h; sourceTree = "<group>"; };
+		D976E45E1CB8538E00F5C124 /* OLMUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OLMUtility.m; sourceTree = "<group>"; };
+		D976E4611CB860E200F5C124 /* OLMSerializable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OLMSerializable.h; sourceTree = "<group>"; };
+		D976E4631CB8674900F5C124 /* OLMMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OLMMessage.h; sourceTree = "<group>"; };
+		D976E4641CB8674900F5C124 /* OLMMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OLMMessage.m; sourceTree = "<group>"; };
+		D976E4671CB8696100F5C124 /* OLMAccount_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OLMAccount_Private.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		D976E43A1CB852E000F5C124 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DE4C8FA4266B10FC69A1C762 /* Pods.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D976E4441CB852E000F5C124 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D976E4481CB852E000F5C124 /* OLMKit.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		C125031A628591D7E1C25FD8 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				235C5A0B708438C11BCE552C /* Pods.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		D9679C4C1C87B53E007F67AA = {
+			isa = PBXGroup;
+			children = (
+				D96991B7CDE3F0F8C3A52C54 /* Pods */,
+				D976E43F1CB852E000F5C124 /* OLMKit */,
+				D976E44B1CB852E000F5C124 /* OLMKitTests */,
+				C125031A628591D7E1C25FD8 /* Frameworks */,
+				D976E43E1CB852E000F5C124 /* OLMKit.framework */,
+				D976E4471CB852E000F5C124 /* OLMKitTests.xctest */,
+			);
+			sourceTree = "<group>";
+		};
+		D96991B7CDE3F0F8C3A52C54 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				0A0809C67039D4BDCE9CE9AF /* Pods.debug.xcconfig */,
+				81B8A7B31F3BA6548ACC45DE /* Pods.release.xcconfig */,
+			);
+			name = Pods;
+			sourceTree = "<group>";
+		};
+		D976E43F1CB852E000F5C124 /* OLMKit */ = {
+			isa = PBXGroup;
+			children = (
+				D976E4631CB8674900F5C124 /* OLMMessage.h */,
+				D976E4641CB8674900F5C124 /* OLMMessage.m */,
+				D976E4401CB852E000F5C124 /* OLMKit.h */,
+				D976E4611CB860E200F5C124 /* OLMSerializable.h */,
+				D976E4551CB8536500F5C124 /* OLMAccount.h */,
+				D976E4671CB8696100F5C124 /* OLMAccount_Private.h */,
+				D976E4561CB8536500F5C124 /* OLMAccount.m */,
+				D976E4591CB8538300F5C124 /* OLMSession.h */,
+				D976E45A1CB8538300F5C124 /* OLMSession.m */,
+				D976E45D1CB8538E00F5C124 /* OLMUtility.h */,
+				D976E45E1CB8538E00F5C124 /* OLMUtility.m */,
+				D976E4421CB852E000F5C124 /* Info.plist */,
+			);
+			path = OLMKit;
+			sourceTree = "<group>";
+		};
+		D976E44B1CB852E000F5C124 /* OLMKitTests */ = {
+			isa = PBXGroup;
+			children = (
+				D976E44C1CB852E000F5C124 /* OLMKitTests.m */,
+				D976E44E1CB852E000F5C124 /* Info.plist */,
+			);
+			path = OLMKitTests;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		D976E43B1CB852E000F5C124 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D976E45B1CB8538300F5C124 /* OLMSession.h in Headers */,
+				D976E4681CB8787D00F5C124 /* OLMAccount_Private.h in Headers */,
+				D976E4651CB8674900F5C124 /* OLMMessage.h in Headers */,
+				D976E4621CB860E200F5C124 /* OLMSerializable.h in Headers */,
+				D976E45F1CB8538E00F5C124 /* OLMUtility.h in Headers */,
+				D976E4571CB8536500F5C124 /* OLMAccount.h in Headers */,
+				D976E4411CB852E000F5C124 /* OLMKit.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		D976E43D1CB852E000F5C124 /* OLMKit */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = D976E44F1CB852E000F5C124 /* Build configuration list for PBXNativeTarget "OLMKit" */;
+			buildPhases = (
+				5DFDEA7662CDB902F5544BBD /* Check Pods Manifest.lock */,
+				D976E4391CB852E000F5C124 /* Sources */,
+				D976E43A1CB852E000F5C124 /* Frameworks */,
+				D976E43B1CB852E000F5C124 /* Headers */,
+				D976E43C1CB852E000F5C124 /* Resources */,
+				DC41A6806320B8867B32F48B /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = OLMKit;
+			productName = OLMKit;
+			productReference = D976E43E1CB852E000F5C124 /* OLMKit.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+		D976E4461CB852E000F5C124 /* OLMKitTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = D976E4521CB852E000F5C124 /* Build configuration list for PBXNativeTarget "OLMKitTests" */;
+			buildPhases = (
+				D976E4431CB852E000F5C124 /* Sources */,
+				D976E4441CB852E000F5C124 /* Frameworks */,
+				D976E4451CB852E000F5C124 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				D976E44A1CB852E000F5C124 /* PBXTargetDependency */,
+			);
+			name = OLMKitTests;
+			productName = OLMKitTests;
+			productReference = D976E4471CB852E000F5C124 /* OLMKitTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		D9679C4D1C87B53E007F67AA /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0730;
+				TargetAttributes = {
+					D976E43D1CB852E000F5C124 = {
+						CreatedOnToolsVersion = 7.3;
+					};
+					D976E4461CB852E000F5C124 = {
+						CreatedOnToolsVersion = 7.3;
+					};
+				};
+			};
+			buildConfigurationList = D9679C501C87B53E007F67AA /* Build configuration list for PBXProject "olm" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = D9679C4C1C87B53E007F67AA;
+			productRefGroup = D9679C4C1C87B53E007F67AA;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				D976E43D1CB852E000F5C124 /* OLMKit */,
+				D976E4461CB852E000F5C124 /* OLMKitTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		D976E43C1CB852E000F5C124 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D976E4451CB852E000F5C124 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		5DFDEA7662CDB902F5544BBD /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		DC41A6806320B8867B32F48B /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		D976E4391CB852E000F5C124 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D976E4661CB8674900F5C124 /* OLMMessage.m in Sources */,
+				D976E4581CB8536500F5C124 /* OLMAccount.m in Sources */,
+				D976E45C1CB8538300F5C124 /* OLMSession.m in Sources */,
+				D976E4601CB8538E00F5C124 /* OLMUtility.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D976E4431CB852E000F5C124 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D976E44D1CB852E000F5C124 /* OLMKitTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		D976E44A1CB852E000F5C124 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = D976E43D1CB852E000F5C124 /* OLMKit */;
+			targetProxy = D976E4491CB852E000F5C124 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+		D9679C511C87B53E007F67AA /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ENABLE_TESTABILITY = YES;
+				ONLY_ACTIVE_ARCH = YES;
+			};
+			name = Debug;
+		};
+		D9679C521C87B53E007F67AA /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+			};
+			name = Release;
+		};
+		D976E4501CB852E000F5C124 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 0A0809C67039D4BDCE9CE9AF /* Pods.debug.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				INFOPLIST_FILE = OLMKit/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = io.ballinger.OLMKit;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = YES;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Debug;
+		};
+		D976E4511CB852E000F5C124 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 81B8A7B31F3BA6548ACC45DE /* Pods.release.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				INFOPLIST_FILE = OLMKit/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = NO;
+				PRODUCT_BUNDLE_IDENTIFIER = io.ballinger.OLMKit;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = YES;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Release;
+		};
+		D976E4531CB852E000F5C124 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				INFOPLIST_FILE = OLMKitTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = io.ballinger.OLMKitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = iphoneos;
+			};
+			name = Debug;
+		};
+		D976E4541CB852E000F5C124 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				INFOPLIST_FILE = OLMKitTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = NO;
+				PRODUCT_BUNDLE_IDENTIFIER = io.ballinger.OLMKitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = iphoneos;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		D9679C501C87B53E007F67AA /* Build configuration list for PBXProject "olm" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D9679C511C87B53E007F67AA /* Debug */,
+				D9679C521C87B53E007F67AA /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		D976E44F1CB852E000F5C124 /* Build configuration list for PBXNativeTarget "OLMKit" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D976E4501CB852E000F5C124 /* Debug */,
+				D976E4511CB852E000F5C124 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		D976E4521CB852E000F5C124 /* Build configuration list for PBXNativeTarget "OLMKitTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D976E4531CB852E000F5C124 /* Debug */,
+				D976E4541CB852E000F5C124 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = D9679C4D1C87B53E007F67AA /* Project object */;
+}
diff --git a/xcode/olm.xcodeproj/xcshareddata/xcschemes/OLMKit.xcscheme b/xcode/olm.xcodeproj/xcshareddata/xcschemes/OLMKit.xcscheme
new file mode 100644
index 0000000..eee8c5e
--- /dev/null
+++ b/xcode/olm.xcodeproj/xcshareddata/xcschemes/OLMKit.xcscheme
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0730"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "D976E43D1CB852E000F5C124"
+               BuildableName = "OLMKit.framework"
+               BlueprintName = "OLMKit"
+               ReferencedContainer = "container:olm.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "D976E4461CB852E000F5C124"
+               BuildableName = "OLMKitTests.xctest"
+               BlueprintName = "OLMKitTests"
+               ReferencedContainer = "container:olm.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D976E43D1CB852E000F5C124"
+            BuildableName = "OLMKit.framework"
+            BlueprintName = "OLMKit"
+            ReferencedContainer = "container:olm.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D976E43D1CB852E000F5C124"
+            BuildableName = "OLMKit.framework"
+            BlueprintName = "OLMKit"
+            ReferencedContainer = "container:olm.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D976E43D1CB852E000F5C124"
+            BuildableName = "OLMKit.framework"
+            BlueprintName = "OLMKit"
+            ReferencedContainer = "container:olm.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/xcode/olm.xcodeproj/xcshareddata/xcschemes/OLMKitTests.xcscheme b/xcode/olm.xcodeproj/xcshareddata/xcschemes/OLMKitTests.xcscheme
new file mode 100644
index 0000000..300e62c
--- /dev/null
+++ b/xcode/olm.xcodeproj/xcshareddata/xcschemes/OLMKitTests.xcscheme
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0730"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "D976E4461CB852E000F5C124"
+               BuildableName = "OLMKitTests.xctest"
+               BlueprintName = "OLMKitTests"
+               ReferencedContainer = "container:olm.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "D976E4461CB852E000F5C124"
+               BuildableName = "OLMKitTests.xctest"
+               BlueprintName = "OLMKitTests"
+               ReferencedContainer = "container:olm.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D976E4461CB852E000F5C124"
+            BuildableName = "OLMKitTests.xctest"
+            BlueprintName = "OLMKitTests"
+            ReferencedContainer = "container:olm.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D976E4461CB852E000F5C124"
+            BuildableName = "OLMKitTests.xctest"
+            BlueprintName = "OLMKitTests"
+            ReferencedContainer = "container:olm.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/xcode/olm.xcworkspace/contents.xcworkspacedata b/xcode/olm.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..3c8fe35
--- /dev/null
+++ b/xcode/olm.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:olm.xcodeproj">
+   </FileRef>
+   <FileRef
+      location = "group:Pods/Pods.xcodeproj">
+   </FileRef>
+</Workspace>
-- 
cgit v1.2.3-70-g09d2