Swiftpack.co - Package - dropbox/SwiftyDropbox

Dropbox for Swift

The Official Dropbox Swift SDK for integrating with Dropbox API v2 on iOS or macOS.

Full documentation here.


Table of Contents


System requirements

  • iOS 9.0+
  • macOS 10.11+
  • Xcode 10.0+
  • Swift 5.0+

Get Started

Register your application

Before using this SDK, you should register your application in the Dropbox App Console. This creates a record of your app with Dropbox that will be associated with the API calls you make.

Obtain an OAuth 2.0 token

All requests need to be made with an OAuth 2.0 access token. An OAuth token represents an authenticated link between a Dropbox app and a Dropbox user account or team.

Once you've created an app, you can go to the App Console and manually generate an access token to authorize your app to access your own Dropbox account. Otherwise, you can obtain an OAuth token programmatically using the SDK's pre-defined auth flow. For more information, see below.


SDK distribution

You can integrate the Dropbox Swift SDK into your project using one of several methods.

Swift Package Manager

The Dropbox Swift SDK can be installed in your project using Swift Package Manager by specifying the Dropbox Swift SDK repository URL:

https://github.com/dropbox/SwiftyDropbox.git

Refer to Apple's "Adding Package Dependencies to Your App" documentation for more information.

CocoaPods

To use CocoaPods, a dependency manager for Cocoa projects, you should first install it using the following command:

$ gem install cocoapods

Then navigate to the directory that contains your project and create a new file called Podfile. You can do this either with pod init, or open an existing Podfile, and then add pod 'SwiftyDropbox' to the main loop. Your Podfile should look something like this:

use_frameworks!

target '<YOUR_PROJECT_NAME>' do
    pod 'SwiftyDropbox'
end

Then, run the following command to install the dependency:

$ pod install

Once your project is integrated with the Dropbox Swift SDK, you can pull SDK updates using the following command:

$ pod update

Note: SwiftyDropbox requires CocoaPods 1.0.0+ when using Alamofire 4.0.0+. Because of this requirement, the CocoaPods App (which uses CocoaPods 1.0.0) cannot be used.


Carthage

You can also integrate the Dropbox Swift SDK into your project using Carthage, a decentralized dependency manager for Cocoa. Carthage offers more flexibility than CocoaPods, but requires some additional work. You can install Carthage (with Xcode 7+) via Homebrew:

brew update
brew install carthage

To install the Dropbox Swift SDK via Carthage, you need to create a Cartfile in your project with the following contents:

# SwiftyDropbox
github "https://github.com/dropbox/SwiftyDropbox" ~> 6.0.3

Then, run the following command to install the dependency to checkout and build the Dropbox Swift SDK repository:

iOS
carthage update --platform iOS

In the Project Navigator in Xcode, select your project, and then navigate to General > Linked Frameworks and Libraries, then drag and drop SwiftyDropbox.framework (from Carthage/Build/iOS). Then to add SwiftyDropbox's Alamofire dependency, drag and drop Alamofire.framework (from Carthage/Build/iOS) to Linked Frameworks and Libraries, as well.

Then, navigate to Build Phases > + > New Run Script Phase. In the newly-created Run Script section, add the following code to the script body area (beneath the "Shell" box):

/usr/local/bin/carthage copy-frameworks

Then, navigate to the Input Files section and add the following path:

$(SRCROOT)/Carthage/Build/iOS/SwiftyDropbox.framework
$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework
macOS
carthage update --platform Mac

In the Project Navigator in Xcode, select your project, and then navigate to General > Embedded Binaries, then drag and drop SwiftyDropbox.framework (from Carthage/Build/Mac). Then to add SwiftyDropbox's Alamofire dependency, drag and drop Alamofire.framework (from Carthage/Build/Mac) to Linked Frameworks and Libraries, as well.

Then navigate to Build Phases > + > New Copy Files Phase. In the newly-created Copy Files section, click the Destination drop-down menu and select Products Directory, then drag and drop SwiftyDropbox.framework.dSYM (from Carthage/Build/Mac).


Manually add subproject

Finally, you can also integrate the Dropbox Swift SDK into your project manually with the help of Carthage. Please take the following steps:

Create a Cartfile in your project with the same contents as the Cartfile listed in the Carthage section of the README.

Then, run the following command to checkout and build the Dropbox Swift SDK repository:

iOS
carthage update --platform iOS

Once you have checked-out out all the necessary code via Carthage, drag the Carthage/Checkouts/SwiftyDropbox/Source/SwiftyDropbox/SwiftyDropbox.xcodeproj file into your project as a subproject.

Then, in the Project Navigator in Xcode, select your project, and then navigate to your project's build target > General > Embedded Binaries > + and then add the SwiftyDropbox.framework file for the iOS platform.

Finally, to retrieve SwiftyDropbox's Alamofire dependency, drag the Carthage/Checkouts/Alamofire/Alamofire.xcodeproj project into your project (as you did with SwiftyDropbox.xcodeproj). Then, in the Project Navigator in Xcode, select your project, and then navigate to your project's build target > General > Linked Frameworks and Libraries > + and then add the Alamofire.framework file for the iOS platform.

macOS
carthage update --platform Mac

Once you have checked-out out all the necessary code via Carthage, drag the Carthage/Checkouts/SwiftyDropbox/Source/SwiftyDropbox/SwiftyDropbox.xcodeproj file into your project as a subproject.

Then, in the Project Navigator in Xcode, select your project, and then navigate to your project's build target > General > Embedded Binaries > + and then add the SwiftyDropbox.framework file for the macOS platform.

