---
title: Using the Dropbox Objective-C API in Swift
teaser: 'Extend the Dropbox SDK to play nicely with Swift and functional programming
  concepts.

  '
tags: ios,swift
author: Tony DiPasquale
published_on: 2014-12-08
---

Using Objective-C sources and frameworks with Swift is made possible through
Interoperability. The compiler has all the magic built in that allows us to call
Objective-C methods as if they were Swift functions. However, Objective-C and
Swift are very different languages and what works well in Objective-C might not
be the best for Swift. Let's look at using an Objective-C API, the Dropbox [Sync
API], with Swift to keep our app's files synced with the cloud.

The Dropbox Sync SDK is a precompiled framework that we can [download] and add
to our Xcode project. Carefully follow the instructions on the download page to
install the SDK and setup a Dropbox App to obtain a Key and Secret. You can
follow the [tutorial] online to implement the SDK and have files syncing rather
quickly. Interoperability allows us to use this Objective-C <abbr
title="Application Programming Interface">API</abbr> in Swift, but let's see how
we can abstract this SDK and sprinkle in some Functional Programming concepts to
make it easier to use.

[Sync API]: https://www.dropbox.com/developers/sync
[download]: https://www.dropbox.com/developers/sync/sdks/ios
[tutorial]: https://www.dropbox.com/developers/sync/start/ios

## Connecting to Dropbox

After we have installed the framework into our Xcode project, we need to import
it into Swift. This is done via a bridging header. We can easily create a
bridging header by adding a `.m` file to our project. Xcode will ask us if we'd
like to create a bridging header. Accept, and then delete the temporary `.m`
file. Now in our bridging header import the Dropbox SDK.

```objc
#import <Dropbox/Dropbox.h>
```

Great! Let's move on to our custom class to interface with the SDK. We can call
it `DropboxSyncService`.

```swift
class DropboxSyncService {}
```

The first thing we need to do is create a `DBAccountManager` with our Key and
Secret. Let's create a `setup` function to handle all the Dropbox setup code.

```swift
class DropboxSyncService {
  func setup() {
    let accountManager = DBAccountManager(appKey: "YOUR_APP_KEY", secret: "YOUR_APP_SECRET")
    DBAccountManager.setSharedManager(accountManager)
  }
}
```

Now, we call our `setup` function from the application delegate function
`application:didFinishLaunchingWithOptions:`.

```swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
  dropboxSyncService.setup()
  return true
}
```

Next, we need to link a user account to the account manager. Somewhere in our
app, there should be a button that, when tapped by the user, will tell the
Dropbox SDK to attempt authentication.

Let's add an `initiateAuthentication` function for that button action to call.

```swift
class DropboxSyncService {
  // ...

  func initiateAuthentication(viewController: UIViewController) {
    DBAccountManager.sharedManager().linkFromController(viewController)
  }
}

// In some View Controller with a "Link Dropbox" button
@IBAction func linkDropbox() {
  dropboxSyncService.initiateAuthentication(self)
}
```

The Dropbox SDK is awesome and will handle all the authentication for us. If the
user has the Dropbox app installed, the app will open and authenticate them. If
not, the SDK will open a modal popup to the Dropbox web based OAuth login. In
either case, the authentication process will open a custom <abbr title="Uniform
Resource Locator">URL</abbr> scheme to our app. We created this URL scheme in
the installation process. All we have to do now is capture the URL in our app
delegate and pass it along to the Dropbox SDK.

```swift
// In the application delegate
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool {
  return dropboxSyncService.finalizeAuthentication(url)
}
```

```swift
class DropboxSyncService {
  // ...

  func finalizeAuthentication(url: NSURL) -> Bool {
    let account = DBAccountManager.sharedManager().handleOpenURL(url)
    return account != .None
  }
}
```

