Skip to content

Commit 2b8aefb

Browse files
committed
Fix issue with saving e-tags for xcstrings file.
Make e-tag storage atomic. Read file content before adding new e-tag. Add serial queue for writing operations.
1 parent 1ced5a5 commit 2b8aefb

File tree

5 files changed

+52
-33
lines changed

5 files changed

+52
-33
lines changed

Sources/CrowdinSDK/CrowdinFileSystem/File.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ class ReadWriteFile<T: ReadWriteProtocol>: File {
8484
guard let file = self.file else { return }
8585
file.write(to: self.path)
8686
}
87+
88+
func read() {
89+
self.file = T.read(from: self.path)
90+
}
8791
}
8892

8993
#if os(iOS) || os(tvOS) || os(watchOS)

Sources/CrowdinSDK/CrowdinSDK/Localization/Provider/LocalizationProvider.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ protocol LocalizationProviderProtocol {
1515
var localStorage: LocalLocalizationStorageProtocol { get }
1616
var remoteStorage: RemoteLocalizationStorageProtocol { get }
1717

18-
var localization: String { get set }
18+
var localization: String { get set }
1919
var localizations: [String] { get }
2020

2121
func refreshLocalization()

Sources/CrowdinSDK/Providers/Crowdin/LocalizationDownloader/CrowdinLocalizationDownloader.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ class CrowdinLocalizationDownloader: CrowdinDownloaderProtocol {
8888

8989
xcstrings.forEach { filePath in
9090
let download = CrowdinXcstringsDownloadOperation(filePath: filePath,
91-
localization: localization,
92-
language: manifestManager.xcstringsLanguage,
91+
localization: manifestManager.xcstringsLanguage,
9392
timestamp: timestamp,
9493
contentDeliveryAPI: contentDeliveryAPI)
9594
download.completion = { [weak self] (strings, plurals, error) in

Sources/CrowdinSDK/Providers/Crowdin/LocalizationDownloader/ETagStorage/FileEtagStorage.swift

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,49 +9,65 @@ import Foundation
99

1010
final class FileEtagStorage: AnyEtagStorage {
1111
private static let fileName = "Etags.json"
12+
private let queue = DispatchQueue(label: "com.crowdin.FileEtagStorage")
1213
let localization: String
13-
let dictionaryFile: DictionaryFile
14-
15-
var etags: [String: [String: String]] {
16-
get {
17-
(dictionaryFile.file as? [String: [String: String]]) ?? [String: [String: String]]()
18-
}
19-
set {
20-
dictionaryFile.file = newValue
21-
try? dictionaryFile.save()
22-
}
14+
private var dictionaryFile: DictionaryFile
15+
16+
private func getEtags() -> [String: [String: String]] {
17+
dictionaryFile.read()
18+
return (dictionaryFile.file as? [String: [String: String]]) ?? [:]
2319
}
24-
20+
2521
init(localization: String) {
2622
self.localization = localization
27-
dictionaryFile = DictionaryFile(path: CrowdinFolder.shared.path + "/" + FileEtagStorage.fileName)
23+
let path = CrowdinFolder.shared.path + "/" + FileEtagStorage.fileName
24+
self.dictionaryFile = DictionaryFile(path: path)
2825
if !dictionaryFile.isCreated {
2926
dictionaryFile.create()
3027
}
3128
}
32-
29+
3330
func save(etag: String?, for file: String) {
34-
var localizationEtags = etags[localization] ?? [:]
35-
localizationEtags[file] = etag
36-
etags[localization] = localizationEtags
31+
queue.sync {
32+
var currentEtags = getEtags()
33+
var localizationEtags = currentEtags[localization] ?? [:]
34+
localizationEtags[file] = etag
35+
currentEtags[localization] = localizationEtags
36+
dictionaryFile.file = currentEtags
37+
try? self.dictionaryFile.save()
38+
}
3739
}
38-
40+
3941
func etag(for file: String) -> String? {
40-
etags[localization]?[file]
42+
queue.sync {
43+
let currentEtags = getEtags()
44+
return currentEtags[localization]?[file]
45+
}
4146
}
42-
47+
4348
func clear() {
44-
etags[localization] = nil
49+
queue.sync {
50+
var currentEtags = getEtags()
51+
currentEtags[localization] = nil
52+
dictionaryFile.file = currentEtags
53+
try? self.dictionaryFile.save()
54+
}
4555
}
46-
56+
4757
func clear(for file: String) {
48-
var localizationEtags = etags[localization] ?? [:]
49-
localizationEtags[file] = nil
50-
etags[localization] = localizationEtags
58+
queue.sync {
59+
var currentEtags = getEtags()
60+
var localizationEtags = currentEtags[localization] ?? [:]
61+
localizationEtags[file] = nil
62+
currentEtags[localization] = localizationEtags
63+
dictionaryFile.file = currentEtags
64+
try? self.dictionaryFile.save()
65+
}
5166
}
52-
67+
5368
/// Remove file
5469
static func clear() {
55-
try? DictionaryFile(path: CrowdinFolder.shared.path + "/" + FileEtagStorage.fileName).remove()
70+
let path = CrowdinFolder.shared.path + "/" + FileEtagStorage.fileName
71+
try? DictionaryFile(path: path).remove()
5672
}
5773
}

Sources/CrowdinSDK/Providers/Crowdin/LocalizationDownloader/Operations/CrowdinXcstringsDownloadOperation.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,18 +174,18 @@ class CrowdinXcstringsDownloadOperation: CrowdinDownloadOperation {
174174
var completion: CrowdinDownloadOperationCompletion? = nil
175175
let localization: String
176176

177-
init(filePath: String, localization: String, language: String, timestamp: TimeInterval?, contentDeliveryAPI: CrowdinContentDeliveryAPI, completion: CrowdinDownloadOperationCompletion?) {
177+
init(filePath: String, localization: String, timestamp: TimeInterval?, contentDeliveryAPI: CrowdinContentDeliveryAPI, completion: CrowdinDownloadOperationCompletion?) {
178178
self.localization = localization
179179
self.timestamp = timestamp
180-
self.eTagStorage = FileEtagStorage(localization: language)
180+
self.eTagStorage = FileEtagStorage(localization: localization)
181181
super.init(filePath: filePath, contentDeliveryAPI: contentDeliveryAPI)
182182
self.completion = completion
183183
}
184184

185-
required init(filePath: String, localization: String, language: String, timestamp: TimeInterval?, contentDeliveryAPI: CrowdinContentDeliveryAPI) {
185+
required init(filePath: String, localization: String, timestamp: TimeInterval?, contentDeliveryAPI: CrowdinContentDeliveryAPI) {
186186
self.localization = localization
187187
self.timestamp = timestamp
188-
self.eTagStorage = FileEtagStorage(localization: language)
188+
self.eTagStorage = FileEtagStorage(localization: localization)
189189
super.init(filePath: filePath, contentDeliveryAPI: contentDeliveryAPI)
190190
}
191191

0 commit comments

Comments
 (0)