• Stars
    star
    650
  • Rank 66,830 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created almost 9 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Swift SDK for the Dropbox API v2.

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 11.0+
  • macOS 10.12+
  • Xcode 10.0+ (11.0+ if you use Carthage)
  • Swift 5.1+

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. Carthage 0.37.0 is required due to XCFramework requirements on Xcode 12. You can install Carthage (with Xcode 11+) 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" ~> 9.2.0

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

iOS
carthage update --platform iOS --use-xcframeworks

Then, in the Project Navigator in Xcode, select your project, and then navigate to your project's build target > General > Frameworks, Libraries, and Embedded Content then drag both the SwiftyDropbox.xcframework and Alamofire.xcframework files (from Carthage/Build) into the table, choosing Embed & Sign.

macOS
carthage update --platform Mac --use-xcframeworks

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 then drag both the SwiftyDropbox.xcframework and Alamofire.xcframework files (from Carthage/Build) into the table, choosing Embed & Sign.


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:

SwiftUI note: You may need to create an Application Delegate if your application doesn't have one.

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. Note that the controller reference will be weakly held. For SwiftUI applications nil can be passed in for the controller argument and the app's root view controller will be used to present the flow.

From your view controller:

iOS
import SwiftyDropbox

func myButtonInControllerPressed() {
    // OAuth 2 code flow with PKCE that grants a short-lived token with scopes, and performs refreshes of the token automatically.
    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.open(url, options: [:], completionHandler: nil) },
        scopeRequest: scopeRequest
    )

    // Note: this is the DEPRECATED authorization flow that grants a long-lived token.
    // If you are still using this, please update your app to use the `authorizeFromControllerV2` call instead.
    // See https://dropbox.tech/developers/migrating-app-permissions-and-access-tokens
    // DropboxClientsManager.authorizeFromController(UIApplication.shared,
    //                                               controller: self,
    //                                               openURL: { (url: URL) -> Void in
    //                                                 UIApplication.shared.open(url, options: [:], completionHandler: nil)
    //                                               })
}
macOS
import SwiftyDropbox

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

    // Note: this is the DEPRECATED authorization flow that grants a long-lived token.
    // If you are still using this, please update your app to use the `authorizeFromControllerV2` call instead.
    // See https://dropbox.tech/developers/migrating-app-permissions-and-access-tokens
    // DropboxClientsManager.authorizeFromController(sharedApplication: NSApplication.shared,
    //                                               controller: self,
    //                                               openURL: { (url: URL) -> Void in
    //                                                 NSWorkspace.shared.open(url)
    //                                               })
}

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:

SwiftUI note: You may need to create an Application Delegate if your application doesn't have one.

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
}

Or if your app is iOS13+, or your app also supports Scenes, add the following code into your application's main scene delegate:

Note: You may need to create a Scene Delegate if your application doesn't have one._

import SwiftyDropbox

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
     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))")
          }
      }
    }

    for context in URLContexts {
        // stop iterating after the first handle-able url
        if DropboxClientsManager.handleRedirectURL(context.url, completion: oauthCompletion) { break }
    }
}
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.

  • On DropboxClientsManager, 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
    • SwiftUI note: You may need to create an Application Delegate if your application doesn't have one.
  • 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)
    • SwiftUI note: You may need to create an Application Delegate if your application doesn't have one.
  • 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.

  • On DropboxClientsManager 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

App Store Connect Privacy Labels

To assist developers using Dropbox SDKs in filling out Apple’s Privacy Practices Questionnaire, we’ve provided the below information on the data that may be collected and used by Dropbox.

As you complete the questionnaire you should note that the below information is general in nature. Dropbox SDKs are designed to be configured by the developer to incorporate Dropbox functionality as is best suited to their application. As a result of this customizable nature of the Dropbox SDKs, we are unable to provide information on the actual data collection and use for each application. We advise developers reference our Dropbox for HTTP Developers for specifics on how data is collected by each Dropbox API.

In addition, you should note that the information below only identifies Dropbox’s collection and use of data. You are responsible for identifying your own collection and use of data in your app, which may result in different questionnaire answers than identified below:

Data Collected by Dropbox Data Use Data Linked to the User Tracking
Contact Info
 ‒ Name Not collected N/A N/A N/A
 ‒ Email Address May be collected
(if you enable authentication using an email address)
β€’ Application functionality Y N
Health & Fitness Not collected N/A N/A N/A
Financial Info Not collected N/A N/A N/A
Location Not collected N/A N/A N/A
Sensitive Info Not collected N/A N/A N/A
Contacts Not collected N/A N/A N/A
User Content
 ‒ Audio Data May be collected β€’ Application functionality Y N
 ‒ Photos or Videos May be collected β€’ Application functionality Y N
 ‒ Other User Content May be collected β€’ Application functionality Y N
Browsing History Not collected N/A N/A N/A
Search History
 ‒ Search History May be collected
(if using search functionality)
β€’ Application functionality
β€’ Analytics
Y N
Identifiers
 ‒ User ID Collected β€’ Application functionality