Finally, to retrieve SwiftyDropbox's Alamofire dependency, drag the Carthage/Checkouts/Alamofire/Alamofire.xcodeproj project into your project (as you did with SwiftyDropbox.xcodeproj). Then, in the Project Navigator in Xcode, select your project, and then navigate to your project's build target > General > Linked Frameworks and Libraries > + and then add the Alamofire.framework file for the macOS platform.


Configure your project

Once you have integrated the Dropbox Swift SDK into your project, there are a few additional steps to take before you can begin making API calls.

Application .plist file

If you are compiling on iOS SDK 9.0, you will need to modify your application's .plist to handle Apple's new security changes to the canOpenURL function. You should add the following code to your application's .plist file:

<key>LSApplicationQueriesSchemes</key>
    <array>
        <string>dbapi-8-emm</string>
        <string>dbapi-2</string>
    </array>

This allows the Swift SDK to determine if the official Dropbox iOS app is installed on the current device. If it is installed, then the official Dropbox iOS app can be used to programmatically obtain an OAuth 2.0 access token.

Additionally, your application needs to register to handle a unique Dropbox URL scheme for redirect following completion of the OAuth 2.0 authorization flow. This URL scheme should have the format db-<APP_KEY>, where <APP_KEY> is your Dropbox app's app key, which can be found in the App Console.

You should add the following code to your .plist file (but be sure to replace <APP_KEY> with your app's app key):

<key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>db-<APP_KEY></string>
            </array>
            <key>CFBundleURLName</key>
            <string></string>
        </dict>
    </array>

After you've made the above changes, your application's .plist file should look something like this:

Info .plist Example


Handling the authorization flow

There are three methods to programmatically retrieve an OAuth 2.0 access token:

  • Direct auth (iOS only): This launches the official Dropbox iOS app (if installed), authenticates via the official app, then redirects back into the SDK
  • Safari view controller auth (iOS only): This launches a SFSafariViewController to facillitate the auth flow. This is desirable because it is safer for the end-user, and pre-existing session data can be used to avoid requiring the user to re-enter their Dropbox credentials.
  • Redirect to external browser (macOS only): This launches the user's default browser to facillitate the auth flow. This is also desirable because it is safer for the end-user, and pre-existing session data can be used to avoid requiring the user to re-enter their Dropbox credentials.

To facilitate the above authorization flows, you should take the following steps:


Initialize a DropboxClient instance

From your application delegate:

iOS
import SwiftyDropbox

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    DropboxClientsManager.setupWithAppKey("<APP_KEY>")
    return true
}
macOS
import SwiftyDropbox

func applicationDidFinishLaunching(_ aNotification: Notification) {
    DropboxClientsManager.setupWithAppKeyDesktop("<APP_KEY>")
}

Begin the authorization flow

You can commence the auth flow by calling authorizeFromController:controller:openURL method in your application's view controller.

From your view controller:

iOS
import SwiftyDropbox

func myButtonInControllerPressed() {

    // Use only one of these two flows at once:

    // Legacy authorization flow that grants a long-lived token.
    DropboxClientsManager.authorizeFromController(UIApplication.shared,
                                                  controller: self,
                                                  openURL: { (url: URL) -> Void in
                                                    UIApplication.shared.openURL(url)
                                                  })

  // New: OAuth 2 code flow with PKCE that grants a short-lived token with scopes.
  let scopeRequest = ScopeRequest(scopeType: .user, scopes: ["account_info.read"], includeGrantedScopes: false)
  DropboxClientsManager.authorizeFromControllerV2(
      UIApplication.shared,
      controller: self,
      loadingStatusDelegate: nil,
      openURL: { (url: URL) -> Void in UIApplication.shared.openURL(url) },
      scopeRequest: scopeRequest
  )
}

macOS
import SwiftyDropbox

func myButtonInControllerPressed() {

    // Use only one of these two flows at once:

    // Legacy authorization flow that grants a long-lived token.
    DropboxClientsManager.authorizeFromController(sharedWorkspace: NSWorkspace.shared,
                                                  controller: self,
                                                  openURL: { (url: URL) -> Void in
                                                    NSWorkspace.shared.open(url)
                                                  })

  // New: OAuth 2 code flow with PKCE that grants a short-lived token with scopes.
  let scopeRequest = ScopeRequest(scopeType: .user, scopes: ["account_info.read"], includeGrantedScopes: false)
  DropboxClientsManager.authorizeFromControllerV2(
      sharedWorkspace: NSWorkspace.shared,
      controller: self,
      loadingStatusDelegate: nil,
      openURL: {(url: URL) -> Void in NSWorkspace.shared.open(url)},
      scopeRequest: scopeRequest
  )
}

Beginning the authentication flow via in-app webview will launch a window like this:

Auth Flow Init Example


Handle redirect back into SDK

To handle the redirection back into the Swift SDK once the authentication flow is complete, you should add the following code in your application's delegate:

iOS
import SwiftyDropbox

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    let oauthCompletion: DropboxOAuthCompletion = {
      if let authResult = $0 {
          switch authResult {
          case .success:
              print("Success! User is logged into DropboxClientsManager.")
          case .cancel:
              print("Authorization flow was manually canceled by user!")
          case .error(_, let description):
              print("Error: \(String(describing: description))")
          }
      }
    }
    let canHandleUrl = DropboxClientsManager.handleRedirectURL(url, completion: oauthCompletion)
    return canHandleUrl
}

macOS
import SwiftyDropbox

func applicationDidFinishLaunching(_ aNotification: Notification) {
    ...... // code outlined above goes here

    NSAppleEventManager.shared().setEventHandler(self,
                                                 andSelector: #selector(handleGetURLEvent),
                                                 forEventClass: AEEventClass(kInternetEventClass),
                                                 andEventID: AEEventID(kAEGetURL))
}

