Five years ago, we wrote a post explaining how to use our library into a Swift project. Unfortunately, during these five years, Apple has made changes to this programming language and what was true at the time is not necessarily so anymore. Therefore, it's time to update this post.
Since 2015, Swift went from version 1.2 to version 5.2. During this time, several changes broke backward compatibility. That is, Swift code which worked in a previous version suddenly became incorrect.
In the same way, integrating Objective-C code in a Swift project also changed and made our previous post obsolete. Indeed, if you use an old version of our library with a recent version of Swift, the application doesn't compile.
Fortunately, we just published a new version of the Objective-C library which integrates more seamlessly with Swift 5.x.
What changed?
Among Swift changes, two major modifications raised issues with the previous versions of our library.
First, the compiler became much stricter with the use of "optional" variables, that is variables which can have either a value or point to nil. By default, Swift 5 thinks that all the Objective-C functions return an optional variable and forces the developer to unwrap the value. We modified our Objective-C library to explicitly declare if the functions return an optional object or not. From now on, when you use our library with Swift, the types are correctly inferred.
Second, when importing Objective-C files, Xcode renames the functions in order to follow the Swift naming conventions. The idea of this feature is to shorten and make more readable iOS and macOS APIs written in Objective-C, which were known for their length.
This renaming raised large issues with our Objective-C library. Not only the documentation of our library didn't correspond anymore with the code, but some functions were simply ignored or were incorrectly converted. For example, the nextFunction(), nextModule, and nextSensor methods were all renamed into next(), which created a conflict when compiling. Fortunately, Apple recently added the possibility to specify in the Objective-C code the name that the function must have in Swift. Thanks to this, the methods now have the same name in Swift and in Objective-C.
New version of the Objective-C library
In order to solve the issues that we just discussed, we published a new version of the Objective-C library: version 1.10.41779.
For applications written in Objective-C, our new library is 100% backward compatible. If the code of your application is written in Objective-C, you can update our Yoctopuce library without having to modify your code.
If you use Swift, it's different. You will most probably need to modify some parts of your code. But most of the time, the code becomes simpler because you don't need anymore to test whether the values returned by the API are null or not.
How to include the Objective-C library into a Swift project?
This part didn't change, you must still add the .m and .h files as well as the yapi subdirectory to the Xcode project and create a "bridging header". The easiest way to do so is to drag-and-drop all the files of the Sources directory of the Objective-C library directly into the Xcode project navigator. Xcode launches a wizard which adds these files to the project and creates a "bridging header" which is used by the Swift files.
Select 'add to targets' when Xcode import files, otherwise your files will not be compiled
A few comments:
- Don't forget to select your application in the "add to target" field of this wizard, otherwise Xcode adds the files to the project but doesn't compile them.
- Select "Yes" when Xcode offers to create the "Bridging header", otherwise the files are compiled but are unusable in your Swift file.
- Select all the files of the Sources directory and not the Sources directory itself, otherwise Xcode doesn't offer to create the "Bridging header".
The second step is to include the headers of the required Objective-C files into the bridging header. For example, if you want to use a Yocto-Meteo in a Swift program, you must include the yocto_api.h, yocto_humidity.h, yocto_temperature.h, and yocto_pressure.h files.
// Use this file to import your target's public headers that
// you would like to expose to Swift.
//
#import "yocto_api.h"
#import "yocto_humidity.h"
#import "yocto_temperature.h"
#import "yocto_pressure.h"
If everything went well, Xcode compiles without error all the Swift files, the Objective-C files, and a few C files.
Using the Yoctopuce library in Swift
Now that all the Objective-C files are compiled, we must write the Swift code which uses the Yoctopuce library. We are going to translate the "Doc-Inventory" example into Swift. Explanations on what this example does are provided in the documentation of our Objective-C library and in the documentation of all the Yoctopuce modules.
var error: NSError?
// Sets up the API to use local USB devices
var res = YAPI.RegisterHub("usb", &error)
if res.rawValue != YAPI_SUCCESS.rawValue {
print("Error: \(error!.localizedDescription)")
exit(0)
}
print("Device list:")
var optional_module = YModule.FirstModule()
while let module = optional_module {
let serial = module.get_serialNumber()
let productName = module.get_productName()
print("\(serial) \(productName)")
optional_module = module.nextModule()
}
YAPI.FreeAPI()
As you can see, the methods have the same name and the same parameters as in Objective-C. The syntax is now coherent with the documentation of our API.
You can also see that only the variables that need it are defined as "optional". For example, the error and optional_module variables can indeed point to nil and you must therefore unwrap them with the ! or the ? character. On the opposite, you can directly use the values returned by get_serialNumber() or get_productName() because they are always valid.
The code of the example
This example is available in the archive of our Objective-C library. It is located in the Examples/Prog-UseWithSwift52 subdirectory. You can download the library from our web site or from GitHub.