Showing posts with label ios. Show all posts
Showing posts with label ios. Show all posts

Wednesday, March 13, 2013

Take (and Manipulate!) a Photo with a Web Page

So not that long ago, if you wanted an app to take a photo, it had to be a native app -- such as a Windows/Mac app or a native mobile application.  But HTML5 has brought a number of new APIs that allow not only taking photos, but analyzing and manipulating them all within a browser.

Sadly, there is still inconsistent support for these features across browsers (both desktop and mobile).  So in practice, the best approach may be to target the APIs supported by iOS 6, which is more or less the minimal feature set across browsers that support any of this at all.

The three pieces we'll need are:
  • The Canvas
  • File I/O
  • Changes to the File Upload input and Media Capture
    • Note that Media Capture has been superceded by getUserMedia (more info) but we don't use that here because iOS doesn't support getUserMedia yet

The Canvas is a space on the page to draw into, using either 2-D or 3-D APIs.  Conveniently, it also lets you draw images into it, inspect and alter the pixel data, and export its content as an image. Canvas is supported by IE 9 and modern versions of Firefox, WebKit/Safari/iOS 6, Opera, and Chrome.  However, not all browsers support 3-D (via WebGL), and browsers don't generally support ALL of the Canvas spec (which has continued to evolve even after initial browser support).  Still, the parts we need are supported across browsers.

The additional "accept" and "capture" attributes on the file upload input element let us specify that the user should be able to select an image file, getting it from an attached camera if possible.  Crucially, on iOS you are given the option to select a photo from the photo library or take a photo on the spot (though some desktop browsers only let you browse for an image file, even if a Webcam is attached). Then the "files" property on the input element lets us access the selected files (unfortunately, not supported in IE < 10).

So the formula for taking and manipulating a photo is:
  1. Configure a file input using the new attributes
  2. When an image file is selected (or photo taken), access the image file
  3. Check the image EXIF data for rotation using File I/O
  4. Decide what size to scale the image to (if needed)
  5. Configure a Canvas for the selected image dimensions
  6. Draw the image to the Canvas, rotating and scaling as appropriate
  7. Access and manipulate the pixel data for the Canvas
  8. Write the pixel data back to the Canvas
  9. Export the image on the Canvas as an image file

Taking these one step at a time (and note that I use jQuery for convenience, but it is not needed for this to work):

1. Configure a file input using the new attributes

There are two ways to configure the input (and it makes no difference to desktop browsers). An input configured like this will prompt the user to take a photo or select a saved impage on either iOS or Android:
<input type="file" id="PhotoPicker"
       accept="image/*" />
With the additional capture="camera" attribute, Android goes directly to the camera without giving the option to use a saved photo (though the iOS behavior is the same):
<input type="file" id="PhotoPicker"
       accept="image/*" capture="camera" />
Note that if you don't like the default appearance, you can conceal the input widget and connect a button or link (styled any way you like) to activate it.  The input doesn't work if set to display: 'none', but we can put it inside a div of size 0 to effectively hide it:
<div style="width: 0; height: 0; overflow: hidden;">
    <input type="file" id="PhotoPicker" 
           accept="image/*" capture="camera" />
</div>
<button class="lovely" id="PhotoButton">1. Select Photo</button>
$('#PhotoButton').click(function() {
    $('#PhotoPicker').trigger('click');
    return false;
});

2. When an image file is selected (or photo taken), access the image file

We just use the new files property on the input:
$('#PhotoPicker').on('change', function(e) {
    e.preventDefault();
    if(this.files.length === 0) return;
    var imageFile = this.files[0];
    ...
});

3. Check the image EXIF data for rotation using File I/O:

All images may have a rotation recorded by the camera, such that no matter how the camera was oriented when the photo was taken, the image can be displayed appropriately on screen.  In particular, an iPad considers the landscape orientation to be normal (though it can still be upside-down), and the portrait orientation takes photos with a 90 degree rotation.  The EXIF header in an image records this rotation, so we can read it in order to display the image appropriately.

This part uses two open source (MPL) EXIF-reading scripts:
<script src="http://www.nihilogic.dk/labs/exif/exif.js"
       type="text/javascript"></script>
<script src="http://www.nihilogic.dk/labs/binaryajax/binaryajax.js"
       type="text/javascript"></script>
