Skip to content

Commit 6980968

Browse files
authored
Merge pull request #486 from NordicSemiconductor/feature/handling-fw-errors
Breaking: Creating `DFUFirmware` may now throw an error
2 parents fbec1cb + 11bd15b commit 6980968

File tree

6 files changed

+131
-123
lines changed

6 files changed

+131
-123
lines changed

Example/iOSDFULibrary/DFU Test Performer/DFUTestSet.swift

+4-10
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import CoreBluetooth
3434

3535
enum DFUTestError : Error {
3636
case fileNotFound
37-
case invalidFirmware
3837
}
3938

4039
typealias AdvertisingData = [String : Any]
@@ -82,10 +81,9 @@ class FilterBy {
8281

8382
class Option {
8483

85-
static let prn : (UInt16) -> ServiceModifier = {
86-
aPRNValue in
84+
static let prn : (UInt16) -> ServiceModifier = { prn in
8785
return {
88-
initiator in initiator.packetReceiptNotificationParameter = aPRNValue
86+
initiator in initiator.packetReceiptNotificationParameter = prn
8987
}
9088
}
9189

@@ -97,14 +95,10 @@ class Option {
9795
extension DFUFirmware {
9896

9997
private static func from(urlToZipFile url: URL?, type: DFUFirmwareType) throws -> DFUFirmware {
100-
guard url != nil else {
98+
guard let url = url else {
10199
throw DFUTestError.fileNotFound
102100
}
103-
let firmware = DFUFirmware(urlToZipFile: url!, type: type)
104-
guard firmware != nil else {
105-
throw DFUTestError.invalidFirmware
106-
}
107-
return firmware!
101+
return try DFUFirmware(urlToZipFile: url, type: type)
108102
}
109103

110104
static func from(zip name: String, locatedIn subdirectory: String) throws -> DFUFirmware {

iOSDFULibrary/Classes/Implementation/Firmware/DFUFirmware.swift

+56-42
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,26 @@ import Foundation
5454
case softdeviceBootloaderApplication = 7
5555
}
5656

57+
/**
58+
An error thrown when instantiating a `DFUFirmware` type from an invalid file.
59+
*/
60+
public struct DFUFirmwareError : Error {
61+
public enum FileType {
62+
case zip
63+
case binOrHex
64+
case dat
65+
}
66+
public let type: FileType
67+
}
68+
69+
extension DFUFirmwareError : LocalizedError {
70+
71+
public var errorDescription: String? {
72+
return NSLocalizedString("The \(type) file is invalid", comment: "")
73+
}
74+
75+
}
76+
5777
/// The DFUFirmware object wraps the firmware file.
5878
@objc public class DFUFirmware : NSObject, DFUStream {
5979
internal let stream: DFUStream
@@ -101,10 +121,12 @@ import Foundation
101121
- parameter urlToZipFile: URL to the Distribution packet (ZIP).
102122

103123
- returns: The DFU firmware object or `nil` in case of an error.
124+
- throws: `DFUFirmwareError` if the file is invalid, or
125+
`DFUStreamZipError` if creating a Zip stream failed.
104126
*/
105-
@objc convenience public init?(urlToZipFile: URL) {
106-
self.init(urlToZipFile: urlToZipFile,
107-
type: DFUFirmwareType.softdeviceBootloaderApplication)
127+
@objc convenience public init(urlToZipFile: URL) throws {
128+
try self.init(urlToZipFile: urlToZipFile,
129+
type: DFUFirmwareType.softdeviceBootloaderApplication)
108130
}
109131

110132
/**
@@ -117,24 +139,20 @@ import Foundation
117139
- parameter type: The type of the firmware to use.
118140

119141
- returns: The DFU firmware object or `nil` in case of an error.
142+
- throws: `DFUFirmwareError` if the file is invalid, or
143+
`DFUStreamZipError` if creating a Zip stream failed.
120144
*/
121-
@objc public init?(urlToZipFile: URL, type: DFUFirmwareType) {
145+
@objc public init(urlToZipFile: URL, type: DFUFirmwareType) throws {
122146
fileUrl = urlToZipFile
123147
fileName = urlToZipFile.lastPathComponent
124148

125149
// Quickly check if it's a ZIP file
126150
let ext = urlToZipFile.pathExtension
127151
if ext.caseInsensitiveCompare("zip") != .orderedSame {
128-
NSLog("\(fileName!) is not a ZIP file")
129-
return nil
152+
throw DFUFirmwareError(type: .zip)
130153
}
131154

132-
do {
133-
stream = try DFUStreamZip(urlToZipFile: urlToZipFile, type: type)
134-
} catch let error as NSError {
135-
NSLog("Error while creating ZIP stream: \(error.localizedDescription)")
136-
return nil
137-
}
155+
stream = try DFUStreamZip(urlToZipFile: urlToZipFile, type: type)
138156
super.init()
139157
}
140158

@@ -147,9 +165,13 @@ import Foundation
147165
- parameter zipFile: The Distribution packet (ZIP) data.
148166

149167
- returns: The DFU firmware object or `nil` in case of an error.
168+
- throws: `DFUFirmwareError` if the file is invalid,
169+
`DFUStreamZipError` if creating a Zip stream failed,
170+
or an error in the Cocoa domain, if the data cannot be written
171+
to a temporary location.
150172
*/
151-
@objc convenience public init?(zipFile: Data) {
152-
self.init(zipFile: zipFile, type: DFUFirmwareType.softdeviceBootloaderApplication)
173+
@objc convenience public init(zipFile: Data) throws {
174+
try self.init(zipFile: zipFile, type: DFUFirmwareType.softdeviceBootloaderApplication)
153175
}
154176

155177
/**
@@ -162,17 +184,15 @@ import Foundation
162184
- parameter type: The type of the firmware to use.
163185

164186
- returns: The DFU firmware object or `nil` in case of an error.
187+
- throws: `DFUFirmwareError` if the file is invalid,
188+
`DFUStreamZipError` if creating a Zip stream failed,
189+
or an error in the Cocoa domain, if the data cannot be written
190+
to a temporary location.
165191
*/
166-
@objc public init?(zipFile: Data, type: DFUFirmwareType) {
192+
@objc public init(zipFile: Data, type: DFUFirmwareType) throws {
167193
fileUrl = nil
168194
fileName = nil
169-
170-
do {
171-
stream = try DFUStreamZip(zipFile: zipFile, type: type)
172-
} catch let error as NSError {
173-
NSLog("Error while creating ZIP stream: \(error.localizedDescription)")
174-
return nil
175-
}
195+
stream = try DFUStreamZip(zipFile: zipFile, type: type)
176196
super.init()
177197
}
178198

@@ -186,8 +206,11 @@ import Foundation
186206
- parameter type: The type of the firmware.
187207

188208
- returns: The DFU firmware object or `nil` in case of an error.
209+
- throws: `DFUFirmwareError` if the file is invalid,
210+
`DFUStreamHexError` if the hex file is invalid,
211+
or an error in the Cocoa domain, if `url` cannot be read.
189212
*/
190-
@objc public init?(urlToBinOrHexFile: URL, urlToDatFile: URL?, type: DFUFirmwareType) {
213+
@objc public init(urlToBinOrHexFile: URL, urlToDatFile: URL?, type: DFUFirmwareType) throws {
191214
fileUrl = urlToBinOrHexFile
192215
fileName = urlToBinOrHexFile.lastPathComponent
193216

@@ -196,27 +219,22 @@ import Foundation
196219
let bin = ext.caseInsensitiveCompare("bin") == .orderedSame
197220
let hex = ext.caseInsensitiveCompare("hex") == .orderedSame
198221
guard bin || hex else {
199-
NSLog("\(fileName!) is not a BIN or HEX file")
200-
return nil
222+
throw DFUFirmwareError(type: .binOrHex)
201223
}
202224

203225
if let datUrl = urlToDatFile {
204226
let datExt = datUrl.pathExtension
205227
guard datExt.caseInsensitiveCompare("dat") == .orderedSame else {
206-
NSLog("\(fileName!) is not a DAT file")
207-
return nil
228+
throw DFUFirmwareError(type: .dat)
208229
}
209230
}
210231

211232
if bin {
212-
stream = DFUStreamBin(urlToBinFile: urlToBinOrHexFile,
213-
urlToDatFile: urlToDatFile, type: type)
233+
stream = try DFUStreamBin(urlToBinFile: urlToBinOrHexFile,
234+
urlToDatFile: urlToDatFile, type: type)
214235
} else {
215-
guard let s = DFUStreamHex(urlToHexFile: urlToBinOrHexFile,
216-
urlToDatFile: urlToDatFile, type: type) else {
217-
return nil
218-
}
219-
stream = s
236+
stream = try DFUStreamHex(urlToHexFile: urlToBinOrHexFile,
237+
urlToDatFile: urlToDatFile, type: type)
220238
}
221239
super.init()
222240
}
@@ -232,10 +250,9 @@ import Foundation
232250

233251
- returns: The DFU firmware object or `nil` in case of an error.
234252
*/
235-
@objc public init?(binFile: Data, datFile: Data?, type: DFUFirmwareType) {
253+
@objc public init(binFile: Data, datFile: Data?, type: DFUFirmwareType) {
236254
fileUrl = nil
237255
fileName = nil
238-
239256
stream = DFUStreamBin(binFile: binFile, datFile: datFile, type: type)
240257
super.init()
241258
}
@@ -250,15 +267,12 @@ import Foundation
250267
- parameter type: The type of the firmware.
251268

252269
- returns: The DFU firmware object or `nil` in case of an error.
270+
- throws: `DFUStreamHexError` if the hex file is invalid.
253271
*/
254-
@objc public init?(hexFile: Data, datFile: Data?, type: DFUFirmwareType) {
272+
@objc public init(hexFile: Data, datFile: Data?, type: DFUFirmwareType) throws {
255273
fileUrl = nil
256274
fileName = nil
257-
258-
guard let s = DFUStreamHex(hexFile: hexFile, datFile: datFile, type: type) else {
259-
return nil
260-
}
261-
stream = s
275+
stream = try DFUStreamHex(hexFile: hexFile, datFile: datFile, type: type)
262276
super.init()
263277
}
264278

iOSDFULibrary/Classes/Utilities/Streams/DFUStreamBin.swift

+16-3
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,30 @@ internal class DFUStreamBin : DFUStream {
5858
return size
5959
}
6060

61-
init(urlToBinFile: URL, urlToDatFile: URL?, type: DFUFirmwareType) {
62-
binaries = try! Data(contentsOf: urlToBinFile)
61+
/// Creates the stream that will allow sending the binary file.
62+
///
63+
/// - parameters:
64+
/// - urlToHexFile: URL to the bin file.
65+
/// - urlToDatFile: Optional URL to the dat file. Dat file is required for nRF5 SDK 7.1+.
66+
/// - type: The firmware type.
67+
/// - throws: An error in the Cocoa domain, if `url` cannot be read.
68+
init(urlToBinFile: URL, urlToDatFile: URL?, type: DFUFirmwareType) throws {
69+
binaries = try Data(contentsOf: urlToBinFile)
6370
firmwareSize = UInt32(binaries.count)
6471

6572
if let dat = urlToDatFile {
66-
initPacketBinaries = try? Data(contentsOf: dat)
73+
initPacketBinaries = try Data(contentsOf: dat)
6774
}
6875

6976
currentPartType = type.rawValue
7077
}
7178

79+
/// Creates the stream that will allow sending the binary file.
80+
///
81+
/// - parameters:
82+
/// - binFile: The content of the bin file.
83+
/// - datFile: A content of an optional dat file. Dat file is required for nRF5 SDK 7.1+.
84+
/// - type: The firmware type.
7285
init(binFile: Data, datFile: Data?, type: DFUFirmwareType) {
7386
binaries = binFile
7487
firmwareSize = UInt32(binaries.count)

iOSDFULibrary/Classes/Utilities/Streams/DFUStreamHex.swift

+26-7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030

3131
import Foundation
3232

33+
public enum DFUStreamHexError : Error {
34+
case invalidHexFile
35+
}
36+
3337
internal class DFUStreamHex : DFUStream {
3438
private(set) var currentPart = 1
3539
private(set) var parts = 1
@@ -58,24 +62,39 @@ internal class DFUStreamHex : DFUStream {
5862
return size
5963
}
6064

61-
init?(urlToHexFile: URL, urlToDatFile: URL?, type: DFUFirmwareType) {
62-
let hexData = try! Data(contentsOf: urlToHexFile)
65+
/// Creates the stream that will allow sending the binary content of a hex file.
66+
///
67+
/// - parameters:
68+
/// - urlToHexFile: URL to the hex file.
69+
/// - urlToDatFile: Optional URL to the dat file. Dat file is required for nRF5 SDK 7.1+.
70+
/// - type: The firmware type.
71+
/// - throws: `DFUStreamHexError` if the hex file is invalid,
72+
/// or an error in the Cocoa domain, if `url` cannot be read.
73+
init(urlToHexFile: URL, urlToDatFile: URL?, type: DFUFirmwareType) throws {
74+
let hexData = try Data(contentsOf: urlToHexFile)
6375
guard let bin = IntelHex2BinConverter.convert(hexData, mbrSize: 0x1000) else {
64-
return nil
76+
throw DFUStreamHexError.invalidHexFile
6577
}
6678
binaries = bin
6779
firmwareSize = UInt32(binaries.count)
6880

6981
if let dat = urlToDatFile {
70-
initPacketBinaries = try? Data(contentsOf: dat)
82+
initPacketBinaries = try Data(contentsOf: dat)
7183
}
7284

7385
currentPartType = type.rawValue
7486
}
75-
76-
init?(hexFile: Data, datFile: Data?, type: DFUFirmwareType) {
87+
88+
/// Creates the stream that will allow sending the binary content of a hex file.
89+
///
90+
/// - parameters:
91+
/// - hexFile: The content of the hex file.
92+
/// - datFile: A content of an optional dat file. Dat file is required for nRF5 SDK 7.1+.
93+
/// - type: The firmware type.
94+
/// - throws: `DFUStreamHexError` if the hex file is invalid.
95+
init(hexFile: Data, datFile: Data?, type: DFUFirmwareType) throws {
7796
guard let bin = IntelHex2BinConverter.convert(hexFile, mbrSize: 0x1000) else {
78-
return nil
97+
throw DFUStreamHexError.invalidHexFile
7998
}
8099
binaries = bin
81100
firmwareSize = UInt32(binaries.count)

iOSDFULibrary/Classes/Utilities/Streams/DFUStreamZip.swift

+15-8
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,22 @@
3131
import Foundation
3232

3333
// Errors
34-
internal enum DFUStreamZipError : Error {
34+
public enum DFUStreamZipError : Error {
35+
/// The ZIP file contains no manifest.json file.
3536
case noManifest
37+
/// The manifest file is invalid.
3638
case invalidManifest
39+
/// The file has not been found.
3740
case fileNotFound
41+
/// Specified type is not present in the ZIP file.
3842
case typeNotFound
3943
}
4044

4145
extension DFUStreamZipError: LocalizedError {
4246

43-
var localizedDescription: String {
47+
public var errorDescription: String? {
4448
switch self {
45-
case .noManifest: return NSLocalizedString("No manifest file found", comment: "")
49+
case .noManifest: return NSLocalizedString("No manifest.json file found", comment: "")
4650
case .invalidManifest: return NSLocalizedString("Invalid manifest.json file", comment: "")
4751
case .fileNotFound: return NSLocalizedString("File specified in manifest.json not found in ZIP", comment: "")
4852
case .typeNotFound: return NSLocalizedString("Specified type not found in manifest.json", comment: "")
@@ -112,12 +116,14 @@ internal class DFUStreamZip : DFUStream {
112116
- parameter type: The type of the firmware to use.
113117

114118
- throws: `DFUStreamZipError` when manifest file was not found or contained
115-
an error.
119+
an error, or a ZIP error if unzipping the file failed.
116120

117121
- returns: The stream.
118122
*/
119123
convenience init(zipFile: Data, type: DFUFirmwareType) throws {
120-
let url = try ZipArchive.createTemporaryFile(zipFile)
124+
guard let url = ZipArchive.createTemporaryFile(zipFile) else {
125+
throw CocoaError(.fileWriteUnknown)
126+
}
121127
try self.init(urlToZipFile: url, type: type)
122128
}
123129

@@ -129,7 +135,7 @@ internal class DFUStreamZip : DFUStream {
129135
- parameter type: The type of the firmware to use.
130136

131137
- throws: `DFUStreamZipError` when manifest file was not found or contained
132-
an error.
138+
an error, or a ZIP error if unzipping the file failed.
133139

134140
- returns: The stream.
135141
*/
@@ -141,9 +147,10 @@ internal class DFUStreamZip : DFUStream {
141147
let manifestUrl = ZipArchive.findFile(DFUStreamZip.MANIFEST_FILE, inside: contentUrls)
142148

143149
if let url = manifestUrl {
144-
145150
let jsonData = try Data(contentsOf: url)
146-
manifest = try? JSONDecoder().decode(ManifestJSONContainer.self, from: jsonData).manifest
151+
manifest = try? JSONDecoder()
152+
.decode(ManifestJSONContainer.self, from: jsonData)
153+
.manifest
147154

148155
if let manifest = manifest, manifest.isValid {
149156
// After validation we are sure that the manifest file contains at

0 commit comments

Comments
 (0)