Aug 29, 2019

Creating a custom DJI Drone Controller App: Part 1


Working with my current client has given me the incredible opportunity to program a drone path using the DJI SDK and iOS. I wanted to show a quick walkthrough of how to get setup to program your own flight paths. We’ll talk quickly about why you would want to do this, how it is done and finally show some examples.

Why Custom?

At first glance, using the DJI Go 4 app, it would appear that you have everything you could possibly need. Why would you need to create your own app when DJI provides you with one? One of the big reasons for this is that in the DJI Go 4 app you have to fly the mission on it’s own and mark the waypoints in the app. This is great if you want a repeatable mission at the same start and end points every time. For example, taking pictures and video of a field. But if you want something you can recreate no matter where you are, it can be very useful to create your own application and program your own flight paths. Let’s take a look at how to get started!


Getting started: Project setup

Install the DJISDK and DJIUXSDK useing cocoapods. If you’re not familiar with cocoapods please see instructions at

Run pod init to start using cocoapods with your project.

In your Podfile add the following lines:

pod 'DJI-SDK-iOS'
pod 'DJIWidget'

Your Podfile should look something like this:

target 'DJIWaypointMissionExample' do
    # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
    # Pods for DJIWaypointMissionExample
    pod 'DJI-SDK-iOS'
    pod 'DJI-UXSDK-iOS'
    pod 'DJIWidget'

In your terminal run pod install then open the generated .xcworkspace file.

In your Xcode project – Build phases: add FFmpeg to your link libraries.

Getting started: Register your app with DJI

To connect to your drone you will need to have a DJI developer account. Navigate to to get started.

Once you have your account. Navigate to and create a new app. Name it whatever you want but make sure the app identifier matches.

Copy the api key and paste it into your apps Info.plist as DJISDKAppKey

Getting started: Connecting to the Drone

In this example we’re using a Mavic Pro, but the connections will be the same for any DJI Drone

In ViewController.swift replace : UIViewController with : DUXDefaultLayoutViewController and add the following imports:

import DJISDK


import DJIWidget

Add the following function:

