Skip to content

Commit

Permalink
Merge pull request #555 from Iterable/tapash/mob-4408-coredata
Browse files Browse the repository at this point in the history
Fix core data multi threading debug errors
  • Loading branch information
tapashmajumder authored May 17, 2022
2 parents 07173cd + 72846b7 commit 2c0527f
Show file tree
Hide file tree
Showing 14 changed files with 333 additions and 194 deletions.
6 changes: 6 additions & 0 deletions swift-sdk.xcodeproj/xcshareddata/xcschemes/swift-sdk.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@
ReferencedContainer = "container:swift-sdk.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-com.apple.CoreData.ConcurrencyDebug 1"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand Down
10 changes: 10 additions & 0 deletions swift-sdk/Internal/CoreDataUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,13 @@ struct CoreDataUtil {
}
}
}

extension NSManagedObjectContext {
func performAndWait<T>(_ block: () throws -> T) throws -> T {
var result: Result<T, Error>?
performAndWait {
result = Result { try block() }
}
return try result!.get()
}
}
5 changes: 4 additions & 1 deletion swift-sdk/Internal/HealthMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ struct HealthMonitorDataProvider: HealthMonitorDataProviderProtocol {
let maxTasks: Int

func countTasks() throws -> Int {
return try persistenceContextProvider.newBackgroundContext().countTasks()
let context = persistenceContextProvider.newBackgroundContext()
return try context.performAndWait {
try context.countTasks()
}
}

private let persistenceContextProvider: IterablePersistenceContextProvider
Expand Down
20 changes: 13 additions & 7 deletions swift-sdk/Internal/IterableCoreDataPersistence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,16 @@ struct CoreDataPersistenceContext: IterablePersistenceContext {
}

func nextTask() throws -> IterableTask? {
let taskManagedObjects: [IterableTaskManagedObject] = try CoreDataUtil.findSortedEntities(context: managedObjectContext,
entity: PersistenceConst.Entity.Task.name,
column: PersistenceConst.Entity.Task.Column.scheduledAt,
ascending: true,
limit: 1)
return taskManagedObjects.first.map(PersistenceHelper.task(from:))
try performAndWait {
let taskManagedObjects: [IterableTaskManagedObject] = try CoreDataUtil.findSortedEntities(context: managedObjectContext,
entity: PersistenceConst.Entity.Task.name,
column: PersistenceConst.Entity.Task.Column.scheduledAt,
ascending: true,
limit: 1)
return taskManagedObjects.first.map(PersistenceHelper.task(from:))
}
}

func findTask(withId id: String) throws -> IterableTask? {
guard let taskManagedObject = try findTaskManagedObject(id: id) else {
return nil
Expand Down Expand Up @@ -179,6 +181,10 @@ struct CoreDataPersistenceContext: IterablePersistenceContext {
managedObjectContext.performAndWait(block)
}

func performAndWait<T>(_ block: () throws -> T) throws -> T {
try managedObjectContext.performAndWait(block)
}

private let managedObjectContext: NSManagedObjectContext
private let dateProvider: DateProviderProtocol

Expand Down
2 changes: 2 additions & 0 deletions swift-sdk/Internal/IterablePersistence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ protocol IterablePersistenceContext {
func perform(_ block: @escaping () -> Void)

func performAndWait(_ block: () -> Void)

func performAndWait<T>(_ block: () throws -> T) throws -> T
}

protocol IterablePersistenceContextProvider {
Expand Down
6 changes: 4 additions & 2 deletions swift-sdk/Internal/IterableTaskRunner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,10 @@ class IterableTaskRunner: NSObject {

private func deleteTask(task: IterableTask) {
do {
try persistenceContext.delete(task: task)
try persistenceContext.save()
try persistenceContext.performAndWait {
try persistenceContext.delete(task: task)
try persistenceContext.save()
}
} catch let error {
ITBError(error.localizedDescription)
healthMonitor.onDeleteError(task: task)
Expand Down
18 changes: 10 additions & 8 deletions swift-sdk/Internal/IterableTaskScheduler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ class IterableTaskScheduler {
do {
let data = try JSONEncoder().encode(apiCallRequest)

try persistenceContext.create(task: IterableTask(id: taskId,
name: apiCallRequest.getPath(),
type: .apiCall,
scheduledAt: scheduledAt ?? dateProvider.currentDate,
data: data,
requestedAt: dateProvider.currentDate))
try persistenceContext.save()

try persistenceContext.performAndWait {
try persistenceContext.create(task: IterableTask(id: taskId,
name: apiCallRequest.getPath(),
type: .apiCall,
scheduledAt: scheduledAt ?? dateProvider.currentDate,
data: data,
requestedAt: dateProvider.currentDate))
try persistenceContext.save()

}
notificationCenter.post(name: .iterableTaskScheduled, object: self, userInfo: nil)
} catch let error {
healthMonitor.onScheduleError(apiCallRequest: apiCallRequest)
Expand Down
13 changes: 13 additions & 0 deletions swift-sdk/Internal/Pending.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,19 @@ extension Pending {
}
}

extension Pending {
static func inBackgroundThread<Value, Failure>(_ block: @escaping () -> Pending<Value, Failure>) -> Pending<Value, Failure> {
let fulfill = Fulfill<Void, Failure>()
DispatchQueue.global(qos: .background).async {
fulfill.resolve(with: ())
}

return fulfill.flatMap { _ in
block()
}
}
}

// This class takes the responsibility of setting value for Pending
class Fulfill<Value, Failure>: Pending<Value, Failure> where Failure: Error {
public init(value: Value? = nil) {
Expand Down
132 changes: 77 additions & 55 deletions swift-sdk/Internal/RequestHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,11 @@ class RequestHandler: RequestHandlerProtocol {
func updateCart(items: [CommerceItem],
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().updateCart(items: items,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().updateCart(items: items,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
Expand All @@ -102,13 +104,15 @@ class RequestHandler: RequestHandlerProtocol {
templateId: NSNumber?,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().trackPurchase(total,
items: items,
dataFields: dataFields,
campaignId: campaignId,
templateId: templateId,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().trackPurchase(total,
items: items,
dataFields: dataFields,
campaignId: campaignId,
templateId: templateId,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
Expand All @@ -119,24 +123,28 @@ class RequestHandler: RequestHandlerProtocol {
dataFields: [AnyHashable: Any]?,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().trackPushOpen(campaignId,
templateId: templateId,
messageId: messageId,
appAlreadyRunning: appAlreadyRunning,
dataFields: dataFields,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().trackPushOpen(campaignId,
templateId: templateId,
messageId: messageId,
appAlreadyRunning: appAlreadyRunning,
dataFields: dataFields,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
func track(event: String,
dataFields: [AnyHashable: Any]?,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().track(event: event,
dataFields: dataFields,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().track(event: event,
dataFields: dataFields,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
Expand All @@ -154,11 +162,13 @@ class RequestHandler: RequestHandlerProtocol {
inboxSessionId: String?,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().trackInAppOpen(message,
location: location,
inboxSessionId: inboxSessionId,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().trackInAppOpen(message,
location: location,
inboxSessionId: inboxSessionId,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
Expand All @@ -168,12 +178,14 @@ class RequestHandler: RequestHandlerProtocol {
clickedUrl: String,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().trackInAppClick(message,
location: location,
inboxSessionId: inboxSessionId,
clickedUrl: clickedUrl,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().trackInAppClick(message,
location: location,
inboxSessionId: inboxSessionId,
clickedUrl: clickedUrl,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
Expand All @@ -184,40 +196,48 @@ class RequestHandler: RequestHandlerProtocol {
clickedUrl: String?,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().trackInAppClose(message,
location: location,
inboxSessionId: inboxSessionId,
source: source,
clickedUrl: clickedUrl,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().trackInAppClose(message,
location: location,
inboxSessionId: inboxSessionId,
source: source,
clickedUrl: clickedUrl,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
func track(inboxSession: IterableInboxSession,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().track(inboxSession: inboxSession,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().track(inboxSession: inboxSession,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
func track(inAppDelivery message: IterableInAppMessage,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().track(inAppDelivery: message,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().track(inAppDelivery: message,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
func inAppConsume(_ messageId: String,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().inAppConsume(messageId,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().inAppConsume(messageId,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

@discardableResult
Expand All @@ -227,12 +247,14 @@ class RequestHandler: RequestHandlerProtocol {
inboxSessionId: String?,
onSuccess: OnSuccessHandler?,
onFailure: OnFailureHandler?) -> Pending<SendRequestValue, SendRequestError> {
chooseRequestProcessor().inAppConsume(message: message,
location: location,
source: source,
inboxSessionId: inboxSessionId,
onSuccess: onSuccess,
onFailure: onFailure)
Pending<SendRequestValue, SendRequestError>.inBackgroundThread {
self.chooseRequestProcessor().inAppConsume(message: message,
location: location,
source: source,
inboxSessionId: inboxSessionId,
onSuccess: onSuccess,
onFailure: onFailure)
}
}

func getRemoteConfiguration() -> Pending<RemoteConfiguration, SendRequestError> {
Expand Down
Loading

0 comments on commit 2c0527f

Please sign in to comment.