@@ -17,7 +17,7 @@ public class MBLoggerCoreDataManager {
1717 static let model = " CDLogMessage "
1818 static let dbSizeLimitKB : Int = 10_000
1919 static let batchSize = 15
20-
20+
2121 /// batch size | operationBatchLimitBeforeNeedToDelete
2222 /// 1 | 20
2323 /// 2 | 14
@@ -36,9 +36,11 @@ public class MBLoggerCoreDataManager {
3636 return max ( 1 , Int ( 20 / pow( Double ( batchSize) , 0.5 ) ) )
3737 } ( )
3838 }
39-
40- private var logBuffer : [ LogMessage ] = [ ]
41- private let queue = DispatchQueue ( label: " com.Mindbox.loggerManager " , qos: . utility)
39+
40+ private var logBuffer : [ LogMessage ]
41+ private let isAppExtension : Bool
42+ private let queue : DispatchQueue
43+
4244 private var writeCount = 0 {
4345 didSet {
4446 if writeCount > Constants . operationBatchLimitBeforeNeedToDelete {
@@ -47,7 +49,22 @@ public class MBLoggerCoreDataManager {
4749 }
4850 }
4951 }
50-
52+
53+ private lazy var flushStrategy : ( ) -> Void = {
54+ if isAppExtension {
55+ return { [ weak self] in
56+ self ? . flushBuffer ( )
57+ }
58+ } else {
59+ return { [ weak self] in
60+ guard let self = self else { return }
61+ if self . logBuffer. count >= Constants . batchSize {
62+ self . flushBuffer ( )
63+ }
64+ }
65+ }
66+ } ( )
67+
5168 // MARK: CoreData objects
5269
5370 private lazy var persistentContainer : MBPersistentContainer = {
@@ -93,51 +110,53 @@ public class MBLoggerCoreDataManager {
93110 context. mergePolicy = NSMergePolicy ( merge: . mergeByPropertyStoreTrumpMergePolicyType)
94111 return context
95112 } ( )
96-
113+
97114 // MARK: Initializers and deinitializer
98-
99- private init ( ) {
115+
116+ private init ( isAppExtension: Bool = Bundle . main. bundlePath. hasSuffix ( " .appex " ) ) {
117+ self . isAppExtension = isAppExtension
118+ self . logBuffer = [ ]
119+ self . logBuffer. reserveCapacity ( Constants . batchSize)
120+ self . queue = DispatchQueue ( label: " com.Mindbox.loggerManager " , qos: . utility)
100121 setupNotificationCenterObservers ( )
101122 }
102-
123+
103124 deinit {
104- removeAllNotificationCenterObservers ( )
125+ NotificationCenter . default . removeObserver ( self )
105126 }
106127}
107128
108129// MARK: - CRUD Operations
109130
110131public extension MBLoggerCoreDataManager {
111132 // MARK: Create
112-
133+
113134 func create( message: String , timestamp: Date , completion: ( ( ) -> Void ) ? = nil ) {
114135 queue. async {
115136 let newLogMessage = LogMessage ( timestamp: timestamp, message: message)
116137 self . logBuffer. append ( newLogMessage)
117-
118- if self . logBuffer. count >= Constants . batchSize {
119- self . flushBuffer ( )
120- }
121-
138+
139+ self . flushStrategy ( )
140+
122141 completion ? ( )
123142 }
124143 }
125-
144+
126145 private func flushBuffer( ) {
127146 guard !logBuffer. isEmpty else { return }
128-
147+
129148 if #available( iOS 13 . 0 , * ) {
130149 performBatchInsert ( )
131150 } else {
132151 performContextInsertion ( )
133152 }
134153 }
135-
154+
136155 @available ( iOS 13 . 0 , * )
137156 private func performBatchInsert( ) {
138157 let insertData = logBuffer. map { [ " message " : $0. message, " timestamp " : $0. timestamp] }
139158 let insertRequest = NSBatchInsertRequest ( entityName: Constants . model, objects: insertData)
140-
159+
141160 do {
142161 try context. execute ( insertRequest)
143162 logBuffer. removeAll ( )
@@ -146,23 +165,23 @@ public extension MBLoggerCoreDataManager {
146165 let errorMessage = " Failed to batch insert logs: \( error. localizedDescription) "
147166 let errorLogData = [ [ " message " : errorMessage, " timestamp " : Date ( ) ] ]
148167 let errorLogInsertRequest = NSBatchInsertRequest ( entityName: Constants . model, objects: errorLogData)
149-
168+
150169 do {
151170 try context. execute ( errorLogInsertRequest)
152171 } catch { }
153172 }
154173 }
155-
174+
156175 // MARK: Read
157-
176+
158177 func getFirstLog( ) throws -> LogMessage ? {
159178 return try fetchSingleLog ( ascending: true )
160179 }
161180
162181 func getLastLog( ) throws -> LogMessage ? {
163182 return try fetchSingleLog ( ascending: false )
164183 }
165-
184+
166185 private func fetchSingleLog( ascending: Bool ) throws -> LogMessage ? {
167186 var fetchedLogMessage : LogMessage ?
168187 try context. executePerformAndWait {
@@ -175,58 +194,66 @@ public extension MBLoggerCoreDataManager {
175194 }
176195 return fetchedLogMessage
177196 }
178-
179- func fetchPeriod( _ from: Date , _ to: Date ) throws -> [ LogMessage ] {
197+
198+ func fetchPeriod( _ from: Date , _ to: Date , ascending : Bool = true ) throws -> [ LogMessage ] {
180199 var fetchedLogs : [ LogMessage ] = [ ]
181-
200+
182201 try context. executePerformAndWait {
183- let fetchRequest = NSFetchRequest < CDLogMessage > ( entityName: Constants . model)
202+ let fetchRequest = NSFetchRequest < NSDictionary > ( entityName: Constants . model)
203+ fetchRequest. resultType = . dictionaryResultType
204+ fetchRequest. propertiesToFetch = [ " timestamp " , " message " , " objectID " ]
184205 fetchRequest. predicate = NSPredicate ( format: " timestamp >= %@ AND timestamp <= %@ " ,
185206 from as NSDate , to as NSDate )
186-
187207 fetchRequest. fetchBatchSize = 50 // Setting batchSize for optimal memory consumption
188-
189- let logs = try context. fetch ( fetchRequest)
190- fetchedLogs = logs. map { LogMessage ( timestamp: $0. timestamp, message: $0. message) }
208+
209+ let sortDescriptor = NSSortDescriptor ( key: " timestamp " , ascending: ascending)
210+ fetchRequest. sortDescriptors = [ sortDescriptor]
211+
212+ let results = try context. fetch ( fetchRequest)
213+ fetchedLogs = results. compactMap { dict -> LogMessage ? in
214+ guard let timestamp = dict [ " timestamp " ] as? Date ,
215+ let message = dict [ " message " ] as? String else { return nil }
216+ return LogMessage ( timestamp: timestamp, message: message)
217+ }
191218 }
192-
219+
193220 return fetchedLogs
194221 }
195-
222+
196223 // MARK: Delete
197-
224+
198225 func deleteTenPercentOfAllOldRecords( ) throws {
199226 try context. executePerformAndWait {
200227 let fetchRequest = NSFetchRequest < NSFetchRequestResult > ( entityName: Constants . model)
201-
228+
202229 let count = try context. count ( for: fetchRequest)
203230 let limit = Int ( ( Double ( count) * 0.1 ) . rounded ( ) ) // 10% percent of all records should be removed
204-
231+
205232 fetchRequest. sortDescriptors = [ NSSortDescriptor ( key: " timestamp " , ascending: true ) ]
206233 fetchRequest. fetchLimit = limit
207-
234+
208235 let deleteRequest = NSBatchDeleteRequest ( fetchRequest: fetchRequest)
209236 deleteRequest. resultType = . resultTypeObjectIDs
210-
237+
211238 let batchDeleteResult = try context. execute ( deleteRequest) as? NSBatchDeleteResult
212239 if let objectIDs = batchDeleteResult? . result as? [ NSManagedObjectID ] {
213240 let changes = [ NSDeletedObjectsKey: objectIDs]
214241 NSManagedObjectContext . mergeChanges ( fromRemoteContextSave: changes, into: [ context] )
215242 }
216-
243+
217244 Logger . common ( message: " \( limit) logs have been deleted " , level: . debug, category: . general)
218245 }
219246 }
220-
247+
221248 func deleteAll( ) throws {
222249 self . logBuffer. removeAll ( )
223250 try context. executePerformAndWait {
224251 let fetchRequest = NSFetchRequest < NSFetchRequestResult > ( entityName: Constants . model)
225252 let deleteRequest = NSBatchDeleteRequest ( fetchRequest: fetchRequest)
226253 deleteRequest. resultType = . resultTypeObjectIDs
227-
254+
228255 let batchDeleteResult = try context. execute ( deleteRequest) as? NSBatchDeleteResult
229-
256+
230257 if let objectIDs = batchDeleteResult? . result as? [ NSManagedObjectID ] {
231258 let changes : [ AnyHashable : Any ] = [ NSDeletedObjectsKey: objectIDs]
232259 NSManagedObjectContext . mergeChanges ( fromRemoteContextSave: changes, into: [ context] )
@@ -243,24 +270,20 @@ private extension MBLoggerCoreDataManager {
243270 selector: #selector( applicationDidEnterBackground) ,
244271 name: UIApplication . didEnterBackgroundNotification,
245272 object: nil )
246-
273+
247274 NotificationCenter . default. addObserver ( self ,
248275 selector: #selector( applicationWillTerminate) ,
249276 name: UIApplication . willTerminateNotification,
250277 object: nil )
251278 }
252-
253- func removeAllNotificationCenterObservers( ) {
254- NotificationCenter . default. removeObserver ( self )
255- }
256-
279+
257280 @objc
258281 func applicationDidEnterBackground( ) {
259282 queue. async { [ weak self] in
260283 self ? . flushBuffer ( )
261284 }
262285 }
263-
286+
264287 @objc
265288 func applicationWillTerminate( ) {
266289 queue. async { [ weak self] in
@@ -280,7 +303,7 @@ private extension MBLoggerCoreDataManager {
280303 entity. message = log. message
281304 entity. timestamp = log. timestamp
282305 }
283-
306+
284307 try self . saveEvent ( withContext: self . context)
285308 logBuffer. removeAll ( )
286309 writeCount += 1
@@ -290,13 +313,13 @@ private extension MBLoggerCoreDataManager {
290313 let errorLogEntity = CDLogMessage ( context: self . context)
291314 errorLogEntity. message = errorMessage
292315 errorLogEntity. timestamp = Date ( )
293-
316+
294317 do {
295318 try self . saveEvent ( withContext: self . context)
296319 } catch { }
297320 }
298321 }
299-
322+
300323 func saveEvent( withContext context: NSManagedObjectContext ) throws {
301324 guard context. hasChanges else { return }
302325 try saveContext ( context)
@@ -327,7 +350,7 @@ private extension MBLoggerCoreDataManager {
327350 } catch { }
328351 }
329352 }
330-
353+
331354 func getDBFileSize( ) -> Int {
332355 guard let url = context. persistentStoreCoordinator? . persistentStores. first? . url else {
333356 return 0
@@ -342,11 +365,15 @@ extension MBLoggerCoreDataManager {
342365 var debugBatchSize : Int {
343366 return Constants . batchSize
344367 }
345-
368+
346369 func debugFlushBuffer( ) {
347370 queue. async {
348371 self . flushBuffer ( )
349372 }
350373 }
374+
375+ convenience init ( debugIsAppExtension: Bool ) {
376+ self . init ( isAppExtension: debugIsAppExtension)
377+ }
351378}
352379#endif
0 commit comments