private func registerWithSDK() { 
    let appKey = Bundle.main.object(forInfoDictionaryKey: SDK_APP_KEY_INFO_PLIST_KEY) as? String
    guard appKey != nil && appKey!.isEmpty == false else {
        NSLog("Please enter your app key in the info.plist")
    DJISDKManager.registerApp(with: self)

This will start the registration process and the DJISDKManagerDelegate will be called after it has been completed. Add the following to your code:

extension ViewController: DJISDKManagerDelegate {
     func appRegisteredWithError(_ error: Error?) {
        guard error == nil else { return }
        if enableBridgeMode {
    func productConnected(_ product: DJIBaseProduct?) {
        guard product != nil else {
            print("error connecting to product")


To connect to the drone you will need a few things.

  1. The DJI SDK Bridge App SDK Bridge on the App Store
  2. The DJI Assistant (for simulations) DJI Mavic Pro – Specs, Tutorials & Guides – DJI


  1. Plug the remote control into your iPad or iPhone and turn the remote on.
  2. Open the DJII Bridge app and note the IP address listed
  3. Paste the IP address into bridgeAppIP
  4. Run the app!

If all has gone well you will see the camera output from your drone.

Let’s Code… finally

Now that the basic connections are setup let’s start creating an actual mission. This is accomplished through the DJIMissionControl object and we’ll build up a DJIWaypointMission object.

Create a function that looks like this:

func createWaypointMission() -> DJIWaypointMission {}

This is where we’ll do the bulk of the work, but first let’s come up with a simple example to show that we can actually control the drone.

Location Updates

Let’s start by registering for location updaates and getting the current location. We’ll use the location to set our take-off point in the mission.

In the view controller create a private var private var currentLocation: CLLocation? and the following function:

func startListeningForLocationUpdates(completion: @escaping (CLLocation) -> Void) {
    guard let key = DJIFlightControllerKey(param: DJIFlightControllerParamAircraftLocation),
        let keyManager = DJISDKManager.keyManager() else { return }
    keyManager.getValueFor(key) {
        value, _ in guard let location = value?.value as? CLLocation else { return } self.currentLocation = location completion(location)
    } keyManager.startListeningForChanges(on: key, withListener: self) { _, newValue in guard let location = newValue?.value as? CLLocation else { return } if self.currentLocation == nil { completion(location) } self.currentLocation = location }

Add the function call to viewDidLoad.

Now that we have our location we can start our custom mission. In your createWaypointMission function create a mutable mission and set a few basic fields.

func createWaypointMission() -> DJIWaypointMission {
    let mission = DJIMutableWaypointMission() mission.headingMode = .usingWaypointHeading mission.autoFlightSpeed = 1
    // Set to your preference mission.maxFlightSpeed = 2
    // Set to your preference mission.finishedAction = .noAction mission.flightPathMode = .normal return mission

For our first mission we’re just going to take off and move to 10 meters off the ground from our current location. This will prove that everything works and give us enough to staart building out more complicated missions in the next tutorial.

First, make sure we have a location.

guard let currentLocation = currentLocation else {
    return DJIWaypointMission()

Next add a new waypoint to the mission:

let waypoint = DJIWaypoint(coordinate: currentLocation.coordinate) waypoint.altitude = 10 mission.add(waypoint)

Your final createWaypointMission should now look like this:

func createWaypointMission() -> DJIWaypointMission {
    guard let currentLocation = currentLocation else {
        return DJIWaypointMission()
    let mission = DJIMutableWaypointMission() mission.headingMode = .usingWaypointHeading mission.autoFlightSpeed = 1
    // Set to your preference mission.maxFlightSpeed = 2
    // Set to your preference mission.finishedAction = .noAction mission.flightPathMode = .normal let waypoint = DJIWaypoint(coordinate: currentLocation.coordinate) waypoint.altitude = 10 mission.add(waypoint)
    return mission

Now all that is left is to upload the mission to the drone!

This part is a little overly complicated because of all the callbacks involved, but basically it works like this:

  1. Load the mission into mission control
  2. Start uploading the mission to the drone
  3. Listen for upload updataes
  4. Once everything is uploaded, start the mission
func uploadAndStartMission(mission: DJIWaypointMission) {
    guard let missionControl = DJISDKManager.missionControl() else {
    missionControl.waypointMissionOperator().uploadMission {
        [weak self] _ in missionControl.waypointMissionOperator().addListener(toStarted: self, with: nil, andBlock: {})
    } missionControl.waypointMissionOperator().addListener(toUploadEvent: self, with: nil) {
        event in guard let progress = event.progress else {
        print("progress: \(progress)")
        if progress.totalWaypointCount - 1 == progress.uploadedWaypointIndex, progress.isWaypointSummaryUploaded {
            DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
                missionControl.waypointMissionOperator().startMission(completion: nil)

Now we hook everything up!

In Main.storyboard add a button and hook it up to your view controller. Something like this:

@IBAction func startButtonPressed(_ sender: UIButton) { }

Now add the following:

startListeningForLocationUpdates {
    location in self.currentLocation = location self.uploadAndStartMission(mission: self.createWaypointMission())

@IBAction func startButtonPressed(_: UIButton) {
    startListeningForLocationUpdates {
        location in self.currentLocation = location self.uploadAndStartMission(mission: self.createWaypointMission())

Now when you press the start button the drone will take off and ascend to 10 meters.

In the next part of this series we’ll take a look at a creating a more complex mission, some other features of the SDK, and a few lessons learned to help you avoid mistakes that I’ve had to learn from. See you next time!

About the Author

Elvin Bearden profile.

Elvin Bearden

Sr. Consultant

Elvin has experience in Swift, Objective-C, Java, PHP and restful API’s. He has spent 5 years developing native iOS applications in a variety of fields. When not working on iOS, Elvin enjoys playing guitar, listening to music, playing video games and reading.

One thought on “Creating a custom DJI Drone Controller App: Part 1

  1. Mohamad Ghaith Alzin says:

    Please note that I am using Mavic Pro 2 Drone and the mission is not working at all!?
    Could you share that code on Github or send it to my email and I will make sure to test it again
    Thank you very much!

  2. Emmanuel says:

    Hi, Your example doesn’t work at all. DUXDefaultLayoutViewController is not declared

    1. Emmanuel,thanks for catching that and my apologies. You need in to import the following:
      import DJISDK
      import DJIUXSDK
      import DJIWidget

      I’ve updated the post as well.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Blog Posts
Snowflake CI/CD using Jenkins and Schemachange
CI/CD and Management of Data Warehouses can be a serious challenge. In this blog you will learn how to setup CI/CD for Snowflake using Schemachange, Github, and Jenkins. For access to the code check out […]
How to get your pull requests approved more quickly
TL;DR The fewer reviews necessary, the quicker your PR gets approved. Code reviews serve an essential function on any software codebase. Done right, they help ensure correctness, reliability, and maintainability of code. On many teams, […]
Kafka & Kubernetes: Scaling Consumers
Kafka and Kubernetes (K8s) are a great match. Kafka has knobs to optimize throughput and Kubernetes scales to multiply that throughput. On the consumer side, there are a few ways to improve scalability. Resource & […]
AWS RDS MYSQL Playground
Do you need a reliable database platform to hammer out some new application ideas? Or, maybe you’d like to learn MYSQL in a disposable environment? This Hashicorp Terraform MYSQL RDS Module will build all the […]