func handleGetURLEvent(_ event: NSAppleEventDescriptor?, replyEvent: NSAppleEventDescriptor?) {
    if let aeEventDescriptor = event?.paramDescriptor(forKeyword: AEKeyword(keyDirectObject)) {
        if let urlStr = aeEventDescriptor.stringValue {
            let url = URL(string: urlStr)!
            let oauthCompletion: DropboxOAuthCompletion = {
                if let authResult = $0 {
                    switch authResult {
                    case .success:
                        print("Success! User is logged into Dropbox.")
                    case .cancel:
                        print("Authorization flow was manually canceled by user!")
                    case .error(_, let description):
                        print("Error: \(String(describing: description))")
                    }
                }
            }
            DropboxClientsManager.handleRedirectURL(url, completion: oauthCompletion)
            // this brings your application back the foreground on redirect
            NSApp.activate(ignoringOtherApps: true)
        }
    }
}

After the end user signs in with their Dropbox login credentials via the in-app webview, they will see a window like this:

Auth Flow Approval Example

If they press Allow or Cancel, the db-<APP_KEY> redirect URL will be launched from the webview, and will be handled in your application delegate's application:handleOpenURL method, from which the result of the authorization can be parsed.

Now you're ready to begin making API requests!


Try some API requests

Once you have obtained an OAuth 2.0 token, you can try some API v2 calls using the Swift SDK.

Dropbox client instance

Start by creating a reference to the DropboxClient or DropboxTeamClient instance that you will use to make your API calls.

import SwiftyDropbox

// Reference after programmatic auth flow
let client = DropboxClientsManager.authorizedClient

or

import SwiftyDropbox

// Initialize with manually retrieved auth token
let client = DropboxClient(accessToken: "<MY_ACCESS_TOKEN>")

Handle the API response

The Dropbox User API and Business API have three types of requests: RPC, Upload and Download.

The response handlers for each request type are similar to one another. The arguments for the handler blocks are as follows:

  • route result type (Void if the route does not have a return type)
  • network error (either a route-specific error or generic network error)
  • output content (URL / Data reference to downloaded output for Download-style endpoints only)

Note: Response handlers are required for all endpoints. Progress handlers, on the other hand, are optional for all endpoints.


Request types

RPC-style request

client.files.createFolder(path: "/test/path/in/Dropbox/account").response { response, error in
    if let response = response {
        print(response)
    } else if let error = error {
        print(error)
    }
}

Upload-style request

let fileData = "testing data example".data(using: String.Encoding.utf8, allowLossyConversion: false)!

let request = client.files.upload(path: "/test/path/in/Dropbox/account", input: fileData)
    .response { response, error in
        if let response = response {
            print(response)
        } else if let error = error {
            print(error)
        }
    }
    .progress { progressData in
        print(progressData)
    }

// in case you want to cancel the request
if someConditionIsSatisfied {
    request.cancel()
}

Download-style request

// Download to URL
let fileManager = FileManager.default
let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destURL = directoryURL.appendingPathComponent("myTestFile")
let destination: (URL, HTTPURLResponse) -> URL = { temporaryURL, response in
    return destURL
}
client.files.download(path: "/test/path/in/Dropbox/account", overwrite: true, destination: destination)
    .response { response, error in
        if let response = response {
            print(response)
        } else if let error = error {
            print(error)
        }
    }
    .progress { progressData in
        print(progressData)
    }


// Download to Data
client.files.download(path: "/test/path/in/Dropbox/account")
    .response { response, error in
        if let response = response {
            let responseMetadata = response.0
            print(responseMetadata)
            let fileContents = response.1
            print(fileContents)
        } else if let error = error {
            print(error)
        }
    }
    .progress { progressData in
        print(progressData)
    }

Handling responses and errors

Dropbox API v2 deals largely with two data types: structs and unions. Broadly speaking, most route arguments are struct types and most route errors are union types.

NOTE: In this context, "structs" and "unions" are terms specific to the Dropbox API, and not to any of the languages that are used to query the API, so you should avoid thinking of them in terms of their Swift definitions.

Struct types are "traditional" object types, that is, composite types made up of a collection of one or more instance fields. All public instance fields are accessible at runtime, regardless of runtime state.