β€’ Analytics
Y N
Purchases Not collected N/A N/A N/A
Usage Data
 ‒ Product Interaction Collected β€’ Application functionality
β€’ Analytics
β€’ Product personalization
Y N
Diagnostics
 ‒ Other Diagnostic Data Collected
(API call logs)
β€’ Application functionality Y N
Other Data N/A N/A N/A N/A

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!

More Repositories

1

zxcvbn

Low-Budget Password Strength Estimation
CoffeeScript
14,665
star
2

lepton

Lepton is a tool and file format for losslessly compressing JPEGs by an average of 22%.
C++
5,008
star
3

godropbox

Common libraries for writing Go services/applications.
Go
4,146
star
4

hackpad

Hackpad is a web-based realtime wiki.
Java
3,520
star
5

djinni

A tool for generating cross-language type declarations and interface bindings.
C++
2,860
star
6

json11

A tiny JSON library for C++11.
C++
2,478
star
7

PyHive

Python interface to Hive and Presto. 🐝
Python
1,663
star
8

pyannotate

Auto-generate PEP-484 annotations
Python
1,405
star
9

goebpf

Library to work with eBPF programs from Go
Go
1,110
star
10

css-style-guide

Dropbox’s (S)CSS authoring style guide
1,044
star
11

dbxcli

A command line client for Dropbox built using the Go SDK
Go
1,028
star
12

securitybot

Distributed alerting for the masses!
Python
991
star
13

dropbox-sdk-js

The Official Dropbox API V2 SDK for Javascript
JavaScript
923
star
14

dropbox-sdk-python

The Official Dropbox API V2 SDK for Python
Python
885
star
15

scooter

An SCSS framework & UI library for Dropbox Web.
CSS
789
star
16

rust-brotli

Brotli compressor and decompressor written in rust that optionally avoids the stdlib
Rust
769
star
17

changes

A dashboard for your code. A build system.
Python
759
star
18

pb-jelly

A protobuf code generation framework for the Rust language developed at Dropbox.
Rust
603
star
19

fast_rsync

An optimized implementation of librsync in pure Rust.
Rust
575
star
20

dropbox-sdk-java

A Java library for the Dropbox Core API.
Java
565
star
21

AffectedModuleDetector

A Gradle Plugin to determine which modules were affected by a set of files in a commit.
Kotlin
561
star
22

sqlalchemy-stubs

Mypy plugin and stubs for SQLAlchemy
Python
555
star
23

pyxl

A Python extension for writing structured and reusable inline HTML.
Python
525
star
24

stone

The Official API Spec Language for Dropbox API V2
Python
397
star
25

nsot

Network Source of Truth is an open source IPAM and network inventory database
Python
392
star
26

divans

Building better compression together
Rust
368
star
27

focus

A Gradle plugin that helps you speed up builds by excluding unnecessary modules.
Kotlin
356
star
28

dependency-guard

A Gradle plugin that guards against unintentional dependency changes.
Kotlin
343
star
29

dropbox-sdk-dotnet

The Official Dropbox API V2 SDK for .NET
C#
327
star
30

hydra

A multi-process MongoDB collection copier.
Python
319
star
31

mypy-PyCharm-plugin

A simple plugin that allows running mypy from PyCharm and navigate between errors
Java
313
star
32

nn

Non-nullable pointers for C++
C++
312
star
33

avrecode

Lossless video compression: decode an H.264-encoded video file and reversibly re-encode it as as a smaller file.
C++
270
star
34

componentbox

Reactive server-driven UI for iOS, Android, and web
Kotlin
256
star
35

dropshots

Easy on-device screenshot testing for Android.
Kotlin
255
star
36

python-zxcvbn

A realistic password strength estimator.
HTML
253
star
37

zxcvbn-ios

A realistic password strength estimator.
Objective-C
224
star
38

dbx_build_tools

Dropbox's Bazel rules and tools
Go
201
star
39

nautilus-dropbox

Dropbox Integration for Nautilus
Python
196
star
40

dropbox-sdk-obj-c

Official Objective-C SDK for the Dropbox API v2.
Objective-C
182
star
41

dropbox-sdk-go-unofficial

⚠️ An UNOFFICIAL Dropbox v2 API SDK for Go
Go
180
star
42

rust-alloc-no-stdlib

An interface to a generic allocator so a no_std rust library can allocate memory, with, or without stdlib being linked.
Rust
169
star
43

llm-security

Dropbox LLM Security research code and results
Python
164
star
44

pygerduty

A Python library for PagerDuty.
Python
164
star
45

kglb

KgLb - L4 Load Balancer
Go
139
star
46

mdwebhook

A sample app that uses webhooks to convert Markdown files to HTML.
Python
136
star
47

pytest-flakefinder

Runs tests multiple times to expose flakiness.
Python
133
star
48

ts-transform-import-path-rewrite

TS AST transformer to rewrite import path
TypeScript
129
star
49

datagraph

