Removing CGPDFDocument Passwords

Working with PDFs on mobile is a pain. On the bright side at least Apple provides us CGPDFDocument

On the bright side Apple does provide CGPDFDocument. If you’re on Android you are left to 3rd party alternatives such as iText and PDFBox.

However examples on how to use CGPDFDocument are limited to the bare basics. On a recent project, I though I had a pretty basic requirement. Provide the user with the ability to remove the password for a protected PDF file. How hard could this be? Unfortunately I wasn’t able to find any examples on how to do this so decided to put my own together.

The most important thing you need to know about working with CGPDFDocument is it is immutable. This means you can’t just call unlockWithPassword and update a property to remove the password.

This means you will need to first unlock the PDF, then create an entirely new document. If you are working with large files you will need to be careful about the memory you are using. Instruments will be your best friend.

Let’s walk through an example. First we need to load a PDF file into a Data object as shown below.

Next you need to unlock your PDF using the existing password. In the example below we create a helper function called unlock that takes the Data from our PDF file and returns an unlocked CGPDFDocument. If the method is unable to either cast the Data object or unlock using the provided password a nil is returned.

We now have an unlocked CGPDFDocument object. Since this is immutable we need to use this as the source to create a new CGPDFDocument without a password. This is done by looping through the now unlocked CGPDFDocument and copying each CGPDFPage into a new CGPDFDocument. The copyPDFtoData helper function in the below example shows how the Core Graphics context is managed as part of the looping process. The end result is a Data object that is a copy of the PDF we started with, but without the password.

Keep in mind this isn’t an exact copy. The CGPDFDocument info and other meta data won’t be copied as part of this process. This can easily be added to the copyPDFtoData but was overkill for this example.

For convenience I’ve combined the above helper functions into a single class for easy maintenance and experimentation.

For more comprehensive PDF handling check out my PDFUtilities project.

Checking if device Passcode is enabled using Swift

If you spent any time building apps for the Enterprise or business space odds are you have turn into the requirement to check if the device has a passcode enabled.

Mobile Device Management (MDM) Solutions are full of tools that handle this for you.  More often I’m finding that organizations are opting for lighter weight solutions to simplify their deployments.

The approach of checking for a passcode would easily become an anti-pattern in that the user at anytime can disable this feature.  I would recommend keeping this in mind and implement the check as only one part of a larger more comprehensive security strategy.

I would call the approach of checking for a passcode somewhat of an anti-pattern in that the user could disable this feature at anytime. If you implement this approach I would highly recommend that this is only one part of your security strategy.

Starting in iOS 8 you could finally check, without jailbreaking, if the device had a passcode set. All you have to do is create a new Keychain entry using the new kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly access option.

Below is an example of using the Keychain to test if the device passcode is enabled.

Then in iOS 9 Apple introduced a much better way to perform this check. In the same way you check if Touch ID is enabled you can check if the device has a passcode.

Below is an example on how you can do this using canEvaluatePolicy.

The below PasscodeUtils.swift combines the two approaches to allow for you to support this functionality back to iOS 8.

Simplifying using Keychain Access Groups

Keychain sharing is an easy and secure way to share information between apps on the same device. This is even more useful when you think about sharing information between an app and it’s extensions. In my opinion Keychain is just as easy as using NSUserDefaults but provides a greater level of security.

Although Keychain sharing is easy to use, I found the use of Access Groups or kSecAttrAccessGroup somewhat tricky.  There are a few different ways of working with Keychain Groups I thought it might be useful to go through the approach I adopted for others looking to implement Keychain sharing within their own apps.

First you will need to setup Keychain sharing in your app.  The below are some great tutorials to get you started.

Next, you will need to try to pass information between apps in your Keychain Group.  Here is where I ran into my problem.  At first I assumed that you simply set the kSecAttrAccessGroup value to the name of the Keychain Group you want to use.  No matter the KeyChain Group value I tried, it always returned a nil.

Why?  If you open your app’s entitlement file you can see why. Your keychain access group is a calculated value of your Keychain Group and your $(AppIdentifierPrefix).

This means if you set kSecAttrAccessGroup to your keychain access group of com.bencoding.awesome-group it would always return nil. You would need to add the App ID Prefix (also called Team ID) to your Keychain Group. In our example this would be something like ABC1234DEF.com.bencoding.awesome-group.

Getting your App ID Prefix from the developer portal isn’t the end of the world and the tutorials at the beginning of this article do a good job covering why this is necessary.

