@ -34,20 +34,22 @@ public class Store {
fileprivate var _collections : [ String : any SomeCollection ] = [ : ]
fileprivate var _collections : [ String : any SomeCollection ] = [ : ]
fileprivate var _apiCallsCollections : [ String : any SomeCollection ] = [ : ]
fileprivate var _apiCallsCollections : [ String : any SomeCollection ] = [ : ]
fileprivate var _apiCallsTimer : Timer ? = nil
// f i l e p r i v a t e v a r _ a p i C a l l T i m e r s : [ S t r i n g : T i m e r ] = [ : ]
fileprivate var _reschedulingCount : Int = 0
public init ( ) { }
public init ( ) { }
public func registerCollection < T : Storable > ( synchronized : Bool ) -> StoredCollection < T > {
public func registerCollection < T : Storable > ( synchronized : Bool ) -> StoredCollection < T > {
// r e g i s t e r c o l l e c t i o n
// r e g i s t e r c o l l e c t i o n
let collection = StoredCollection < T > ( synchronized : synchronized , store : self )
let collection = StoredCollection < T > ( synchronized : synchronized , store : self , loadCompletion : { _ in
} )
self . _collections [ T . resourceName ( ) ] = collection
self . _collections [ T . resourceName ( ) ] = collection
if synchronized { // r e g i s t e r a d d i t i o n a l c o l l e c t i o n f o r a p i c a l l s
if synchronized { // r e g i s t e r a d d i t i o n a l c o l l e c t i o n f o r a p i c a l l s
let apiCallCollection = StoredCollection < ApiCall < T > > ( synchronized : false , store : self )
let apiCallCollection = StoredCollection < ApiCall < T > > ( synchronized : false , store : self , loadCompletion : { apiCallCollection in
self . _reloadTimers ( collection : apiCallCollection )
} )
self . _apiCallsCollections [ T . resourceName ( ) ] = apiCallCollection
self . _apiCallsCollections [ T . resourceName ( ) ] = apiCallCollection
}
}
@ -87,6 +89,12 @@ public class Store {
// MARK: - A p i c a l l r e s c h e d u l i n g
// MARK: - A p i c a l l r e s c h e d u l i n g
fileprivate func _reloadTimers < T : Storable > ( collection : StoredCollection < ApiCall < T > > ) {
for apiCall in collection {
self . startCallsRescheduling ( apiCall : apiCall )
}
}
func apiCallCollection < T : Storable > ( ) throws -> StoredCollection < ApiCall < T > > {
func apiCallCollection < T : Storable > ( ) throws -> StoredCollection < ApiCall < T > > {
if let apiCallCollection = self . _apiCallsCollections [ T . resourceName ( ) ] as ? StoredCollection < ApiCall < T > > {
if let apiCallCollection = self . _apiCallsCollections [ T . resourceName ( ) ] as ? StoredCollection < ApiCall < T > > {
return apiCallCollection
return apiCallCollection
@ -96,15 +104,25 @@ public class Store {
func registerApiCall < T : Storable > ( _ apiCall : ApiCall < T > ) throws {
func registerApiCall < T : Storable > ( _ apiCall : ApiCall < T > ) throws {
let collection : StoredCollection < ApiCall < T > > = try self . apiCallCollection ( )
let collection : StoredCollection < ApiCall < T > > = try self . apiCallCollection ( )
collection . addOrUpdate ( instance : apiCall )
}
if let existingDataCall = collection . first ( where : { $0 . dataId = = apiCall . dataId } ) {
switch apiCall . method {
func deleteApiCallById < T : Storable > ( _ id : String , type : T . Type ) throws {
case Method . put . rawValue :
let collection : StoredCollection < ApiCall < T > > = try self . apiCallCollection ( )
existingDataCall . body = apiCall . body
try collection . deleteById ( id )
collection . addOrUpdate ( instance : existingDataCall )
case Method . delete . rawValue :
try self . deleteApiCallById ( existingDataCall . id , collectionName : T . resourceName ( ) )
default :
collection . addOrUpdate ( instance : apiCall ) // r e w r i t e n e w a t t e m p t v a l u e s
}
} else {
collection . addOrUpdate ( instance : apiCall )
}
}
}
func deleteApiCallById ( _ id : String , collectionName : String ) throws {
func deleteApiCallById ( _ id : String , collectionName : String ) throws {
if let collection = self . _apiCallsCollections [ collectionName ] {
if let collection = self . _apiCallsCollections [ collectionName ] {
try collection . deleteById ( id )
try collection . deleteById ( id )
return
return
@ -112,43 +130,29 @@ public class Store {
throw StoreError . collectionNotRegistered ( type : collectionName )
throw StoreError . collectionNotRegistered ( type : collectionName )
}
}
func deleteApiCall < T : Storable > ( _ apiCall : ApiCall < T > ) throws {
func startCallsRescheduling < T : Storable > ( apiCall : ApiCall < T > ) {
let collection : StoredCollection < ApiCall < T > > = try self . apiCallCollection ( )
let delay = pow ( 2 , 0 + apiCall . attemptsCount )
try collection . delete ( instance : apiCall )
let seconds = NSDecimalNumber ( decimal : delay ) . intValue
}
Logger . log ( " Rerun request in \( seconds ) seconds... " )
func startCallsRescheduling ( ) {
self . _reschedulingCount += 1
DispatchQueue ( label : " queue.scheduling " , qos : . utility )
. asyncAfter ( deadline : . now ( ) + . seconds ( seconds ) ) {
Logger . log ( " Try to execute api call... " )
Task {
do {
_ = try await self . _executeApiCall ( apiCall )
} catch {
Logger . error ( error )
}
}
}
let delay = pow ( 2 , 1 + self . _reschedulingCount )
let seconds = NSDecimalNumber ( decimal : delay ) . doubleValue
self . _apiCallsTimer = Timer . scheduledTimer ( withTimeInterval : seconds , repeats : false , block : { timer in
self . _executeApiCalls ( )
} )
}
}
fileprivate func _executeApiCalls ( ) {
func startCallsRescheduling < T : Storable > ( apiCallId : String , type : T . Type ) throws {
let apiCallCollection : StoredCollection < ApiCall < T > > = try self . apiCallCollection ( )
DispatchQueue ( label : " lestorage.queue.network " ) . async {
if let apiCall = apiCallCollection . findById ( apiCallId ) {
self . startCallsRescheduling ( apiCall : apiCall )
do {
for collection in self . _apiCallsCollections . values {
if let apiCalls = collection . allItems ( ) as ? [ any SomeCall ] {
let sortedCalls = apiCalls . sorted ( keyPath : \ . lastAttemptDate , ascending : true )
for apiCall in sortedCalls {
try apiCall . execute ( )
}
} else {
Logger . w ( " _apiCallsCollections item not castable to [any SomeCall] " )
}
}
} catch {
Logger . error ( error )
}
}
}
}
}