IOS-DFU-Library: firmware updates to a Bluetooth device from an iPhone
OTA DFU Library for Mac and iOS, compatible with nRF5x SoCs
At a glance
- What is it?
- Nordic's iOS DFU library is the piece that lets a phone push new firmware to a sensor, a beacon, or a development kit over Bluetooth low energy. The interesting part of the documentation is not the API but the platform quirk at the centre of it: iOS caches the services of a paired device, and the only way to make it forget them is a characteristic that has to exist, be handled correctly, and be signalled at exactly the right moment.
- Who is it for?
- The iOS DFU library fits a team shipping a Bluetooth product based on the nRF series and needing an update path that does not require a physical connection, and it fits a developer working with a development kit who wants to flash firmware from a phone. It does not fit a product that has already shipped without the service changed characteristic, because the platform will not forget the old services.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 34 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the library does, and the three steps around it
The companion application in the repository does one thing, and the readme describes it in three steps: select a zip file with the appropriate firmware, select a Bluetooth low energy device with an open bootloader, and start the firmware upload. A settings screen lets you change the parameters the library uses while uploading.
That is a small application, and it is worth noticing that it is in the same repository as the library. The reason is that the library is the part that is hard, and a reference application that exercises it is the way a developer finds out how to use it.
The library itself is described as a tested library for the iOS 8 and later platform, for performing a device firmware update on an nRF5x device using an iPhone or an iPad, and it is designed to make it very easy to include these devices in an application. The compatibility claim is that it works with all bootloader and firmware update versions, which is a strong claim for a library that has to speak two different protocols and deal with the platform's service cache.
The hardware context explains why this library exists at all. The nRF5x chips are flash based system on chips, which the readme calls the most flexible solution available, and a key feature of that family and of the associated software architecture is the possibility of an over-the-air device firmware upgrade. That lets an original equipment manufacturer fix bugs and add features to products already in the field, through the cloud, and the readme names the security and flexibility that this brings to product development.
So the use case is a product in the field, and the deliverable is a library that an application can call so that a user can update their device without a cable. The repository layout matches that: there is a library directory, a test application, a tests directory, a demonstration application, a documentation directory, a change log, and the packaging files for the three distribution routes.
The service changed characteristic, the whole story in one subsection
There is a section of the readme that is more detailed than a typical library's documentation, and it is about a single characteristic, and it is the most valuable thing in the file.
The requirement is stated as a must. For the update to work on iOS, the target device must have the service changed characteristic with the indicate property in the generic attribute service. Without that characteristic, iOS assumes that the services of the device will never change, and will not invalidate them after the device switches into bootloader mode.
That is the whole failure mode in two sentences. The phone has cached the service list of the paired device. The device reboots into a bootloader that presents a completely different service list. The phone keeps using the cached one. And the update appears to hang, or to connect to services that no longer exist.
The behaviour of the characteristic is then described in two cases. On a paired device, a change of the attribute table has to be signalled with an indication to the characteristic, and iOS automatically enables the notification and handles the indication by performing a service discovery. On a device that is not trusted, because it is not paired, iOS clears the service cache every time the device disconnects, so the problem does not arise.
So the whole problem exists only for bonded devices, and the fix is a signal from the device at the right moment. Every design decision in the rest of the section follows from that.
For the legacy protocol, the indication is handled correctly from the eighth generation of the software development kit, so the library is compatible with that behaviour. For the secure protocol from the twelfth generation, the implementation does not support bonding: the experimental buttonless sample does not pass the bond information when switching to bootloader mode, and the bootloader does not send the indication. The workaround is unusual enough to be worth reading twice. The bootloader starts advertising with a MAC address incremented by one, so from the phone's point of view it is a completely new device, and a fresh service discovery is done.
And the readme then tells you to think about the consequences, because this is a hack with edges. If the new firmware changes the list of services, you may want to increment the address again. Adding one to a public address is not possible unless you register a new one, and devices may ship with addresses such that two of them end up identical, so the feature has to be used carefully.
The fourteenth generation of the kit is where the design was fixed properly. Buttonless update with shared bonds was added, a bonded relationship is required to use it, the address does not change in bootloader mode, and instead the bootloader sends the service changed indication when it enters bootloader mode and when it returns to application mode. For bonded devices the readme recommends using that service.
So the history is a story of the platform fighting back, and the library absorbing the fight over several major versions.
The protocol history, and what the library inherits
The change log section of the readme is really a protocol history, and reading it tells you which behaviours your code has to account for.
The legacy protocol begins at the fourth generation of the software development kit, with the first version of the update over Bluetooth Smart, supporting an application update. The sixth generation adds bootloader support, so the bootloader itself can be updated, and because the updated bootloader may depend on the new soft device, the two may be sent and installed together. Buttonless update for non-bonded devices arrives at that point too.
The seventh generation makes the extended initial packet required, and the readme lists what it contains: the device type and revision, the application version, the compatible soft devices, and the firmware checksum. That is a validation payload, and its presence means the update can be checked before it is applied rather than after.
The eighth generation allows the bond information to be preserved after an application update, with the new application sending the service change indication to the phone when it first starts, which is the mechanism described earlier, and it brings buttonless update for bonded devices and the sharing of the long term key between the application and the bootloader.
The secure protocol arrives at the twelfth generation, and here the compatibility guarantee is the important line: the library is fully backwards compatible, so it supports both the new and the legacy protocol. The thirteenth changes the buttonless implementation to use different identifiers, drops bond sharing, and has the bootloader use the address plus one. The fourteenth makes buttonless no longer experimental, adds a new buttonless characteristic for bonded devices, and keeps the address unchanged.
So the shape of the library is: one interface, two protocol implementations underneath, and a set of platform quirks that have been worked around in three different ways over the years.
The requirements section is short and confirms the scope. The library is compatible with the nRF51 and nRF52 devices that have the S-Series soft device and the update bootloader flashed on. So it is not a general Bluetooth firmware update library; it is for one chip family with one bootloader, and the value is in the depth of that specialisation rather than in the breadth.
Three ways to install it
The installation section gives three routes, one for each of the package managers an Apple developer might reach for, and each one is documented in full.
The first is the package manager for Apple platforms, where you add a pod for the library to your project file, with the frameworks flag turned on, then install the dependencies, open the resulting workspace, and import the library into your classes with a single import statement.
target 'YourAppTargetName' do
use_frameworks!
pod 'NordicDFU'
endThe second is the older dependency manager, where you create a dependency file with a GitHub entry and a version constraint, and the comment tells you to replace the placeholder with the version you want.
github "nordicsemi/IOS-DFU-Library" ~> x.y // Replace x.y with your required versionThe build step for that route has a flag for the framework format and a platform list, and the readme explains what comes out: two framework files that you copy into your project, and the symbol files, which you may want to keep bundled for future use if you need to resymbolicate crash logs. That last instruction is the kind of practical note that saves an afternoon when a crash log arrives with unusable addresses.
The third is the Swift package manager, and the manifest is given in full with the tools version, the package name, the dependency with the version range, and the target that depends on it.
// swift-tools-version:5.6
import PackageDescription
let package = Package(
name: "<Your Product Name>",
dependencies: [
.package(
url: "https://github.com/nordicsemi/IOS-DFU-Library",
.upToNextMajor(from: "<Desired Version>")
)
],
targets: [.target(name: "<Your Target Name>", dependencies: ["NordicDFU"])]
)Three routes, three different eras of the ecosystem, and a library that is still distributed through all of them. The last one is the one the readme recommends for new work, and the first two are there for the projects that already use them.
The library is published under the CocoaPods name and carries a compatibility badge for the older manager as well, and it is distributed under a three clause BSD licence.
The repository, and the two interfaces
The top level of the repository shows how the project is organised, and one entry stands out.
There is a library directory, a test application, a tests directory, a demonstration application, a documentation directory, and a change log. There is a pod specification, a project file, a package manifest, a dependency file, and a test plan file for the native test target. And there is a separate readme for the Objective-C interface, next to the main one.
That second readme is the interesting thing. A Swift library for Apple platforms has historically been reachable from Objective-C as well, and shipping a separate guide for the older interface says the library still supports both audiences, with the Swift guide as the primary one and the Objective-C guide as a supported path rather than a historical note.
The documentation directory is worth a mention too, because the readme points at a hosted documentation site for the library, and a directory in the repository means that site is generated from the same tree, so the reference and the source cannot disagree.
The two application directories are a testing device for the library. A library that talks to a Bluetooth device over the air, with a bootloader that has to be entered first and a service cache that has to be invalidated, is exactly the kind of code where the interesting bugs are in the parts that only show up on real hardware. A test application gives you something to point a phone at, and a demonstration application gives you a working example of the whole flow.
The continuous integration directory and the change log at the root are the remaining pieces of a project that is still being worked on: the last push was in September 2026 and the newest release, the fourth in the seventeen series, was in August of that year, with the previous two in 2024. So the library is mature in its protocol coverage and still receiving fixes, and the gap between the release dates is a sign of a stable library rather than an abandoned one.
Editorial conclusion
The iOS DFU library fits a team shipping a Bluetooth product based on the nRF series and needing an update path that does not require a physical connection, and it fits a developer working with a development kit who wants to flash firmware from a phone. It does not fit a product that has already shipped without the service changed characteristic, because the platform will not forget the old services. Verify first that your device has that characteristic, that the security features of the device match the ones your implementation assumes, since the documentation says the library is compatible with all bootloader versions but the behaviour around bonding has changed more than once, and that you read the history section for the version your device shipped with rather than the newest one.
Frequently asked questions
What does the Nordic iOS DFU library do?
It performs an over-the-air device firmware update on an nRF5x device from an iPhone or an iPad, and is designed to be easy to include in an application. The library claims compatibility with all bootloader and update versions, and it is distributed for the nRF51 and nRF52 families with the S-Series soft device and the update bootloader.
What is the service changed characteristic and why does it matter?
It is a characteristic in the generic attribute service that the target device must have, with the indicate property. Without it iOS assumes the device's services will never change, caches them when the device is paired, and will not invalidate them after the device switches into bootloader mode, so the update cannot complete.
How did the buttonless update behaviour change across the software development kit versions?
The twelfth generation's secure implementation does not support bonding, and the workaround was for the bootloader to advertise with the MAC address incremented by one, so the phone treats it as a new device. The thirteenth kept the address plus one with different identifiers and no bond sharing. The fourteenth made buttonless no longer experimental, added a characteristic for bonded devices, kept the address unchanged, and has the bootloader signal the change when it enters and leaves bootloader mode.
How do I install the Nordic iOS DFU library?
Through the CocoaPods-style package manager by adding a pod for the library to your project file and enabling the frameworks flag, through the older dependency manager by adding a GitHub entry with a version constraint, or through the Swift package manager with a manifest naming the dependency and a target that depends on it. The readme recommends the Swift package manager for new work.
Does the Nordic iOS DFU library support both update protocols?
Yes. The secure protocol arrived in the twelfth generation of the kit, and the readme states that the library is fully backwards compatible, so it supports both the new and the legacy protocol. The compatibility is in the library interface; the underlying quirks of the two protocols differ.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nordicsemi-ios-dfu-library)Was this useful?