The `handleOpenURL` function returns an implicitly unwrapped optional,
`DBAccount!`, which means it could fail returning `nil`. This happens because
the <abbr title="Application Programming Interface">API</abbr> is written in
Objective-C where any object could be `nil` and Xcode automatically makes
objects implicitly unwrapped so developers can use them without the optional
syntax.  We return a `Bool` by checking if the optional account is `.None`. If
the Dropbox account was successfully linked we can fire off a notification so
our original view controller with the button to link Dropbox can react.

## Getting the Files

When we start looking at files and retrieving data, we see that dealing with
errors becomes important. Many of the Dropbox functions can return an object or
mutate a `DBError` pointer that we hand it. This is a common practice in
Objective-C for many functions that can fail, but mutable pointer passing
doesn't feel right in Swift.

If a function can have a result or error,
then the `Result` type introduced in an [earlier post]
sounds like a perfect choice.

[earlier post]: https://thoughtbot.com/blog/efficient-json-in-swift-with-functional-concepts-and-generics

```swift
enum Result<A> {
  case Success(Box<A>)
  case Error(NSError)

  static func success(v: A) -> Result<A> {
    return .Success(Box(v))
  }

  static func error(e: NSError) -> Result<A> {
    return .Error(e)
  }
}

final class Box<A> {
  let value: A

  init(_ value: A) {
    self.value = value
  }
}
```

Here we see our `Result` type is an `enum` with two states: `Success` and
`Error`. We have to use a `Box` type that is a `final class` because of a
[deficiency] in the Swift compiler. We also created two convenience static
functions so we do not have to wrap our value in a `Box` every time we create a
`Success` result.

[deficiency]: https://devforums.apple.com/message/995261#995261

First, we need a list of files contained within our app folder on Dropbox. The
SDK provides `DBFilesystem.listFolder(path: DBPath, inout error: DBError)` to
get a list of `DBFileInfo`s. This Objective-C <abbr title="Application
Programming Interface">API</abbr> isn't as nice to use in Swift so let's create
an extension that gives us a nicer function to call that returns a `Result`.

```swift
extension DBFilesystem {
  func listFolder(path: DBPath) -> Result<[DBFileInfo]> {
    var error: DBError?
    let files = listFolder(path, error: &error)

    switch error {
    case .None: return .success(files as [DBFileInfo])
    case let .Some(err): return .error(err)
  }
}
```

Now in our `DropboxSyncService` class we can add a `getFiles()` function to
call the extension. Before we do that, we need to create the shared filesystem
using the account. Let's put this in the `finalizeAuthentication` function.

```swift
class DropboxSyncService {
  // ...

  func finalizeAuthentication(url: NSURL) -> Bool {
    let account = DBAccountManager.sharedManager().handleOpenURL(url)
    DBFilesystem.setSharedFilesystem(DBFilesystem(account: account))
    return account != .None
  }

  func getFiles() -> Result<[DBFileInfo]> {
    return DBFilesystem.sharedFileSystem().listFolder(DBPath.root())
  }
}
```

This works but there are a couple issues. The `.setSharedFilesystem()` function
takes a non-optional value, which could result in a runtime exception if the
`DBFileSystem` is `nil`. Let's use bind (`>>-`) to set the shared filesystem if
the initializer doesn't fail.

```swift
infix operator >>- { associativity left precedence 150 }

func >>-<A, B>(a: A?, f: A -> B?) -> B? {
  switch a {
  case let .Some(x): return f(x)
  case .None: return .None
  }
}

class DropboxSyncService {
  // ...

  func finalizeAuthentication(url: NSURL) -> Bool {
    let account = DBAccountManager.sharedManager().handleOpenURL(url)
    DBFilesystem(account: account) >>- DBFilesystem.setSharedFilesystem
    return account != .None
  }
}
```

Also, I would prefer to have a list of the file names instead of `DBFileInfo`s.
We use `map` to get the file names out from the `DBFileInfo`s.