Union types, on the other hand, represent a single value that can take on multiple value types, depending on state. We capture all of these different type scenarios under one "union object", but that object will exist only as one type at runtime. Each union state type, or tag, may have an associated value (if it doesn't, the union state type is said to be void). Associated value types can either be primitives, structs or unions. Although the Swift SDK represents union types as objects with multiple instance fields, at most one instance field is accessible at runtime, depending on the tag state of the union.

For example, the /delete endpoint returns an error, Files.DeleteError, which is a union type. The Files.DeleteError union can take on two different tag states: path_lookup (if there is a problem looking up the path) or path_write (if there is a problem writing -- or in this case deleting -- to the path). Here, both tag states have non-void associated values (of types Files.LookupError and Files.WriteError, respectively).

In this way, one union object is able to capture a multitude of scenarios, each of which has their own value type.

To properly handle union types, you should pass each union through a switch statement, and check each possible tag state associated with the union. Once you have determined the current tag state of the union, you can then access the value associated with that tag state (provided there exists an associated value type, i.e., it's not void).


Route-specific errors

client.files.deleteV2(path: "/test/path/in/Dropbox/account").response { response, error in
    if let response = response {
        print(response)
    } else if let error = error {
        switch error as CallError {
        case .routeError(let boxed, let userMessage, let errorSummary, let requestId):
            print("RouteError[\(requestId)]:")

            switch boxed.unboxed as Files.DeleteError {
            case .pathLookup(let lookupError):
                switch lookupError {
                case .notFound:
                    print("There is nothing at the given path.")
                case .notFile:
                    print("We were expecting a file, but the given path refers to something that isn't a file.")
                case .notFolder:
                    print("We were expecting a folder, but the given path refers to something that isn't a folder.")
                case .restrictedContent:
                    print("The file cannot be transferred because the content is restricted...")
                case .malformedPath(let malformedPath):
                    print("Malformed path: \(malformedPath)")
                case .other:
                    print("Unknown")
                }
            case .pathWrite(let writeError):
                print("WriteError: \(writeError)")
                // you can handle each `WriteError` case like the `DeleteError` cases above
            case .tooManyWriteOperations:
                print("Another write operation occurring at the same time prevented this from succeeding.")
            case .tooManyFiles:
                print("There are too many files to delete.")
            case .other:
                print("Unknown")
            }
        case .internalServerError(let code, let message, let requestId):
            ....
            ....
            // a not route-specific error occurred
        ....
        ....
        ....
        }
    }
}

Generic network request errors

In the case of a network error, errors are either specific to the endpoint (as shown above) or more generic errors.

To determine if an error is route-specific or not, the error object should be cast as a CallError, and depending on the type of error, handled in the appropriate switch statement.

client.files.deleteV2(path: "/test/path/in/Dropbox/account").response { response, error in
    if let response = response {
        print(response)
    } else if let error = error {
        switch error as CallError {
        case .routeError(let boxed, let userMessage, let errorSummary, let requestId):
            // a route-specific error occurred
            // see handling above
            ....
            ....
            ....
        case .internalServerError(let code, let message, let requestId):
            print("InternalServerError[\(requestId)]: \(code): \(message)")
        case .badInputError(let message, let requestId):
            print("BadInputError[\(requestId)]: \(message)")
        case .authError(let authError, let userMessage, let errorSummary, let requestId):
            print("AuthError[\(requestId)]: \(userMessage) \(errorSummary) \(authError)")
        case .accessError(let accessError, let userMessage, let errorSummary, let requestId):
            print("AccessError[\(requestId)]: \(userMessage) \(errorSummary) \(accessError)")
        case .rateLimitError(let rateLimitError, let userMessage, let errorSummary, let requestId):
            print("RateLimitError[\(requestId)]: \(userMessage) \(errorSummary) \(rateLimitError)")
        case .httpError(let code, let message, let requestId):
            print("HTTPError[\(requestId)]: \(code): \(message)")
        case .clientError(let error):
            print("ClientError: \(error)")
        }
    }
}

Response handling edge cases

Some routes return union types as result types, so you should be prepared to handle these results in the same way that you handle union route errors. Please consult the documentation for each endpoint that you use to ensure you are properly handling the route's response type.

A few routes return result types that are datatypes with subtypes, that is, structs that can take on multiple state types like unions.

For example, the /delete endpoint returns a generic Metadata type, which can exist either as a FileMetadata struct, a FolderMetadata struct, or a DeletedMetadata struct. To determine at runtime which subtype the Metadata type exists as, pass the object through a switch statement, and check for each possible class, with the result casted accordingly. See below:

client.files.deleteV2(path: "/test/path/in/Dropbox/account").response { response, error in
    if let response = response {
        switch response {
        case let fileMetadata as Files.FileMetadata:
            print("File metadata: \(fileMetadata)")
        case let folderMetadata as Files.FolderMetadata:
            print("Folder metadata: \(folderMetadata)")
        case let deletedMetadata as Files.DeletedMetadata:
            print("Deleted entity's metadata: \(deletedMetadata)")
        }
    } else if let error = error {
        switch error as CallError {
        case .routeError(let boxed, let userMessage, let errorSummary, let requestId):
            // a route-specific error occurred
            // see handling above
        case .internalServerError(let code, let message, let requestId):
            ....
            ....
            // a not route-specific error occurred
            // see handling above
        ....
        ....
        ....
        }
    }
}

This Metadata object is known as a datatype with subtypes in our API v2 documentation.

Datatypes with subtypes are a way combining structs and unions. Datatypes with subtypes are struct objects that contain a tag, which specifies which subtype the object exists as at runtime. The reason we have this construct, as with unions, is so we can capture a multitude of scenarios with one object.

In the above example, the Metadata type can exists as FileMetadata, FolderMetadata or DeleteMetadata. Each of these types have common instances fields like "name" (the name for the file, folder or deleted type), but also instance fields that are specific to the particular subtype. In order to leverage inheritance, we set a common supertype called Metadata which captures all of the common instance fields, but also has a tag instance field, which specifies which subtype the object currently exists as.

In this way, datatypes with subtypes are a hybrid of structs and unions. Only a few routes return result types like this.


Customizing network calls

Configure network client

It is possible to configure the networking client used by the SDK to make API requests. You can supply custom fields like a custom user agent or custom delegates to manage response handler code, or a custom server trust policy. See below:

iOS
import SwiftyDropbox

let transportClient = DropboxTransportClient(accessToken: "<MY_ACCESS_TOKEN>",
                                             baseHosts: nil,
                                             userAgent: "CustomUserAgent",
                                             selectUser: nil,
                                             sessionDelegate: mySessionDelegate,
                                             backgroundSessionDelegate: myBackgroundSessionDelegate,
                                             serverTrustPolicyManager: myServerTrustPolicyManager)

DropboxClientsManager.setupWithAppKey("<APP_KEY>", transportClient: transportClient)
macOS
import SwiftyDropbox

let transportClient = DropboxTransportClient(accessToken: "<MY_ACCESS_TOKEN>",
                                             baseHosts: nil,
                                             userAgent: "CustomUserAgent",
                                             selectUser: nil,
                                             sessionDelegate: mySessionDelegate,
                                             backgroundSessionDelegate: myBackgroundSessionDelegate,
                                             serverTrustPolicyManager: myServerTrustPolicyManager)

DropboxClientsManager.setupWithAppKeyDesktop("<APP_KEY>", transportClient: transportClient)

Specify API call response queue

By default, response/progress handler code runs on the main thread. You can set a custom response queue for each API call that you make via the response method, in the event want your response/progress handler code to run on a different thread:

let client = DropboxClientsManager.authorizedClient!

client.files.listFolder(path: "").response(queue: DispatchQueue(label: "MyCustomSerialQueue")) { response, error in
    if let result = response {
        print(Thread.current)  // Output: <NSThread: 0x61000007bec0>{number = 4, name = (null)}
        print(Thread.main)     // Output: <NSThread: 0x608000070100>{number = 1, name = (null)}
        print(result)
    }
}

DropboxClientsManager class

The Swift SDK includes a convenience class, DropboxClientsManager, for integrating the different functions of the SDK into one class.

Single Dropbox user case

For most apps, it is reasonable to assume that only one Dropbox account (and access token) needs to be managed at a time. In this case, the DropboxClientsManager flow looks like this:

  • call setupWithAppKey/setupWithAppKeyDesktop (or setupWithTeamAppKey/setupWithTeamAppKeyDesktop) in integrating app's app delegate
  • client manager determines whether any access tokens are stored -- if any exist, one token is arbitrarily chosen to use
  • if no token is found, call authorizeFromController/authorizeFromControllerDesktop to initiate the OAuth flow
  • if auth flow is initiated, call handleRedirectURL (or handleRedirectURLTeam) in integrating app's app delegate to handle auth redirect back into the app and store the retrieved access token (using a DropboxOAuthManager instance)
  • client manager instantiates a DropboxTransportClient (if not supplied by the user)
  • client manager instantiates a DropboxClient (or DropboxTeamClient) with the transport client as a field

The DropboxClient (or DropboxTeamClient) is then used to make all of the desired API calls.

  • call unlinkClients to logout Dropbox user and clear all access tokens

Multiple Dropbox user case

For some apps, it is necessary to manage more than one Dropbox account (and access token) at a time. In this case, the DropboxClientsManager flow looks like this:

  • access token uids are managed by the app that is integrating with the SDK for later lookup
  • call setupWithAppKeyMultiUser/setupWithAppKeyMultiUserDesktop (or setupWithTeamAppKeyMultiUser/setupWithTeamAppKeyMultiUserDesktop) in integrating app's app delegate
  • client manager determines whether an access token is stored with thetokenUid as a key -- if one exists, this token is chosen to use
  • if no token is found, call authorizeFromController/authorizeFromControllerDesktop to initiate the OAuth flow
  • if auth flow is initiated, call handleRedirectURL (or handleRedirectURLTeam) in integrating app's app delegate to handle auth redirect back into the app and store the retrieved access token (using a DropboxOAuthManager instance)
  • at this point, the app that is integrating with the SDK should persistently save the tokenUid from the DropboxAccessToken field of the DropboxOAuthResult object returned from the handleRedirectURL (or handleRedirectURLTeam) method
  • tokenUid can be reused either to authorize a new user mid-way through an app's lifecycle via reauthorizeClient (or reauthorizeTeamClient) or when the app initially launches via setupWithAppKeyMultiUser/setupWithAppKeyMultiUserDesktop (or setupWithTeamAppKeyMultiUser/setupWithTeamAppKeyMultiUserDesktop)
  • client manager instantiates a DropboxTransportClient (if not supplied by the user)
  • client manager instantiates a DropboxClient (or DropboxTeamClient) with the transport client as a field

The DropboxClient (or DropboxTeamClient) is then used to make all of the desired API calls.

  • call resetClients to logout Dropbox user but not clear any access tokens
  • if specific access tokens need to be removed, use the clearStoredAccessToken method in DropboxOAuthManager

Examples

  • PhotoWatch - View photos from your Dropbox. Supports Apple Watch.

Documentation


Stone

All of our routes and data types are auto-generated using a framework called Stone.

The stone repo contains all of the Swift specific generation logic, and the spec repo contains the language-neutral API endpoint specifications which serve as input to the language-specific generators.


Modifications

If you're interested in modifying the SDK codebase, you should take the following steps:

  • clone this GitHub repository to your local filesystem
  • run git submodule init and then git submodule update
  • navigate to TestSwifty_[iOS|macOS]
  • check the CocoaPods version installed (via pod --version) is same as "locked" in TestSwifty_[iOS|macOS]/Podfile.lock
  • run pod install
  • open TestSwifty_[iOS|macOS]/TestSwifty_[iOS|macOS].xcworkspace in Xcode
  • implement your changes to the SDK source code.

To ensure your changes have not broken any existing functionality, you can run a series of integration tests:

  • create a new app on https://www.dropbox.com/developers/apps/, with "Full Dropbox" access. Note the App key
  • open Info.plist and configure the "URL types > Item 0 (Editor) > URL Schemes > Item 0" key to db-"App key"
  • open AppDelegate.swift and replace "FULL_DROPBOX_APP_KEY" with the App key as well
  • run the test app on your device and follow the on-screen instructions

Bugs

Please post any bugs to the issue tracker found on the project's GitHub page.

Please include the following with your issue:

  • a description of what is not working right
  • sample code to help replicate the issue

Thank you!

Github

link
Stars: 545

Dependencies

Used By

Total: 0

Releases

- 2020-10-03 01:56:26

Updated Swift version from 4.2 to 5.0.

- 2020-09-17 22:42:12

Now the SDK can be properly added as a Swift Package in Xcode.

- 2020-09-01 18:14:39

DropboxClientsManager's handleRedirectURL(_:completion:) method now returns a boolean indicates whether the URL can be handled.

Support Short-lived and Scoped token feature - 2020-08-11 00:46:24

  • Introduced a new authentication flow for Short-lived and Scoped token feature.
  • DropboxClientsManager's handleRedirectURL method is now made asynchronous to accommodate the new authentication flow.
  • Updated to latest API specs.

5.1.0 release - 2019-06-21 22:38:56

  • Update stone specs.
  • Upgrade Alamofire to 4.8.2.

Swift 4.2 - 2018-12-09 21:40:23

Swift 4.2 and XCode 10 are now required For Swift 3 and XCode 9, use the 4.8.x versions.

Spec Update - 2018-12-05 23:53:44

Common Namespace:

  • Force matching dot character in alias EmailAddress
  • Allow DisplayNameLegacy to support a name of zero chars

Contacts namespace:

  • New namespace
  • New routes: delete_manual_contacts and delete_manual_contacts_batch
  • New argument structs for new routes

File_properties namespace:

  • Doesn’t allow app folder app to access file property endpoints.

Files namespace:

  • Create copy_batch:2 and move_batch:2 endpoints. Deprecate existing copy_batch and move_batch.

Sharing namespace:

  • Add no_one option to LinkAudience union

Sharing_files namespace:

  • Doesn’t allow app folder app to access sharing files endpoints.

Teams namespace:

  • Only Team apps with Team member file access can access team/properties endpoints.
  • Add is_disconnected boolean to RemovedStatus struct
  • Add error response type to namespace/list route

Team_log namespace:

  • New event types added

Upgrade Alamofire & fix warnings in auto-generated code. - 2018-11-13 22:38:11

  • Update Alamofire to 4.7.3 from 4.5.0
  • Fix warnings from the usage of 'open' in auto-generated code.

Spec Update - 2018-10-11 00:00:38

Files Namespace:

  • Updated doc strings

Team_log Namespace:

  • Updated event docstrings
  • New reset field for loading events with a cursor
  • New event types added

Team_policies Namespace:

  • New CameraUploadsPolicyState union

Spec Update - 2018-05-25 22:02:18

The following spec changes are released in this version.

files namespace:

New documentation
Added new too_large error type for uploads

team namespace:

Add is_directory_restricted attribute to MemberProfile, MemberAddArg
Add new_is_directory_restricted to MemberSetProfileArg

team_log namespace:

Additional documentation

Spec Update - 2018-04-11 21:31:57

  • file_properties namespace:

    • updated comments
  • files namespace:

    • new SymlinkInfo struct on FileMetadata
    • added too_many_write_operations error type to WriteError
    • New CreateFolderBatch endpoint and related datatypes
    • Added parent_rev attribute to DeleteArg
    • New Thumbnail sizes
    • New ThumbnailMode object
    • New SyncSettings objects
  • sharing namespace:

    • add seen_State.PlatformType to UserFileMembershipInfo
    • new set_access_inheritance for FolderAction
    • additional user info added to UserInfo struct
    • new AccessInheritance union
    • new set_access_inheritance route
  • team namespace:

    • new HasTeamSelectiveSync object
    • new selective sync settings included in various return objects and error types
    • new update_sync_settings route
  • team_policies namespace:

    • new ShowcaseEnabledPolicy object
    • new ShowcaseDownloadPolicy object
    • new ShowcaseExternalSharingPolicy object

Spec Update - 2018-04-06 21:48:30

  • Added support for PathRoot

  • files namespace:

    • Added download_zip endpoint
    • Add properties_error to UploadError
  • team_common namespace:

    • Added new MemberSpaceLimitType union
  • team namespace:

    • Added additional error types to MembersRemoveError union
    • Updated docstring
  • users namespace:

    • Additional member space limit fields in TeamSpaceAllocation struct
  • team_log namespace:

    • Lots of updates to struct names and descriptions (note these routes and structs are still in preview and subject to further changes)

Spec update and bug fixes - 2017-11-19 02:38:21

Spec update

    Namespace Common:
    - New PathRoot and RootInfo related structs and unions

    Namespace File_properties:
    - additional documentation
    - new RemoveTemplateArg struct
    - new routes for templates
    - updated TemplateFilter union
    - new PropertiesSearch structs
    - new properties/search/continue route

    Namespace Files:
    - updated documentation
    - updated routes with support for select_admin_mode
    - add include_property_groups attribute to to args structs
    - add propert_groups attribute to response structs

    Namespace Sharing:
    - update routes to include indicate for select_admin_mode
    - updated examples
    - add too_many_files attribute to RemoveFolderMemberError

    Namespace Team:
    - add new has_file_events input/output structs to featres/get_values endpoint
    - add additional examples for device related structs
    - reduce minimum value for UserQuota to 15
    - Updated QuotaError structs
    - New structs and routes to support excluded_users

    Namespace Team_policies:
    - add new SmartSyncPolicy union

    Namespace Users:
    - add root_info object to FullAccount struct

Bug fixes

  • Use correct String constructor.
  • Improved auth flow security with state parameter.

Bug fix - 2017-10-19 18:40:50

Bug fix

  • Fixed (potentially) longpoll crash issue.

Spec update - 2017-10-11 03:16:51

Spec update

  • Move shared_link to end of parameter list for ListFolderArg.
  • Add mode to ListRevisionsArg.

Bug fix

  • Fixed bug where project wouldn't build in Xcode 8.

Spec update and bug fix - 2017-09-19 08:59:15

Spec update

General: Updated docstrings in multiple namespaces Updated general route configuration to support select_admin_mode

file_properties namespace: New routes and structs for the file properties and templates API functionality

file_requests namespace: New routes and structs for the file_requests API functionality

files: deprecate preview routes for files_properties

properties: removed in favor of file_properties

team_property_templates: removed in favor of file_properties

team: deprecate beta properites routes

team_members: Update NamePart? to be OptionalNamePart?

team_policies: Add new team policies for SSO Paper, RolloutMethod and PasswordStrength

Bug fix

  • Removed Xcode 9 compile warnings.

Spec update and numerous fixes - 2017-05-17 19:25:37

Potentially breaking changes

  • Browser auth no longer supported on iOS
  • In-app webview no longer supported on iOS or macOS

Example:

DropboxClientsManager.authorizeFromController(UIApplication.shared, controller: self,
                                                      openURL: {(url: URL) -> Void in UIApplication.shared.openURL(url)},
                                                browserAuth:true)

becomes

DropboxClientsManager.authorizeFromController(UIApplication.shared, controller: self,
                                                      openURL: {(url: URL) -> Void in UIApplication.shared.openURL(url)})

Motivation:

Companies like Google, which Dropbox uses as an identity provider, are starting to force everyone away from using lightweight web-views, in favor of full-fledged browsers (for security reasons).

Fixes

  • Added SFSafariViewController for auth.
  • Update to AlamoFire 4.4.0
  • Fixed CocoaPods WebKit import issue.
  • Switched to OAuth 2.0 API v2 page.
  • Fixed longpoll timeout issue.
  • Fixed locked phone upload issue.
  • Make controllers nullable.
  • Clear persistent cookies.
  • Fixed cancel handler bug on iOS.
  • OAuth flow now localized.
  • Added support for 403 access error type.
  • Exposed user_message and error_summary in error response.
  • Fixed Xcode warnings.
  • Consolidated test project.
  • Added release scripts.

Spec update

Files namespace:

  • Added too_many_write_operations in UploadSessionFinishError.
  • Removed PathRootError and invalid_path_root in LookupError. (breaking change)
  • Added team_folder to WriteError.

Sharing namespace:

  • Added access_level to LinkSettings.
  • Added change_access_level to LinkAction.
  • Added access_level and audience_restricting_shared_folder to SharedContentLinkMetadataBase.
  • Added access_type and expected_link_metadata to SharedFileMetadata.
  • Added path to ParentFolderAccessInfo.
  • Added actions to UpdateFolderPolicyArg.
  • Added team_folder to UpdateFolderPolicyError.

Stone Cfg namespace:

  • Added allow_app_folder_app attribute.
  • Added takes_path_root attribute.

Team namespace:

  • Added route token/get_authenticated_admin.
  • Added route features/get_values.
  • Added expires to ActiveWebSession.
  • Added cursor and has_more to TeamFolderListResult for pagination.
  • Added route team_folder/list/continue.
  • Made member_given_name and member_surname optional in MemberAddArg. (breaking change)

User namepsace:

  • Moved AccountType from user namespace to user_common namespace.

Added TeamLog namespace.

Alamofire 3.5 update. - 2016-09-30 19:04:34

  • Update SwiftyDropbox to use Alamofire 3.5.

Swift 2.3 Support - 2016-09-30 19:03:54

  • Swift 2.3 support.
  • Xcode 7 support.

Swift 3 support. - 2016-09-30 19:03:10

  • Swift 3 support.
  • Xcode 8 support.
  • iOS 10 support.

Incremented version number. - 2016-09-30 19:02:22

Too many updates were included in 4.0.0, so version number is incremented here.

Bug fixes. - 2016-09-30 19:00:56

Bug fixes

  • Fixed deserialization bug where default field values for server response objects were assumed to be explicitly supplied by server.

Bug fixes. - 2016-09-30 19:00:09

Bug fixes

  • Updated public keyword to open.
  • Updated README.

Improvements. - 2016-09-30 18:59:07

Improvements

  • Added Swift Package Manager support.
  • Added networking client customizability.
  • Assorted name changes.
  • Reorganized main Xcode project.

Bug fixes / Improvements. - 2016-09-30 18:56:39

Bug fixes

  • Fixed bugs with main Xcode project.

Improvements

  • Removed preconditions from DropboxClientsManager to avoid hard crashes.
  • Added @discardableResult tags to all routes and response / progress methods to avoid compiler warnings in the event return object is not used.

Bug fixes. - 2016-09-30 18:54:00

Bug fixes

  • Fixed error with swift_2_3 branch where main Xcode project did not compile.

Bug fixes. - 2016-09-30 18:52:53

Bug fixes

  • Fixed OAuth bug on macOS.

Updated public specs. - 2016-09-29 19:29:11

Auth namespace:

  • Added user_suspended to AuthError.

Files namespace:

  • Added PathRootError.
  • Added invalid_path_root to LookupError.
  • Added autorename to CreateFolderArg.
  • Added DeleteBatchArg, DeleteBatchResultEntry, DeleteResult, DeleteBatchResult, DeleteBatchError and DeleteBatchJobStatus.
  • Added delete_batch and delete_batch/check routes.
  • Added RelocationPath.
  • Added to allow_shared_folder and autorename to RelocationArg.
  • Added RelocationBatchArg, RelocationBatchResult, RelocationBatchJobStatus. RelocationResult and RelocationBatchError.
  • Added copy_batch and copy_batch/check routes.
  • Added move_batch and move_batch/check routes.

Sharing namespace:

  • Changed PathOrId validation pattern.
  • Changed path in ShareFolderArg from type files.Path to files.WritePath.
  • Added contains_app_folder, contains_team_folder and invalid_path_root to ShareFolderArg.

Stone Cfg namespace:

  • Changed validation pattern for owner in Route.

Team namespace:

  • Added team_license_limit to MembersRecoverError.
  • Removed beta_group attribute from members/recover.

3.2.0 Release - 2016-08-03 01:25:51

SwiftyDropbox features / bugs:

  • OAuth 2 canceling supported as enumerated type.

Update to latest API specs: Stone specs:

  • Updated to new stone spec format (unions are default open).

Auth namespace:

  • Added RateLimitReason and RateLimitError for describing 429 responses.

Files namespace:

  • Added upload_session/finish_batch route.

Sharing namespace:

  • Added change_member_file_access route.
  • Added invite_view_no_comment to FolderPolicy.
  • Added share_link to FolderAction.
  • Added make_viewer_no_comment to MemberAction.
  • Added preview_url to SharedFolderMetadata.
  • Added access_details to MemberAccessLevelResult, which is the parent folders that a member has access to.
  • Added too_many_invitees error to AddFolderMemberError.
  • Added automatic_group to AddMemberSelectorError.
  • Added insufficient_quota to MountFolderError.
  • Added removed to TeamMemberStatus.

Team namespace:

  • Added new_group_management_type to GroupUpdateArgs for groups/delete.
  • Added include_removed flag to MembersListArg
  • Added members/recover route.

Other changes:

  • sharing/remove_folder_member only ever returns an async_job_id.
  • sharing/check_remove_member_job_status returns a MemberAccessLevelResult on completion.
  • sharing/update_folder_member returns a MemberAccessLevelResult on completion.
  • no_explicit_access information added to UpdateFolderMemberError.

3.1.0 (2016-07-07) - 2016-08-02 17:43:41

SwiftyDropbox features / bugs:

  • Add User/Business API unit integration tests for most endpoints.
  • Swift 2.2 compatibility.
  • Ability to download content directly to memory.
  • Added Business API endpoint support.
  • Integration with open source “Stone” generation framework.
  • Expose serialization methods.
  • Support Stone spec inheritance.
  • Support Union route parameters.
  • Improved OAuth support:
    • Network activity indicator shown when loading the app authorization page.
    • "No Internet Connection" error shown in the event there is no internet connection.
    • Cancelling OAuth flow sends a cancel URL to the delegate.
  • Perform downloads / uploads in background with background NSURLSession.
  • Upload route methods argument name changed from body to input.
  • Shared Dropbox client class variable (for convenience) removed.

Update to latest API specs:

  • Authentication
    • Add token/revoke endpoint.
  • Account
    • Add disabled field to Account.
  • Sharing (DbxUserSharingRequests)
    • Add endpoints for sharing files and managing shared file membership.
    • Change return type of removeFolderMember(..) endpoint to always be an async Job ID (LaunchEmptyResult.isAsyncJobId() will be true).
    • Add checkRemoveMemberJobStatus(..) for checking asynchronous removeFolderMember(..) requests.
      • Returns additional information compared to checkJobStatus(..)
    • Change return type of updateFolderMember(..) to return a MemberAccessLevelResult instead of void.
    • Add NO_EXPLICIT_ACCESS to UpdateFolderMemberError.
  • Files (DbxUserFilesRequests)
    • Add file properties endpoints.
    • Add saveUrl(..) saving online content to your Dropbox.
    • Shared folders (DbxUserSharingRequests)
    • Add AccessLevel.VIEWER_NO_COMMENT.
    • Add GroupInfo.getIsOwner().
    • Change return type of SharePathError.Tag.ALREADY_SHARED from Void to SharedFolderMetadata.
    • Add leaveACopy argument to relinquishFolderMembership(..).
    • Change relinquishFolderMembership(..) to return async job ID when leaving a copy.
    • Add JobError.Tag.RELINQUISH_FOLDER_MEMBERSHIP_ERROR.
    • Add leave_a_copy to FolderAction.
    • Add time_invited to SharedFolderMetadata.
    • Return is_osx_package or inside_osx_package error when user attempts to share an OS X package or
    • Add include_deleted and include_has_explicit_shared_members parameter to get_metadata and list_folder.
    • Add close parameter to upload_session/start endpoint to support explicitly close an upload session.
    • Add upload_session/append_v2 endpoint which provides explicit session close support.
    • Add copy_reference endpoint.
    • Add get_temporary_link endpoint.
  • Shared links
    • Add remove_expiration parameter to modify_shared_link_settings endpoint.
  • Business endpoints (DbxTeamTeamRequests)
    • Add MemberProfiler.getMembershipType().
    • Add keepAccount argument to membersRemove(..).
    • Add CANNOT_KEEP_ACCOUNT_AND_TRANSFER and CANNOT_KEEP_ACCOUNT_AND_DELETE_DATA to MembersRemoveError.
    • member_count is now optional in GroupSummary.
    • devices/list_team_devices endpoint is now deprecated by devices/list_member_devices.
    • linked_apps/list_team_linked_apps is now deprecated by linked_apps/list_members_linked_apps endpoint.
    • Add return_members parameter to groups/members/set_access_type endpoint.