@@ -19,8 +19,16 @@ import FirebaseCore
19
19
enum ModelDownloadStatus {
20
20
case notStarted
21
21
case inProgress
22
- case successful
23
- case failed
22
+ case complete
23
+ }
24
+
25
+ /// Download error codes.
26
+ enum ModelDownloadErrorCode {
27
+ case noError
28
+ case urlExpired
29
+ case noConnection
30
+ case downloadFailed
31
+ case httpError( code: Int )
24
32
}
25
33
26
34
/// Manager to handle model downloading device and storing downloaded model info to persistent storage.
@@ -64,13 +72,19 @@ extension ModelDownloadTask {
64
72
func download( progressHandler: ProgressHandler ? , completion: @escaping Completion ) {
65
73
/// Prevent multiple concurrent downloads.
66
74
guard downloadStatus == . notStarted else {
67
- completion ( . failure( . internalError( description: ModelDownloadTask . ErrorDescription
68
- . anotherDownloadInProgress) ) )
69
75
DeviceLogger . logEvent ( level: . debug,
70
76
message: ModelDownloadTask . ErrorDescription. anotherDownloadInProgress,
71
77
messageCode: . anotherDownloadInProgressError)
78
+ telemetryLogger? . logModelDownloadEvent ( eventName: . modelDownload,
79
+ status: . failed,
80
+ downloadErrorCode: . downloadFailed)
81
+ completion ( . failure( . internalError( description: ModelDownloadTask . ErrorDescription
82
+ . anotherDownloadInProgress) ) )
72
83
return
73
84
}
85
+ telemetryLogger? . logModelDownloadEvent ( eventName: . modelDownload,
86
+ status: . downloading,
87
+ downloadErrorCode: . noError)
74
88
downloader. downloadFile ( with: remoteModelInfo. downloadURL,
75
89
progressHandler: { downloadedBytes, totalBytes in
76
90
/// Fraction of model file downloaded.
@@ -82,7 +96,7 @@ extension ModelDownloadTask {
82
96
DeviceLogger . logEvent ( level: . debug,
83
97
message: ModelDownloadTask . DebugDescription
84
98
. receivedServerResponse,
85
- messageCode: . validModelDownloadResponse )
99
+ messageCode: . validHTTPResponse )
86
100
self . handleResponse (
87
101
response: response. urlResponse,
88
102
tempURL: response. fileURL,
@@ -98,25 +112,33 @@ extension ModelDownloadTask {
98
112
DeviceLogger . logEvent ( level: . debug,
99
113
message: description,
100
114
messageCode: . hostnameError)
101
- // TODO: Handle this case better.
102
- case FileDownloaderError . sessionInvalidated:
103
- downloadError = . failedPrecondition
104
- DeviceLogger . logEvent ( level: . debug,
105
- message: ModelDownloadTask . ErrorDescription. sessionInvalidated,
106
- messageCode: . invalidDownloadSessionError)
115
+ self . telemetryLogger? . logModelDownloadEvent (
116
+ eventName: . modelDownload,
117
+ status: . failed,
118
+ downloadErrorCode: . noConnection
119
+ )
107
120
case FileDownloaderError . unexpectedResponseType:
108
- let description = ModelDownloadTask . ErrorDescription. invalidServerResponse
121
+ let description = ModelDownloadTask . ErrorDescription. invalidHTTPResponse
109
122
downloadError = . internalError( description: description)
110
123
DeviceLogger . logEvent ( level: . debug,
111
124
message: description,
112
- messageCode: . invalidResponseError)
113
-
125
+ messageCode: . invalidHTTPResponse)
126
+ self . telemetryLogger? . logModelDownloadEvent (
127
+ eventName: . modelDownload,
128
+ status: . failed,
129
+ downloadErrorCode: . downloadFailed
130
+ )
114
131
default :
115
132
let description = ModelDownloadTask . ErrorDescription. unknownDownloadError
116
133
downloadError = . internalError( description: description)
117
134
DeviceLogger . logEvent ( level: . debug,
118
135
message: description,
119
136
messageCode: . modelDownloadError)
137
+ self . telemetryLogger? . logModelDownloadEvent (
138
+ eventName: . modelDownload,
139
+ status: . failed,
140
+ downloadErrorCode: . downloadFailed
141
+ )
120
142
}
121
143
completion ( . failure( downloadError) )
122
144
}
@@ -125,22 +147,67 @@ extension ModelDownloadTask {
125
147
126
148
/// Handle model download response.
127
149
func handleResponse( response: HTTPURLResponse , tempURL: URL , completion: @escaping Completion ) {
150
+ downloadStatus = . complete
128
151
guard ( 200 ..< 299 ) . contains ( response. statusCode) else {
129
152
switch response. statusCode {
130
153
/// Possible failure due to download URL expiry.
131
154
case 400 :
132
155
let currentDateTime = Date ( )
133
156
/// Check if download url has expired.
134
157
guard currentDateTime > remoteModelInfo. urlExpiryTime else {
158
+ DeviceLogger . logEvent ( level: . debug,
159
+ message: ModelDownloadTask . ErrorDescription
160
+ . invalidModelName ( remoteModelInfo. name) ,
161
+ messageCode: . invalidModelName)
162
+ telemetryLogger? . logModelDownloadEvent (
163
+ eventName: . modelDownload,
164
+ status: . failed,
165
+ downloadErrorCode: . httpError( code: response. statusCode)
166
+ )
135
167
completion ( . failure( . invalidArgument) )
136
168
return
137
169
}
170
+ DeviceLogger . logEvent ( level: . debug,
171
+ message: ModelDownloadTask . ErrorDescription. expiredModelInfo,
172
+ messageCode: . expiredModelInfo)
173
+ telemetryLogger? . logModelDownloadEvent (
174
+ eventName: . modelDownload,
175
+ status: . failed,
176
+ downloadErrorCode: . urlExpired
177
+ )
138
178
completion ( . failure( . expiredDownloadURL) )
139
- case 401 , 403 : completion ( . failure( . permissionDenied) )
140
- case 404 : completion ( . failure( . notFound) )
179
+ case 401 , 403 :
180
+ DeviceLogger . logEvent ( level: . debug,
181
+ message: ModelDownloadTask . ErrorDescription. permissionDenied,
182
+ messageCode: . permissionDenied)
183
+ telemetryLogger? . logModelDownloadEvent (
184
+ eventName: . modelDownload,
185
+ status: . failed,
186
+ downloadErrorCode: . httpError( code: response. statusCode)
187
+ )
188
+ completion ( . failure( . permissionDenied) )
189
+ case 404 :
190
+ DeviceLogger . logEvent ( level: . debug,
191
+ message: ModelDownloadTask . ErrorDescription
192
+ . modelNotFound ( remoteModelInfo. name) ,
193
+ messageCode: . modelNotFound)
194
+ telemetryLogger? . logModelDownloadEvent (
195
+ eventName: . modelDownload,
196
+ status: . failed,
197
+ downloadErrorCode: . httpError( code: response. statusCode)
198
+ )
199
+ completion ( . failure( . notFound) )
141
200
default :
142
201
let description = ModelDownloadTask . ErrorDescription
143
202
. modelDownloadFailed ( response. statusCode)
203
+ DeviceLogger . logEvent ( level: . debug,
204
+ message: description,
205
+ messageCode: . modelDownloadError)
206
+ telemetryLogger? . logModelDownloadEvent (
207
+ eventName: . modelDownload,
208
+ status: . failed,
209
+ downloadErrorCode: . httpError( code: response. statusCode)
210
+ )
144
211
completion ( . failure( . internalError( description: description) ) )
145
212
}
146
213
return
@@ -169,16 +236,17 @@ extension ModelDownloadTask {
169
236
messageCode: . downloadedModelInfoSaved)
170
237
/// Build model from model info.
171
238
let model = CustomModel ( localModelInfo: localModelInfo)
172
- downloadStatus = . successful
239
+ DeviceLogger . logEvent ( level: . debug,
240
+ message: ModelDownloadTask . DebugDescription. modelDownloaded,
241
+ messageCode: . modelDownloaded)
173
242
telemetryLogger? . logModelDownloadEvent (
174
243
eventName: . modelDownload,
175
- status: downloadStatus,
176
- model: model
244
+ status: . succeeded,
245
+ model: model,
246
+ downloadErrorCode: . noError
177
247
)
178
248
completion ( . success( model) )
179
249
} catch let error as DownloadError {
180
- downloadStatus = . failed
181
- telemetryLogger? . logModelDownloadEvent ( eventName: . modelDownload, status: downloadStatus)
182
250
if error == . notEnoughSpace {
183
251
DeviceLogger . logEvent ( level: . debug,
184
252
message: ModelDownloadTask . ErrorDescription. notEnoughSpace,
@@ -188,14 +256,18 @@ extension ModelDownloadTask {
188
256
message: ModelDownloadTask . ErrorDescription. saveModel,
189
257
messageCode: . downloadedModelSaveError)
190
258
}
259
+ telemetryLogger? . logModelDownloadEvent ( eventName: . modelDownload,
260
+ status: . succeeded,
261
+ downloadErrorCode: . downloadFailed)
191
262
completion ( . failure( error) )
192
263
return
193
264
} catch {
194
- downloadStatus = . failed
195
- telemetryLogger? . logModelDownloadEvent ( eventName: . modelDownload, status: downloadStatus)
196
265
DeviceLogger . logEvent ( level: . debug,
197
266
message: ModelDownloadTask . ErrorDescription. saveModel,
198
267
messageCode: . downloadedModelSaveError)
268
+ telemetryLogger? . logModelDownloadEvent ( eventName: . modelDownload,
269
+ status: . succeeded,
270
+ downloadErrorCode: . downloadFailed)
199
271
completion ( . failure( . internalError( description: error. localizedDescription) ) )
200
272
return
201
273
}
@@ -208,7 +280,8 @@ extension ModelDownloadTask {
208
280
private enum DebugDescription {
209
281
static let savedModelFile = " Model file saved successfully to device. "
210
282
static let savedLocalModelInfo = " Downloaded model info saved successfully to user defaults. "
211
- static let receivedServerResponse = " Received a valid response from server. "
283
+ static let receivedServerResponse = " Received a valid response from download server. "
284
+ static let modelDownloaded = " Model download completed successfully. "
212
285
}
213
286
214
287
/// Error descriptions.
@@ -221,13 +294,22 @@ extension ModelDownloadTask {
221
294
" Model download failed with HTTP error code: \( code) "
222
295
}
223
296
297
+ static let modelNotFound = { ( name: String ) in
298
+ " No model found with name: \( name) "
299
+ }
300
+
301
+ static let invalidModelName = { ( name: String ) in
302
+ " Invalid model name: \( name) "
303
+ }
304
+
224
305
static let sessionInvalidated = " Session invalidated due to failed pre-conditions. "
225
- static let invalidServerResponse =
226
- " Could not get valid server response for model downloading. "
306
+ static let invalidHTTPResponse =
307
+ " Could not get valid HTTP response for model downloading. "
227
308
static let unknownDownloadError = " Unable to download model due to unknown error. "
228
309
static let saveModel = " Unable to save downloaded remote model file. "
229
310
static let notEnoughSpace = " Not enough space on device. "
230
311
static let expiredModelInfo = " Unable to update expired model info. "
231
312
static let anotherDownloadInProgress = " Download already in progress. "
313
+ static let permissionDenied = " Invalid or missing permissions to download model. "
232
314
}
233
315
}
0 commit comments