Then we use the new File API to read the image data (and there's a nice image here describing the EXIF Orientation codes):
var width;
var height;
var binaryReader = new FileReader();
binaryReader.onloadend=function(d) {
    var exif, transform = "none";
    exif=EXIF.readFromBinaryFile(createBinaryFile(d.target.result));

    if(exif.Orientation === 8) {
        width = img.height;
        height = img.width;
        transform = "left";
    } else if(exif.Orientation === 6) {
        width = img.height;
        height = img.width;
        transform = "right";
    }
    ...
};

binaryReader.readAsArrayBuffer(imageFile);

4. Decide what size to scale the image to (if needed)

If you want to limit the size of the image, you can scale the height and width accordingly. With mobile devices with high-resolution cameras, this may be necessary to limit memory usage:
var MAX_WIDTH = 1024;
var MAX_HEIGHT = 768;
if (width/MAX_WIDTH > height/MAX_HEIGHT) {
    if (width > MAX_WIDTH) {
        height *= MAX_WIDTH / width;
        width = MAX_WIDTH;
    }
} else {
    if (height > MAX_HEIGHT) {
        width *= MAX_HEIGHT / height;
        height = MAX_HEIGHT;
    }
}

5. Configure a Canvas for the selected image dimensions

If the canvas is on the page, you can just grab it:
var canvas = $('#PhotoEdit')[0];
Otherwise, you can use an offscreen canvas:
var canvas = document.createElement('canvas');
And then size it accordingly:
canvas.width = width;
canvas.height = height;

6. Draw the image to the Canvas, rotating and scaling as appropriate

In order to draw the image to a Canvas, first we have to load it in an <img />, and a convenient way is to create a URL to assign as the img.src from the selected file (using part of the File API):
var img = new Image();
var url = window.URL ? window.URL : window.webkitURL;
img.src = url.createObjectURL(imageFile);
img.onload = function(e) {
    url.revokeObjectURL(this.src);
    ...
};
Then inside the onload handler, we can draw the image to the canvas. (In practice, I put most of the logic we're discussing inside the onload handler.) A transformation matrix on the context handles rotating the image (if needed) and shifting it back into the viewable area.
var ctx = canvas.getContext("2d");
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, 700, 600);
if(transform === 'left') {
    ctx.setTransform(0, -1, 1, 0, 0, height);
    ctx.drawImage(img, 0, 0, height, width);
} else if(transform === 'right') {
    ctx.setTransform(0, 1, -1, 0, width, 0);
    ctx.drawImage(img, 0, 0, height, width);
} else if(transform === 'flip') {
    ctx.setTransform(1, 0, 0, -1, 0, height);
    ctx.drawImage(img, 0, 0, width, height);
} else {
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.drawImage(img, 0, 0, width, height);
}
ctx.setTransform(1, 0, 0, 1, 0, 0);

7. Access and manipulate the pixel data for the Canvas

The getImageData method returns an array of pixel data, with one byte each for red, green, blue, and alpha. This example applies a "green screen" effect, setting pixels to transparent (alpha = 0) if they are mainly green.
var pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
var r, g, b, i;
for (var py = 0; py < pixels.height; py += 1) {
    for (var px = 0; px < pixels.width; px += 1) {
        i = (py*pixels.width + px)*4;
        r = pixels.data[i];
        g = pixels.data[i+1];
        b = pixels.data[i+2];
        if(g > 100 && g > r*1.35 && g > b*1.6) pixels.data[i+3] = 0;
    }
}
There are of course other possibilities for filtering the image.

8. Write the pixel data back to the Canvas

Then we use putImageData to write the array of pixel data back to the canvas.
ctx.putImageData(pixels, 0, 0);

9. Export the image on the Canvas as an image file

Finally, the whole content of the canvas can be exported as an image. There may be a way to do this through the browser (in Firefox, for instance, right-clicking on any Canvas lets you view it as an image), but there is a canvas API call as well. Every browser supports saving the image as a PNG, and some may support other formats:
var data = canvas.toDataURL('image/png');
The resulting data URL looks like this:
data:image/png;base64,...
Where the "..." is the base 64 encoded version of the binary image file. So you can, for instance, upload the data URL to a server as a form field, and the server can strip off the data:image/png;base64, prefix and base 64 decode the rest and save it to a file like mycanvas.png.

Summary

So there you have it -- a Web page that takes a photograph, scales and orients it as appropriate, processes the image as desired, and then displays it to the user and/or uploads it to a server.
Unfortunately, there is not yet a way to present a "Save As" dialog to the user to save the file directly to their local disk -- the FileSystem and FileWriter APIs are not widely supported across browsers. But you can of course upload the image to the server and then redirect the user to a server URL to save it...

Consolidated Sample Code

Here's a single page with a working example using the code we've gone through here.

You can also use this sample image to see the green screen replacement effect.

Platform Support

Bold entries work fully:

Desktop Browsers:
  • Firefox 18
  • Safari 6
  • Chrome 25
  • IE 10
  • IE 9 (does not support getting selected files from file input)

Mobile Browsers:
  • iOS 6
  • iOS 5 (file input doesn't work)
  • BlackBerry 10
  • Android 4.1 (built-in browser)
  • Android 4.1 with Chrome from Play store
  • Android 2 (native browser; does not take photo)
  • Windows Phone 8 (file input doesn't work)
  • Surface RT (doesn't work)

Tuesday, August 14, 2012

Going mobile? The key to any successful project is asking tough questions before you write code

The following is an article written by Don Coleman, Director of Mobile Development, which was originally published in the July issue of NJ Tech News.


About half of U.S. businesses will be in the mobile market by the end of 2012, according to a recent survey by Robert Half Technology (“Mad for Mobile.” New Jersey TechNews, April 2012). Nearly a quarter of the CIOs surveyed said they planned to develop a mobile application for the first time this year. The mobile boom is in full swing. Developing mobile apps can be scary and exciting, especially for managers who must make difficult business decisions in a rapidly changing technology landscape. Asking tough questions before you begin mobile development can save a lot of headaches down the road.

Are you focused?
It’s a good strategy to kick off a project with some blue-sky brainstorming. Sometimes ignoring reality is the best way to get all the ideas on the table. But the hard part is taking your big ideas and breaking them down into much smaller ideas that you can actually execute.

Do you have a clear idea of what you’re going to build? There is a wide spectrum of possibilities. You could be building a mobile version of your website. You could be building a task-specific app for clients or customers. Perhaps you are building a set of tools to help your employees work smarter and more efficiently.

Make sure you are not trying to do too much. Often it’s a good idea to start with the minimum number of features you need to satisfy your users. You can always add features later.

Probably the biggest thing to keep in mind is this: it’s not an effective use of resources to go mobile just for the sake of it. The rush to mobile now feels a bit like the dot com boom in the late 90s—building websites just to build websites. Be thoughtful. Focus.

Have you considered your audience?
This might seem like an obvious one, but managers and developers often make assumptions without really understanding their users.

If you are building something for your customers, do you know what they want or need? What is you target market? Do you have any studies to back this up? What devices do your users have? Android and iPhone are the most popular, but you might also have to consider other devices, such as the Blackberry or Windows Phone.

It’s also important to be realistic about how much time your customers will spend using your app. Some product managers have this fantasy that they will hold a user’s attention for an hour and a half. Try five minutes.


If you are building tools for your employees, do you understand their workflow? Do you know what kind of hardware they are using? Does the tool need to work offline and online? You want to build something that your employees will easily (and willingly) adopt. It’s important to understand how these mobile tools will be integrated with other software and hardware systems in your company.

Also, don’t forget about the bigger picture for your users. How are you making their lives better or easier? In addition to solving problems and meeting needs, you might consider ways to bring a little fun into their mobile experience. Create something that delights them.

Is your team ready?
This is a tough question, in part because it may force you to consider longer-term business strategies. Is your mobile project a one-time endeavor or do you intend to make mobile development part of your company’s skillset? Is it better to hire someone to build the app for you? Or are you willing to spend time and money training your existing team?

If you have good software developers they can become mobile developers. A consultant can help you plan the project and get your team up to speed. This could be a good strategy if you have developers willing and excited to learn. If your developers aren’t willing and excited to learn new technologies, you have much bigger problems.

Which technologies will you use?
In some ways, choosing technologies is the easy part—easier, at least, than understanding your users and getting developers up to speed. It’s also easy to get into the trap of using shiny, new, cool tools without putting people first.

The first decision you’ll have to make is whether to develop a native app or a web app. With a native app—for instance, one developed specifically for the iPhone or Android platforms—you can easily access each phone’s built-in devices, like the camera, microphone, and file system.

If you are going to build native applications for multiple platforms, start with one platform first, instead of trying to develop on multiple platforms in parallel. Your applications won't launch at the same time, but you can take what you learned on the first platform and apply it to the others.

The mobile web allows you to build applications that are not platform specific and can run in the browser on any phone. But browser capabilities vary across devices. Geolocation support is good. Camera support exists in newer Android and Blackberry but is unavailable on the iPhone.

One solution is to use a framework like PhoneGap that will allow you to build cross-platform native apps using HTML5. PhoneGap provides access to device features (camera and file system) that are not yet available in all web browsers. PhoneGap applications are packaged like native apps and delivered to users through the app stores.


The good news is that the line between web apps and native apps is blurring, and a year from now this distinction might not be a sticking point.

How much are you willing to invest in the project?
Obviously, developer and designer time will be the most expensive resource. All of the plumbing around the project—planning, marketing, and managing—can also add up. Building custom software is expensive, and your first mobile project could be more expensive than you think, especially if you have to train your team. The scope of the project and your vision for your business will likely determine the cost of going mobile.

A consultant could set you on a good path, but only if you do your legwork first. Although there’s no silver bullet here, asking tough questions before you dive in could lead to a more successful mobile app, one that makes your users happy and won’t break the bank.



Bio: Don Coleman, director of consulting at Chariot Solutions, helps businesses put together clear roadmaps for mobile development. He loves equally solving interesting technical problems and making businesses work better.

Monday, July 30, 2012

Using Cocoapods to Manage Private Libraries

Introduction

Cocoapods (http://cocoapods.org/) is a dependency management framework for XCode. It allows you to declaratively define project dependencies and have them included in the build of your project. It's like Apache Maven or Ruby Gems for XCode. There are many tutorials on getting started with Cocoapods, so in this blog post I will present a simple case where we have a project that depends upon two internally developed projects or private libraries.
We will start by creating two projects, each with a simple UI screen and some 3rd party dependency. Each of these projects will contain a file known as a Podspec that defines what gets included when another project depends upon it. Next, we will create a third project that depends upon these internally developed libraries and see how everything gets tied together.
In my contrived example, I'll create two projects, each with a dependency on AFNetworking (a nice network abstraction layer). AFNetworking is already available via the public cocoapods spec library. Cocoapods will be used to add this dependency. Each project will also use YQL to query a weather service (Yahoo versus Wunderground) and present the forecast for a given zip code. The master project, which I like to call the Weather Comparator, will pull in the Yahoo and Wunderground projects and use their code to present a comparison of the services.

The Private Libraries

We start by creating the first project, Yahoo Weather. Once we have Cocoapods installed on our system, we create an XCode project for our first framework. I started using the Empty iOS application template. Once the XCode project is created, exit XCode. Create a text file called "Podfile" at the root directory of the project. The contents of the Podfile looks like the following:
platform :ios, '5.1'

xcodeproj 'YahooWeather.xcodeproj'

pod 'AFNetworking'
The first line defines the platform and SDK version, the next line defines the name of the XCode project file, and subsequent lines define the dependencies for this project. Podfiles can be much more complex, but this post will keep things simple. Check out the Cocoapods site for more information. Once the Podfile is complete, drop to a terminal and execute pod install at the root of the project directory. You'll see some messages about the dependencies being downloaded and configured and when the process is complete, there will be a message indicating NOT to use the YahooWeather.xcodeproj anymore, but use the YahooWeather.xcworkspace file now. This is a workspace that includes your project and the Pods project.
Under the Pods project, there is a Pods directory, which contains the libraries specified in the Podfile (AFNetworking in this case). Besides creating the workspace, Cocoapods configures your project to include a static library called libPods. This is the output of the Pods project build. Essentially it includes the compiled version of the pods for which you asked to be included in the project. We can now use the AFNetworking classes within our code. The source code for the YahooWeather project can be found on my github account: https://github.com/stevenpsmith/YahooWeatherService. The finished app can actually be run and looks like this (I know, gorgeous, right):
Following the same steps, we create a second project called WunderWeather. Much of the code is similar, except now we are using the Wunderground weather API. There are some other small changes, but for simplicity sake I reused much of the same code, just using different names for the classes (notice the class name prefix). Again, remember this is a contrived example, so reusing the code was merely for time efficiency. The source code for WunderWeather can be also be found on my github at https://github.com/stevenpsmith/WunderWeatherService. And for your viewing pleasure, here is what it looks like:
One important thing to note here, is that each of our frameworks uses a slightly different class prefix. This ensures that when both of these projects get used in a third project (the WeatherComparator), there are no name collisions. Wouldn't namespaces be nice?
When we are done coding each of these projects, we can commit them to our "private" git repository. In this case I am using github public repositories, but these projects can reside anywhere that is accessible from the projects that will depend upon them. That could a private repo, file system, etc. Cocoapods also support SVN and Mercurial, but git is what all the cool kids are using, so that is what this example will use.

Creating the Podspec

The next step is to configure a podspec for each of our frameworks. The podspec defines the source files, resources, etc. that our libraries/frameworks expose for use by other projects. In our case, we need to expose all the classes in the API, Model, and ViewController directories within our projects. We also want to expose our XIB files. Those are resources which need to get compiled and added to our final ".app" file. In a similar way, resources might include images that will get added into the ".app" file during the final build process. To create a podspec, execute pod spec create projectname at the root of the project directory. In our case, it would be pod spec create YahooWeather or pod spec create WunderWeather
The file that gets created has comments and examples of all the properties that can be set. For our projects, the podpsec looks like this (with all the extraneous comments and unused attributes removed):
Pod::Spec.new do |s|
  s.name         = "YahooWeather"
  s.version      = "0.0.1"
  s.summary      = "Provides a Yahoo weather forecast, with basic UI, for a given zip code."
  s.homepage     = "https://github.com/stevenpsmith/YahooWeatherService"
  s.license      = 'MIT'
  s.author       = { "stevenpsmith" => "ssmith@chariotsolutions.com" }
  s.source       = { :git => "https://github.com/stevenpsmith/YahooWeatherService.git", :tag => 'v0.0.3' }
  s.platform     = :ios, '5.1'
  s.source_files = 'YahooWeather/API/*.{h,m}', 'YahooWeather/Model/*.{h,m}', 'YahooWeather/ViewController/*.{h,m}'
  s.resources = "YahooWeather/ViewController/*.{xib}"
  s.requires_arc = true
  s.dependency 'AFNetworking'
end
Notice the source property. This points to our "private" repo. You can also access local repositories (instead of server based repos) by using a URI like this: '/Users/steve/projects/foo', which points to the root directory of your version controlled code. Also note the use of the tag key (which could also be a :commit). If consumers of this library do not specify a tag or commit, this specifies the snapshot of the code that will be provided. Without specifying anything, the latest commit of the master branch will be used. The source_files and resources specify the assets within this project that will be provided to the consuming projects. The dependency property specifies any pods that this pods would depend upon (in our case, AFNetworking). This file needs to be committed to the repository, and if a tag or commit is specified, that must exist within the repo as well. It's a good practice to validate the podspec by executing pod spec lint <podspec_name>
This will save a lot of grief with typos, incorrect source directories, etc. Once your podspec is clean, commit it to the repository and tag if needed. The podspec for WunderWeather looks eerily similar.

Using the private libraries

The next step is to use these libraries in a new project. This is the WeatherComparator project, and it's source can be found at https://github.com/stevenpsmith/WeatherComparator. We get started like we did before, creating the project within XCode, then exiting and creating a Podfile that references the dependencies. Except this Podfile will look a little different, as shown below:
platform :ios, '5.1'

xcodeproj 'WeatherComparator.xcodeproj'

pod "YahooWeather", :git => 'https://github.com/stevenpsmith/YahooWeatherService.git', :tag => 'v0.0.3'
pod "WunderWeather", :git => 'https://github.com/stevenpsmith/WunderWeatherService.git', :tag => 'v0.0.2'
Here we are adding the :git key to the pod declaration. Again, this could be subversion, mercurial, and even a filesystem reference. Notice there are no dependencies on AFNetworking. Our new project doesn't directly depend upon those, but since the pods do, it will get pulled into our new workspace as it did in the other projects. Execute pod install in a terminal at the root of the new project directory, open the workspace as we did before, and we are good to go. Here is a screen shot of our weather comparator:
If you take a look at the code in the WeatherComparator project, it is very simple. One interesting thing to look at is the Pods-resources.sh script located in the TargetsSupportFiles/Pods directory within the Pods project. Cocoapods generates this script file based on the contents of the podspec for the libraries. This script actually compiles the XIB files and adds them to the projects final build. As you can see, pods can use storyboards as well. This provides a nice way to create modules that include UIs and then combine them into applications.
Even though we used github public repositories, this post shows how to use Cocoapods to manage your project's public and private dependencies. To try out the project, install Cocoapods, download the WeatherComparator project, execute pod install at the root, open the workspace, run the project, and see how different Yahoo and Wunderground weather forecasts can be. Pretty surprising actually :).

Friday, April 27, 2012

Securing Data in iOS

There are numerous ways to secure data that you are storing on an iOS device.

The Simple/Built-in Way

The simplest way is to take advantage of the iOS Data Protection (iOS 4+). This can be accomplished by setting an attribute on a file like this:

    [[NSFileManager defaultManager] createFileAtPath:[self filePath]
                    contents:[@"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding]
                    attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete
                                                           forKey:NSFileProtectionKey]];

There are several different levels of file protection. The following was taken from the NSFileManager class reference from Apple:

  • NSFileProtectionNone - no file protection
  • NSFileProtectionComplete - file is encrypted when the device is locked or booting
  • NSFileProtectionCompleteUnlessOpen - file is encrypted and can only be opened when the device is unlocked. Once open, the file can continue to accessed even if the user locks the device.
  • NSFileProtectionCompleteUntilFirstUserAuthentication - The file is stored in an encrypted format on disk and cannot be accessed until after the device has booted. After the user unlocks the device for the first time, your application can access the file and continue to access it even if the user subsequently locks the device.

A Core Data sqlite store can also be encrypted by setting the NSFileProtectionKey file attribute to one of the above values (after you create your persistent store coordinator).

However, an important thing to realize is that this type of data protection requires the device to have a passcode set on it (http://support.apple.com/kb/HT4175).

CommonCrypto

What if you really need to insure that your data is protected regardless of whether the device has a passcode set? One way is to use the CommonCrypto libraries from Apple. This can be fairly complex. There is a great write up here from Rob Napier on what's involved. Fortunately, he has also provided a wrapper that greatly simplifies this process here. Using this library (or writing your own), you can encrypt your data and store it wherever you need. If you are using Core Data, you could write an NSValueTransformer for the Core Data entity attributes that require encryption using CommonCrypto or the RNCrypto library to encrypt/decrypt the attribute values.

SQLCipher

One more way to protect your data, specifically data that you want to store in a SQLite database, would be to use SQLCipher. SQLCipher encrypts/decrypts data at the page level and is transparent to your application code. You still use the standard SQLite APIs, with one additional method call when accessing the database (passing your key to sqlite). There are excellent instructions on setting it up for use in an iOS project here.

You can build SQLCipher as a set of static libraries by following the same iOS instructions with some modifications and additions. Here are the high level steps:

  1. Instead of creating a view based iOS project, create a static library project
  2. Follow the balance of the steps for including SQLCipher in your project
  3. Add a new "Aggregate" build target
  4. Add a "Run Script" build phase and paste the following script into it. This will build both the simulator and iOS based targets for the libcrypto, libsqlcipher, and libssl libraries.
  5. xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphonesimulator -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator
    
    xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphoneos -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos
    
  6. Add another "Run Script" build phase and paste the following script into it. This will merge the simulator and iOS builds into one for each library. These three libraries are what would be added to a project using SQLCipher.
  7. CRYPTO_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/libcrypto.a" &&
    SQLCIPHER_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/libsqlcipher.a" &&
    SSL_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/libssl.a" &&
    
    DEVICE_CRYPTO_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/libcrypto.a" &&
    DEVICE_SQLCIPHER_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/libsqlcipher.a" &&
    DEVICE_SSL_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/libssl.a" &&
    
    UNIVERSAL_LIBRARY_DIR="${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal" &&
    
    
    # Create framework directory structure.
    rm -rf "${UNIVERSAL_LIBRARY_DIR}" &&
    mkdir -p "${UNIVERSAL_LIBRARY_DIR}" &&
    
    # Generate universal binary for the device and simulator.
    lipo "${CRYPTO_LIB}" "${DEVICE_CRYPTO_LIB}" -create -output "${UNIVERSAL_LIBRARY_DIR}/libcrypto" &&
    lipo "${SQLCIPHER_LIB}" "${DEVICE_SQLCIPHER_LIB}" -create -output "${UNIVERSAL_LIBRARY_DIR}/libsqlcipher" &&
    lipo "${SSL_LIB}" "${DEVICE_SSL_LIB}" -create -output "${UNIVERSAL_LIBRARY_DIR}/libssl"
    

Once completed, you can build your new aggregate target and there should be 3 libraries located in a subdirectory within your project build directory. Just add these to your XCode project as you would any other library and you are good to go.