What is another “magic” string in your app?

If you are building enterprise apps which are often resigned or just hate using “magic” strings this approach leaves you looking for an alternative.

Another approach is to use the Keychain’s own meta data find the App ID Prefix .  All you need to do is create a keychain item and then read the returned kSecAttrAccessGroup value from the meta data.  Using the provided kSecAttrAccessGroup value you can parse the  App ID Prefix and default Keychain Access Group.  Removing (or at least reducing) the need for “magic” strings.

I’ve encapsulated this approach into the KeyChainAccessGroupHelper.swift shown at the end of this post.

This approach makes using Keychain sharing with any of the popular Keychain libraries easier.  Below demonstrates how to use KeyChainAccessGroupHelper with the KeychainAccess library.


The below gist shows the implementation details of KeyChainAccessGroupHelper.swift

KeyChainAccessGroupHelper is also used as part of the KeyStorage project.

Solving Appcelerator Studio’s “Invalid Request” when Compiling

I use Titanium for many of my projects and have found it a great framework. Just like with any tools every once and awhile you run into some interesting errors. I am often switching networks and every once and awhile I get the below error in the Appcelerator Studio console window.

[INFO] :   Alloy compiled in 9.88357s
[INFO] :   Alloy compiler completed successfully
[TRACE] :  offline build file ...
[TRACE] :  online 1
[TRACE] :  sending request ...
[TRACE] :  result from /build-verify=> {"success":false,"error":"invalid request","code":"com.appcelerator.security.invalid.session"}, err=null
[ERROR] :  invalid request

This is an interesting warning as it means the Appcelerator CLI no longer has valid session information.  Great for security, but can be confusing as the error is not resolved by logging in and out of Appcelerator Studio.

This is easily resolved by the following:

  1. Open terminal
  2. Type appc logout, this will logout the Appcelerator CLI
  3. Type appc login, then enter your Appcelerator credentials

If you are using the CLI most likely will never see the above but for those of us that like to use Appcelerator Studio might find this little trick helpful.

Logging Exceptions to Google Analytics Using SwiftyBeaver

SwiftyBeaver_thumb.png

There has been an avalanche of Swift based logging frameworks lately.  After trying several, I found SwiftyBeaver was both flexible and simple enough to move all of my apps onto.  Similar to most of the other logging frameworks, SwifyBeaver is extensible. But, unlike many others, there is no real magic injected into the framework making it easy to read and hopefully maintain.  This is best demonstrated by how compacted it’s plugin system is.  You really only need to write you business specific information, the rest is handled for you.

I use Google Analytics for behavior tracking and basic exception tracking today.  Although all of my Google Analytics code is centralized exception reporting is handled explicitly.  With the move to SwiftyBeaver I wanted to see how reporting exceptions to Google Analytics could be handled automatically as part of my logging strategy.  To accomplish this I created the SwiftyBeaver Google Analytics Exception Logger, available here.  Just as the very long name indicates this plugin will automatically post error information to Google Analytics.

Before you start

The Google Analytics Exception plugin requires that you first install SwiftyBeaver and Google Analytics.  You can find information on how to do this below.

After both SwiftyBeaver and Google Analytics have been installed you need to copy the plugin into your project. Instructions for doing this are available here.  You’re now ready to start configuring logging in your project.

Creating your Logger

Creating a logger in your project is extremely simple. Typically you will want to add the below to the top of your AppDelegate.swift so you can use logging throughout your project.

import SwiftyBeaver
let logger = SwiftyBeaver.self

Adding the Google Analytics Logger Destination

Now that you have created your SwiftyBeaver logger you need to add destinations.  Without adding any destinations SwiftyBeaver wont actually do anything.  For this example I’m going to add two destinations. The first will be the built-in Console destination which simply writes to the Xcode console.

let console = ConsoleDestination()
logger.addDestination(console)

Next we’ll add the Google Analytics Exception Logger.  When creating the GABeaver plugin you must added your Google Analytics key.  This will be used when reporting Exceptions.  You can also specify the reporting threshold.  This parameter controls the minimum logging level that should be reported to Google Analytics as an exception. By default this is set to only report error levels or greater.  If you wanted to report warnings or higher you could simply provide a threshold of warning and the plugin will automatically send both warnings and errors.

Below illustrates how to add the Google Analytics Exception plugin with the default settings.

