Skip to content

Commit 397f815

Browse files
Merge pull request #18 from mendix/moo/MOO-2155-native-fs-exception-handling
[MOO-2155] - Fix file not found exception
2 parents 88a0456 + 95634d3 commit 397f815

File tree

5 files changed

+89
-145
lines changed

5 files changed

+89
-145
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
## [Unreleased]
1010

11+
- We fixed an issue that caused a FileNotFoundException during file deletion operations.
1112
- We updated mendix-native to support react v19 and react native v0.78.2.
1213

1314
## [v0.1.3] - 2025-12-05

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Mendix native mobile package for React Native applications.
66

77
Before you begin, ensure you have the following installed:
88

9-
- **Node.js**: Version 18 (specified in `.nvmrc`)
9+
- **Node.js**: Version 24 (specified in `.nvmrc`)
1010
- **Yarn**: Package manager (Yarn workspaces are required)
1111
- **React Native development environment** (optional, only needed if running the example app): Follow the [React Native environment setup guide](https://reactnative.dev/docs/environment-setup)
1212
- For iOS: Xcode and CocoaPods

ios/Modules/Helper/StorageHelper.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ public class StorageHelper {
1919

2020
public static func clearDataAt(url: URL, component: String) {
2121
let path = url.appendingPathComponent(component).path
22-
_ = NativeFsModule.remove(path, error: nil)
22+
do {
23+
try NativeFsModule.remove(path)
24+
} catch {
25+
NSLog("Failed to clear data at path: \(path), error: \(error.localizedDescription)")
26+
}
2327
}
2428
}

ios/Modules/NativeFsModule/NativeFsModule.swift

Lines changed: 79 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,27 @@ public class NativeFsModule: NSObject {
2424
return "\(String(describing: NativeFsModule.self)): \(message)"
2525
}
2626

27+
private func getBlobManager() -> RCTBlobManager? {
28+
guard let blobManager: RCTBlobManager = ReactAppProvider.getModule(type: RCTBlobManager.self) else {
29+
NSLog("NativeFsModule: Failed to get RCTBlobManager")
30+
return nil
31+
}
32+
return blobManager
33+
}
34+
2735
private func readBlobRefAsData(_ blob: [String: Any]) -> Data? {
28-
return RCTBlobManager().resolve(blob)
36+
guard let data = getBlobManager()?.resolve(blob) else {
37+
NSLog("NativeFsModule: Failed to resolve blob")
38+
return nil
39+
}
40+
return data
2941
}
3042

3143
private func readDataAsBlobRef(_ data: Data) -> [String: Any]? {
32-
let blobId = RCTBlobManager().store(data)
44+
guard let blobId = getBlobManager()?.store(data) else {
45+
NSLog("NativeFsModule: Failed to store data as blob")
46+
return nil
47+
}
3348
return [
3449
"blobId": blobId as Any,
3550
"offset": 0,
@@ -49,87 +64,41 @@ public class NativeFsModule: NSObject {
4964
}
5065
}
5166

52-
static func readJson(_ filePath: String, error: NSErrorPointer) -> [String: Any]? {
67+
static func readJson(_ filePath: String) throws -> [String: Any]? {
5368
guard let data = readData(filePath) else {
5469
return nil
5570
}
56-
57-
do {
58-
let result = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
59-
return result as? [String: Any]
60-
} catch let jsonError {
61-
error?.pointee = jsonError as NSError
62-
return nil
63-
}
71+
let result = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
72+
return result as? [String: Any]
6473
}
6574

66-
static func save(_ data: Data, filepath: String, error: NSErrorPointer) -> Bool {
75+
static func save(_ data: Data, filepath: String) throws {
6776
let directoryURL = URL(fileURLWithPath: (filepath as NSString).deletingLastPathComponent)
68-
69-
do {
70-
try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
71-
} catch let directoryError {
72-
error?.pointee = directoryError as NSError
73-
return false
74-
}
75-
76-
var options: Data.WritingOptions = .atomic
77-
if encryptionEnabled {
78-
options = [.atomic, .completeFileProtection]
79-
}
80-
81-
do {
82-
try data.write(to: URL(fileURLWithPath: filepath), options: options)
83-
return true
84-
} catch let writeError {
85-
error?.pointee = writeError as NSError
86-
return false
87-
}
77+
try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
78+
let options: Data.WritingOptions = encryptionEnabled ? [.atomic, .completeFileProtection] : .atomic
79+
try data.write(to: URL(fileURLWithPath: filepath), options: options)
8880
}
8981

90-
static func move(_ filepath: String, newPath: String, error: NSErrorPointer) -> Bool {
82+
static func move(_ filepath: String, newPath: String) throws {
9183
let fileManager = FileManager.default
92-
9384
guard fileManager.fileExists(atPath: filepath) else {
94-
error?.pointee = NSError(domain: NativeFsErrorDomain, code: -1, userInfo: [NSLocalizedDescriptionKey: "File does not exist"])
95-
return false
85+
throw NSError(domain: NativeFsErrorDomain, code: -1, userInfo: [NSLocalizedDescriptionKey: "File does not exist"])
9686
}
97-
9887
let directoryURL = URL(fileURLWithPath: (newPath as NSString).deletingLastPathComponent)
99-
100-
do {
101-
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
102-
} catch let directoryError {
103-
error?.pointee = directoryError as NSError
104-
return false
105-
}
106-
107-
do {
108-
try fileManager.moveItem(atPath: filepath, toPath: newPath)
109-
return true
110-
} catch let moveError {
111-
error?.pointee = moveError as NSError
112-
return false
113-
}
88+
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
89+
try fileManager.moveItem(atPath: filepath, toPath: newPath)
11490
}
11591

116-
static func remove(_ filepath: String, error: NSErrorPointer) -> Bool {
92+
static func remove(_ filepath: String) throws {
11793
let fileManager = FileManager.default
118-
11994
guard fileManager.fileExists(atPath: filepath) else {
120-
return false
121-
}
122-
123-
do {
124-
try fileManager.removeItem(atPath: filepath)
125-
return true
126-
} catch let removeError {
127-
error?.pointee = removeError as NSError
128-
return false
95+
NSLog("Trying to delete non-existing file: \(filepath)")
96+
return
12997
}
98+
try fileManager.removeItem(atPath: filepath)
13099
}
131100

132-
static func ensureWhiteListedPath(_ paths: [String], error: NSErrorPointer) -> Bool {
101+
static func ensureWhiteListedPath(_ paths: [String]) throws {
133102
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first ?? ""
134103
let cachesPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first ?? ""
135104
let tempPath = (NSTemporaryDirectory() as NSString).standardizingPath
@@ -138,15 +107,13 @@ public class NativeFsModule: NSObject {
138107
if !path.hasPrefix(documentsPath) &&
139108
!path.hasPrefix(cachesPath) &&
140109
!path.hasPrefix(tempPath) {
141-
error?.pointee = NSError(
110+
throw NSError(
142111
domain: NativeFsErrorDomain,
143112
code: 999,
144113
userInfo: [NSLocalizedDescriptionKey: "The path \(path) does not point to the documents directory"]
145114
)
146-
return false
147115
}
148116
}
149-
return true
150117
}
151118

152119
static func list(_ dirPath: String) -> [String] {
@@ -169,34 +136,26 @@ public class NativeFsModule: NSObject {
169136
reject: @escaping RCTPromiseRejectBlock
170137
) {
171138

172-
var error: NSError?
173-
if !NativeFsModule.ensureWhiteListedPath([filepath], error: &error) {
174-
reject(NativeFsModule.INVALID_PATH, NativeFsModule.formatError("Path not accessible"), error)
175-
return
176-
}
139+
guard isWhiteListedPath(filepath, reject: reject) else { return }
177140

178141
guard let data = readBlobRefAsData(blob) else {
179142
reject(NativeFsModule.ERROR_READ_FAILED, NativeFsModule.formatError("Failed to read blob"), nil)
180143
return
181144
}
182145

183-
if !NativeFsModule.save(data, filepath: filepath, error: &error) {
146+
do {
147+
try NativeFsModule.save(data, filepath: filepath)
148+
resolve(nil)
149+
} catch {
184150
reject(NativeFsModule.ERROR_SAVE_FAILED, NativeFsModule.formatError("Save failed"), error)
185-
return
186151
}
187-
188-
resolve(nil)
189152
}
190153

191154
public func read(_ filepath: String,
192155
resolve: @escaping RCTPromiseResolveBlock,
193156
reject: @escaping RCTPromiseRejectBlock) {
194157

195-
var error: NSError?
196-
if !NativeFsModule.ensureWhiteListedPath([filepath], error: &error) {
197-
reject(NativeFsModule.INVALID_PATH, NativeFsModule.formatError("Path not accessible"), error)
198-
return
199-
}
158+
guard isWhiteListedPath(filepath, reject: reject) else { return }
200159

201160
guard let data = NativeFsModule.readData(filepath) else {
202161
resolve(nil)
@@ -216,45 +175,33 @@ public class NativeFsModule: NSObject {
216175
resolve: @escaping RCTPromiseResolveBlock,
217176
reject: @escaping RCTPromiseRejectBlock) {
218177

219-
var error: NSError?
220-
if !NativeFsModule.ensureWhiteListedPath([filepath, newPath], error: &error) {
221-
reject(NativeFsModule.INVALID_PATH, NativeFsModule.formatError("Path not accessible"), error)
222-
return
223-
}
178+
guard isWhiteListedPath(filepath, newPath, reject: reject) else { return }
224179

225-
if !NativeFsModule.move(filepath, newPath: newPath, error: &error) {
180+
do {
181+
try NativeFsModule.move(filepath, newPath: newPath)
182+
resolve(nil)
183+
} catch {
226184
reject(NativeFsModule.ERROR_MOVE_FAILED, NativeFsModule.formatError("Failed to move file"), error)
227-
return
228185
}
229-
230-
resolve(nil)
231186
}
232187

233188
public func remove(_ filepath: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
234189

235-
var error: NSError?
236-
if !NativeFsModule.ensureWhiteListedPath([filepath], error: &error) {
237-
reject(NativeFsModule.INVALID_PATH, NativeFsModule.formatError("Path not accessible"), error)
238-
return
239-
}
190+
guard isWhiteListedPath(filepath, reject: reject) else { return }
240191

241-
if !NativeFsModule.remove(filepath, error: &error) {
192+
do {
193+
try NativeFsModule.remove(filepath)
194+
resolve(nil)
195+
} catch {
242196
reject(NativeFsModule.ERROR_DELETE_FAILED, NativeFsModule.formatError("Failed to delete file"), error)
243-
return
244197
}
245-
246-
resolve(nil)
247198
}
248199

249200
public func list(_ dirPath: String,
250201
resolve: @escaping RCTPromiseResolveBlock,
251202
reject: @escaping RCTPromiseRejectBlock) {
252203

253-
var error: NSError?
254-
if !NativeFsModule.ensureWhiteListedPath([dirPath], error: &error) {
255-
reject(NativeFsModule.INVALID_PATH, NativeFsModule.formatError("Path not accessible"), error)
256-
return
257-
}
204+
guard isWhiteListedPath(dirPath, reject: reject) else { return }
258205

259206
resolve(NativeFsModule.list(dirPath))
260207
}
@@ -263,11 +210,7 @@ public class NativeFsModule: NSObject {
263210
resolve: @escaping RCTPromiseResolveBlock,
264211
reject: @escaping RCTPromiseRejectBlock) {
265212

266-
var error: NSError?
267-
if !NativeFsModule.ensureWhiteListedPath([filePath], error: &error) {
268-
reject(NativeFsModule.INVALID_PATH, NativeFsModule.formatError("Path not accessible"), error)
269-
return
270-
}
213+
guard isWhiteListedPath(filePath, reject: reject) else { return }
271214

272215
guard let data = NativeFsModule.readData(filePath) else {
273216
resolve(nil)
@@ -282,12 +225,7 @@ public class NativeFsModule: NSObject {
282225
public func fileExists(_ filepath: String,
283226
resolve: @escaping RCTPromiseResolveBlock,
284227
reject: @escaping RCTPromiseRejectBlock) {
285-
286-
var error: NSError?
287-
if !NativeFsModule.ensureWhiteListedPath([filepath], error: &error) {
288-
reject(NativeFsModule.INVALID_PATH, NativeFsModule.formatError("Path not accessible"), error)
289-
return
290-
}
228+
guard isWhiteListedPath(filepath, reject: reject) else { return }
291229

292230
let exists = FileManager.default.fileExists(atPath: filepath)
293231
resolve(NSNumber(value: exists))
@@ -297,46 +235,36 @@ public class NativeFsModule: NSObject {
297235
resolve: @escaping RCTPromiseResolveBlock,
298236
reject: @escaping RCTPromiseRejectBlock) {
299237

300-
var error: NSError?
301-
if !NativeFsModule.ensureWhiteListedPath([filepath], error: &error) {
302-
reject(NativeFsModule.INVALID_PATH, NativeFsModule.formatError("Path not accessible"), error)
303-
return
304-
}
238+
guard isWhiteListedPath(filepath, reject: reject) else { return }
305239

306-
guard let data = NativeFsModule.readJson(filepath, error: &error) else {
307-
if let error = error {
308-
reject(NativeFsModule.ERROR_SERIALIZATION_FAILED, NativeFsModule.formatError("Failed to deserialize JSON"), error)
309-
} else {
310-
resolve(nil)
311-
}
312-
return
240+
do {
241+
let data = try NativeFsModule.readJson(filepath)
242+
resolve(data)
243+
} catch {
244+
reject(NativeFsModule.ERROR_SERIALIZATION_FAILED, NativeFsModule.formatError("Failed to deserialize JSON"), error)
313245
}
314-
315-
resolve(data)
316246
}
317247

318248
public func writeJson(_ data: [String: Any],
319249
filepath: String,
320250
resolve: @escaping RCTPromiseResolveBlock,
321251
reject: @escaping RCTPromiseRejectBlock) {
322252

323-
var error: NSError?
324-
if !NativeFsModule.ensureWhiteListedPath([filepath], error: &error) {
325-
reject(NativeFsModule.INVALID_PATH, "Path not accessible", error)
253+
guard isWhiteListedPath(filepath, reject: reject) else { return }
254+
255+
var jsonData: Data
256+
do {
257+
jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
258+
} catch {
259+
reject(NativeFsModule.ERROR_SERIALIZATION_FAILED, NativeFsModule.formatError("Failed to serialize JSON"), error)
326260
return
327261
}
328262

329263
do {
330-
let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
331-
332-
if !NativeFsModule.save(jsonData, filepath: filepath, error: &error) {
333-
reject(NativeFsModule.ERROR_SAVE_FAILED, NativeFsModule.formatError("Failed to write JSON"), error)
334-
return
335-
}
336-
264+
try NativeFsModule.save(jsonData, filepath: filepath)
337265
resolve(nil)
338266
} catch {
339-
reject(NativeFsModule.ERROR_SERIALIZATION_FAILED, NativeFsModule.formatError("Failed to serialize JSON"), error)
267+
reject(NativeFsModule.ERROR_SAVE_FAILED, NativeFsModule.formatError("Failed to write JSON"), error)
340268
}
341269
}
342270

@@ -345,4 +273,15 @@ public class NativeFsModule: NSObject {
345273
"SUPPORTS_DIRECTORY_MOVE": true,
346274
"SUPPORTS_ENCRYPTION": true
347275
]
276+
277+
private func isWhiteListedPath(_ paths: String..., reject: RCTPromiseRejectBlock) -> Bool {
278+
do {
279+
try NativeFsModule.ensureWhiteListedPath(paths)
280+
return true
281+
} catch let error {
282+
reject(NativeFsModule.INVALID_PATH, NativeFsModule.formatError("Path not accessible"), error)
283+
return false
284+
}
285+
}
348286
}
287+

ios/Modules/NativeOtaModule/OtaHelpers.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ class OtaHelpers: NSObject {
5050
}
5151

5252
static func getNativeDependencies() -> [String: Any] {
53-
guard let path = Bundle.main.path(forResource: "native_dependencies", ofType: "json") else {
53+
guard let path = Bundle.main.path(forResource: "native_dependencies", ofType: "json"),
54+
let data = try? NativeFsModule.readJson(path) else {
5455
return [:]
5556
}
56-
57-
return NativeFsModule.readJson(path, error: nil) ?? [:]
57+
return data
5858
}
5959
}

0 commit comments

Comments
 (0)