Haskell
127
star
50

miniutf

A C++ library for basic Unicode manipulation.
C
119
star
51

PhotoWatch

A demo app for the SwiftyDropbox SDK.
Swift
118
star
52

pilot

Cross-platform MVVM in Swift
Swift
113
star
53

librsync

Dropbox modified version of librysnc
C
109
star
54

XCoverage

Xcode Plugin that displays coverage data in the text editor
Objective-C
100
star
55

vsmc

Vendor Security Model Contract
96
star
56

merou

Permission management service
Python
95
star
57

othw

OAuth 2 the Hard Way - calling the Dropbox API in lots of languages without any Dropbox or OAuth libraries
JavaScript
86
star
58

hypershard-android

CLI tool for collecting tests
Kotlin
84
star
59

trapperkeeper

A suite of tools for ingesting and displaying SNMP traps.
Python
80
star
60

amqp-coffee

An AMQP 0.9.1 client for Node.js.
CoffeeScript
78
star
61

idle.ts

A TypeScript library used to detect idle/active users.
TypeScript
77
star
62

dropbox-sdk-rust

Dropbox SDK for Rust
Rust
74
star
63

lopper

A lightweight C++ framework for vectorizing image-processing code
C++
73
star
64

typed-css-modules-webpack-plugin

Generate TypeScript typing declarations for your TypeScript + CSS Modules project.
TypeScript
69
star
65

kaiken

User scoping library for Android applications.
Kotlin
69
star
66

dropbox-api-content-hasher

Code to compute the Dropbox API's "content_hash"
Java
68
star
67

differ

C
64
star
68

stopwatch

Scoped, nested, aggregated python timing library
Python
63
star
69

dbx-career-framework

Python
62
star
70

llama

Library for testing and measuring network loss and latency between distributed endpoints.
Go
61
star
71

nodegallerytutorial

Step by step tutorial to build a production-ready photo gallery Web Service using Node.JS and Dropbox.
JavaScript
61
star
72

load_management

This repository contains Go utilities for managing isolation and improving reliability of multi-tenant systems.
Go
53
star
73

rules_node

Node rules for Bazel (unsupported)
Python
52
star
74

rust-brotli-decompressor

An implementation of https://github.com/google/brotli in rust avoiding the stdlib
Rust
50
star
75

hermes

SRE Event and Autotasking system
Python
48
star
76

dropbox-api-v2-explorer

The Official API Explorer for Dropbox's APIs
TypeScript
45
star
77

pynsot

A Python client and CLI utility for the Network Source of Truth (NSoT) REST API.
Python
45
star
78

DropboxBusinessAdminTool

Power User tool to assist Dropbox Business Administrators in managing their Dropbox team
C#
44
star
79

ts-transform-react-constant-elements

A TypeScript AST Transformer that can speed up reconciliation and reduce garbage collection pressure by hoisting React elements to the highest possible scope.
TypeScript
44
star
80

llama-archive

Loss & LAtency MAtrix
Python
43
star
81

DropboxBusinessScripts

Scripting resources to serve as a base for common Dropbox Business tasks
Python
41
star
82

dropbox-ios-dropins-sdk

An iOS library for choosing files in Dropbox.
Objective-C
40
star
83

ttvc

Measure Visually Complete metrics in real time
TypeScript
40
star
84

encfs

EncFS Encrypted Filesystem
C++
38
star
85

dropbox-api-spec

The Official API Spec for Dropbox API V2 SDKs.
Python
37
star
86

onenote-parser

C++
34
star
87

image-search

A hypothetical Dropbox API app that makes it possible to do image searches from Dropbox.
Haskell
34
star
88

dbx-unittest2pytest

Convert unittest asserts to pytest rewritten asserts.
Python
26
star
89

dropbox-api-v2-repl

Utilities to test the Dropbox API v2.
Python
26
star
90

hypershard-ios

⚑ the ridiculously fast XCUITest collector.
Swift
25
star
91

hocrux

Handwritten optical character recognition
Python
25
star
92

questions

Simple application for storing interview questions.
Python
24
star
93

dropbox_hook

A tool for testing your Dropbox webhook endpoints.
Python
23
star
94

ruba

fast in-memory analytics datastore in Rust
Rust
21
star
95

libunwind

Pyston's fork of libunwind; originally from git://git.sv.gnu.org/libunwind.git
C
21
star
96

changes-client

A build client for Changes.
Go
19
star
97

libavcodec-hooks

Fork of ffmpeg (git://source.ffmpeg.org/ffmpeg.git). Required to compile avrecode lossless video compression (https://github.com/dropbox/avrecode). Adds hooks into low-level coding functions of libavcodec. License: LGPL.
C
18
star
98

phabricator-changes

Integration between Phabricator and Changes. This repository is no longer maintained.
PHP
18
star
99

Dropline

Tool to monitor how busy an area is using Wi-Fi. Originally intended for Dropbox's Tuck Shop.
Haskell
18
star
100

goprotoc

Go
17
star