let gaErrorLogger = GABeaver(googleAnalyticsKey: "your GA Key")
logger.addDestination(gaErrorLogger)

Optional Configurations

By default only Error Log messages will be sent to Google Analytics.  You can change this by setting the Threshold property on the logger.

For example, you can record all message with a level of WARNING or great by doing the following:

gaErrorLogger.Threshold = SwiftyBeaver.Level.Warning

You can also configure the logger to write to the console each time a message is logged. To enable console logging, set the printToConsole property to true as shown below:

gaErrorLogger.printToConsole = true

More Information

Additional information is available on github at SwiftyBeaver-GA-Exception-Logger.

SwiftyBeaver

Using OpenURL to launch links in a UIWebView

UIWebView in great for displaying HTML formatting information in your app.  In many cases you might be displaying fragments you didn’t create opening the door for links your user might action.  By default your embedded UIWebView will follow the link which can lead to an odd user experience.  There are several options for handling this including opening the SFSafariViewController.  Since a majority of text I will be displaying doesn’t have links I will just be using openURL to open the link in Safari. With the back button introduced in iOS9 this simplistic user experience can be added in just a few lines of code. The below walks through what is needed to implement this approach.

Adding the delegate

First the UIWebViewDelegate Protocol needs to be added to your controller.   This allows us to override the necessary loading content methods.

import UIKit

class ViewController: UIViewController, UIWebViewDelegate {

    @IBOutlet weak var webView: UIWebView!
    let html = "<div>Lorem ipsum dolor sit amet..."

}

Next in your viewDidLoad override method set the delegate method of your UIWebView to the controller, ie self.

override func viewDidLoad() {
    super.viewDidLoad()
    self.webView.delegate = self
    self.webView.loadHTMLString(html, baseURL: nil)
}

Loading Content

The final step is to add the webView:shouldStartLoadWithRequest:navigationType: method. This will be called whenever content is loaded or a navigation action such as clicking on a link is called.

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {

    if navigationType == UIWebViewNavigationType.LinkClicked {
        UIApplication.sharedApplication().openURL(request.URL!)
        return false
    }
    
    return true
}

You will notice we are checking if the navigationType  is LinkClicked  and then using openURL to open the link in Safari.  It is important to note that if this is a link we return false. This will stop the UIWebView from loading the link content.  Otherwise we always return true so the HTML passed to the UIWebView will load as expected.

The Result

With our delegate and content loading method in place Safari will load each time the user taps on a link in your UIWebView.  The following shows this snippet in action.

UIWebViewOpenUrl

A gist with the complete code is available here.

UIWebView Background Experiments

UIWebView is one of the most useful controls in UIKit and has almost a limitless amount of options you can configure.   My use case isn’t all that unique I’m simply displaying snippets of HTML email messages in a full screen UIViewController.  The need to set the background color of the UIWebView is the only thing that makes this more than just a simple drag and drop in Interface Builder.  Although this can all be done in 2-3 lines of code I thought it would be fun to share the combinations.

The Starting Snippet…

The code we are using for this experiment couldn’t be simpler consisting of a UIWebView that takes up most of the screen. We’ll use this snippet for our background experiments.

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var webView: UIWebView!
    let html = "<div>Lorem ipsum dolor sit amet...</div>"
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.lightGrayColor()
        self.webView.delegate = self
        self.webView.loadHTMLString(html, baseURL: nil)
    }
}

UIWebView Default Behavior

Let’s start with our default behavior.  When you overscroll you can see the linen background.  For my use case I want to replace the background linen with a solid color.

UIWebView-Default

 

UIWebView White Background

By adding two lines of code, the background linen has been replaced with a white background.

self.webView.opaque = true
self.webView.backgroundColor = UIColor.whiteColor()

UIWebView-White-Background

UIWebView Underlying UIView

Since the background UIView for our UIViewController is lightGrayColor we can set the backgroundColor of the UIWebView to clearColor to have our overscroll exposure the underlying UIView.  This is a nice option if you have a background image or treatment you want to expose to the user when they overscroll.

self.webView.opaque = true
self.webView.backgroundColor = UIColor.clearColor()

UIWebView-Show-Background

UIWebView Transparent Background

By changing opaque to false we show the background view under the contents of the UIWebView.  This is outside my use case but can see where this could be a useful technique for overlaying text.

self.webView.opaque = false
self.webView.backgroundColor = UIColor.clearColor()

UIWebView-Transparent-Background

 

Gist to the example code is available here.