```swift
class DropboxSyncService {
  // ...

  func getFiles() -> Result<[String]> {
    let fileInfoArrayResult = DBFilesystem.sharedFileSystem().listFolder(DBPath.root())

    switch fileInfoArrayResult {
    case let .Success(fileInfoArrayBox):
      return fileInfoArrayBox.value.map { fileInfo in
        fileInfo.path.stringValue()
      }
    case let .Error(err): return .error(err)
    }
  }
}
```

The `listFolder` function returns a `Result<[DBFileInfo]>` that could be in an
error or success state. We only want to extract an array of `String`s if it was
successful, so we use a `switch` statement to check for success, then map over the
array of `DBFileInfo`s and return the string value of the path.

What this `switch` statement is really doing is applying a function to the value
inside a successful `Result` then returning it's output as a new `Result`;
otherwise, if the `Result` is in the error state, it returns a new `Result` with
the error.

This is what fmap does.
We'll use the fmap operator (`<^>`) to clean up that function.

```swift
infix operator <^> { associativity left precedence 150 }

func <^><A, B>(f: A -> B, a: Result<A>) -> Result<B> {
  switch a {
  case let .Success(aBox): return .success(f(aBox.value))
  case let .Error(err): return .error(err)
  }
}

class DropboxSyncService {
  // ...

  func getFiles() -> Result<[String]> {
    let fileInfos = DBFilesystem.sharedFileSystem().listFolder(DBPath.root())
    let filePaths: [DBFileInfo] -> [String] = { $0.map { $0.path.stringValue() } }
    return filePaths <^> fileInfos
  }
}
```

Now we have a nice Swift-like <abbr title="Application Programming
Interface">API</abbr> to get a list of files. We will use a similar process to
get the file data. First, add an extension to the Dropbox SDK to use the
`Result` type. Then, use the functional operators to make operations with the
`Result` type easy to work with. Here is everything we need for retrieving the
file data.

```swift
extension DBFilesystem {
  // ...

  func openFile(path: DBPath) -> Result<DBFile> {
    var error: DBError?
    let file = openFile(path, error: &error)

    switch error {
    case .None: return .success(file)
    case let .Some(err): return .error(err)
    }
  }
}

extension DBFile {
  func readData() -> Result<NSData> {
    var error: DBError?
    let data = readData(&error)

    switch error {
    case .None: return .success(data)
    case let .Some(err): return .error(err)
    }
  }
}

func >>-<A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> {
  switch a {
  case let .Success(aBox): return f(aBox.value)
  case let .Error(err): return .error(err)
  }
}

class DropboxSyncService {
  // ...

  func getFile(filename: String) -> Result<NSData> {
    let path = DBPath.root().childPath(filename)
    return DBFilesystem.sharedFilesystem().openFile(path) >>- { $0.readData() }
  }
}
```

Finally, let's use the same process again to implement the function for
creating files.

```swift
extension DBFilesystem {
  // ...

  func createFile(path: DBPath) -> Result<DBFile> {
    var error: DBError?
    let file = createFile(path, error: &error)

    switch error {
    case .None: return .success(file)
    case let .Some(err): return .error(err)
    }
  }
}

extension DBFile {
  // ...

  func writeData(data: NSData) -> Result<()> {
    var error: DBError?
    writeData(data, error: &error)

    switch error {
    case .None: return .success(())
    case let .Some(err): return .error(err)
    }
  }
}

class DropboxSyncService {
  // ...

  func saveFile(filename: String, data: NSData) -> Result<()> {
    let path = DBPath.root().childPath(filename)
    return DBFilesystem.sharedFilesystem().createFile(path) >>- { $0.writeData(data) }
  }
}
```

## Conclusion

We see that using the Dropbox SDK is a great way to have automatic file syncing
within our apps. The SDK is written in Objective-C but we can easily modify its
functions using a `Result` type and some functional concepts like bind and
fmap to make it convenient to use in Swift as well.
