Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Download and Save Async Data in Multiple Threads #3

Closed
kheinrich188 opened this issue Dec 23, 2014 · 2 comments
Closed

Download and Save Async Data in Multiple Threads #3

kheinrich188 opened this issue Dec 23, 2014 · 2 comments

Comments

@kheinrich188
Copy link

hey Marko,

i have an issue and i-m thinking that i doing something wrong with saving or creating/mapping CoreDataModels while getting data from a Webservice.
Situation:

  1. I download from a Webservice all users cardboxes and serialize the Json into an DTO Object and then map the DTO to the CoreData DBOCardBoxModel
  2. ForEach downloaded and mapped Cardbox download the Cards async
    2.1) When the Api Call is finished, serialize the Json into a DTO Object
    2.1.1) Then i want create a new CoreDataModel when the DTO.cardNbr not exists, else get first and map then the properties from the dto to dbo
    2.1.2) In the Mapper.Map(dto->dbo) i want to map all the properties and set relations, so in example a card can have multiple choice answers, so i want to create them and add this relation to the card

Exception throw at the firstOrCreateWithAttributes methode when my call was for example 25 times called, when it called 5 times the exception didnt throw.
I think it is because the NSManagementObjectContext is not on each thread, how can i handle this?

Here is some of my Code:
I using the following extensions:

Another question from me is where i place my the Save funktion? I¥m not shure about this.
1.) The Api Call that will call 25 Times: (the self.saveDB will call the saveAndWait(default) funktion)

public func ActiveCardBoxesById(dboCardBoxes : Array<String>){
        //let operationQueue = NSOperationQueue()
        //operationQueue.maxConcurrentOperationCount = dboCardBoxes.count
        for cardBox in dboCardBoxes {
            if self.CanSendRequest() {
                var request = self.CreateBaseRequest()
                request.requestSerializer.headers["clientid"] = self.UniqueId
                request.GET("/cardbox/\(cardBox)/cards", parameters: nil, success: {(response: HTTPResponse) -> Void in
                    if (response.responseObject != nil) {
                        //TODO
                        let dtoCards = Cards(JSONDecoder(response.responseObject!))
                        for dtoCard in dtoCards.cards! {
                            var dboCard = DBOCard.firstOrCreateWithAttribute("cardNbr", value: dtoCard.cardNbr!) as DBOCard
                            Mapper.Map(dtoCard, dbo: dboCard)
                        }

                        self.dataDelegate?.ConfirmLastSyncCardBox!()
                        self.logger.info("ConfirmLastSyncCardBox \(cardBox)")
                    }
                    }, failure: {(error: NSError, response: HTTPResponse?) -> Void in
                        self.logger.error("Getting ActiveCardboxesById throw error : \(error)")
                        self.baseDelegate?.ErrorInWebService(error)
                })


            }
            else {
                logger.warn("Can not communicate with API")
                self.baseDelegate?.ErrorSendingRequest()
            }
        }
        self.saveDB()

    }

2.) This is the Mapper.Map methode: (the self.saveDB will call the saveAndWait(default) funktion)

class func Map(dto : DTOCard, dbo : DBOCard) -> DBOCard {
        if let cardNbr = dto.cardNbr {
            dbo.setValue(cardNbr, forKey: "cardNbr")
        }
        if let cardBoxNbr = dto.cardBoxNbr {
            dbo.setValue(cardBoxNbr, forKey: "cardBoxNbr")
        }
        if let daystowait = dto.daystowait {
            dbo.setValue(daystowait, forKey: "daystowait")
        }
        if let delay = dto.delay {
            dbo.setValue(delay, forKey: "delay")
        }
        if let favorite = dto.favorite {
            dbo.setValue(favorite, forKey: "favorite")
        }
        if let knownInPlannedRow = dto.knownInPlannedRow {
            dbo.setValue(knownInPlannedRow, forKey: "knownInPlannedRow")
        }
        if let knownInRow = dto.knownInRow {
            dbo.setValue(knownInRow, forKey: "knownInRow")
        }
        if let lastPlannedPlayed = dto.lastPlannedPlayed {
            dbo.setValue(lastPlannedPlayed, forKey: "lastPlannedPlayed")
        }
        if let lastPlayed = dto.lastPlayed {
            dbo.setValue(lastPlayed, forKey: "lastPlayed")
        }
        if let multipleChoice = dto.multipleChoice {
            dbo.setValue(multipleChoice, forKey: "multipleChoice")
        }
        if let timesKnown = dto.timesKnown {
            dbo.setValue(timesKnown, forKey: "timesKnown")
        }
        if let timesNotKnown = dto.timesNotKnown {
            dbo.setValue(timesNotKnown, forKey: "timesNotKnown")
        }
        if let selectedForLearning = dto.selectedForLearning {
            dbo.setValue(selectedForLearning, forKey: "selectedForLearning")
        }
        if let delete = dto.delete {
            dbo.setValue(delete, forKey: "delete")
        }
        if let answer = dto.answer {
            dbo.setValue(answer, forKey: "answer")
        }
        if let answerImage = dto.answerImage {
            dbo.setValue(answerImage, forKey: "answerImage")
        }
        if let answerPureText = dto.answerPureText {
            dbo.setValue(answerPureText, forKey: "answerPureText")
        }
        if let question = dto.question {
            dbo.setValue(question, forKey: "question")
        }
        if let questionImage = dto.questionImage {
            dbo.setValue(questionImage, forKey: "questionImage")
        }
        if let questionPureText = dto.questionPureText {
            dbo.setValue(questionPureText, forKey: "questionPureText")
        }

        if let dtoMultipleChoices = dto.multipleChoiceCards {
            var counter = 1
            for dtoMultipleChoice in dtoMultipleChoices {
                var acessableOrderIndex = counter
                if let orderIndex = dtoMultipleChoice.orderIndex {
                    acessableOrderIndex = orderIndex
                }
                //TODO LOOK FIRST FOR CARDID AND ORDERINDEX
                let predicate = NSPredicate(format: "cardId = \(dtoMultipleChoice.cardId! + counter) AND orderIndex = \(acessableOrderIndex)")
                var mappedMultipleChoice : DBOMultipleChoice
                if let existingDboMultipleChoice = DBOMultipleChoice.firstWithPredicate(predicate!) as? DBOMultipleChoice {
                    mappedMultipleChoice = self.Map(dtoMultipleChoice, dbo: existingDboMultipleChoice)
                    mappedMultipleChoice.setValue(acessableOrderIndex, forKey: "orderIndex")
                }
                else {
                    var dboMultipleChoice = DBOMultipleChoice.firstOrCreateWithAttribute("cardId", value: dtoMultipleChoice.cardId! + counter) as DBOMultipleChoice
                    mappedMultipleChoice = self.Map(dtoMultipleChoice, dbo: dboMultipleChoice)
                    mappedMultipleChoice.setValue(acessableOrderIndex, forKey: "orderIndex")
                }

                dbo.addMultipleChoice(mappedMultipleChoice)
            }
            counter = 1
        }
        if let dtoCategories = dto.categories {
            for dtoCategory in dtoCategories {
                var dboCategory = DBOCategory.firstOrCreateWithAttribute("categoryId", value: dtoCategory.categoryId!) as DBOCategory
                var mappedCategory = self.Map(dtoCategory, dbo: dboCategory)
                mappedCategory.addCard(dbo)
                //add category to card
                dbo.addCategorie(mappedCategory)
                //add card to category

            }
        }
//        //get cardbox and add card
        if let dboCardBox = DBOCardBox.firstWithAttribute("cardBoxNbr", value: "\(dbo.cardBoxNbr)") as? DBOCardBox {
            //we have an cardbox
            dboCardBox.addCard(dbo)
            dbo.cardInCardBox = dboCardBox
        }
        self.save()
        return dbo
    }

Exceptions sometimes dangling with reference Dangling reference to an invalid object.=null, NSValidationErrorValue

Or
Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x7fc1aa487870> was mutated while being enumerated.'

XCode Version: 6.1

@kheinrich188
Copy link
Author

Ok i solved it and UNDERSTAND what i´m doing wrong. When you downloading ASYNC Data from Webserver and want to store it in CoreData you have to create a temporary MOC (Management Object Context) and work in the Thread with this MOC and when your data is finished loading, you can save your MOC with your BaseStoreCoordinator and thats it, changes will merge in MainThread and everything is fine 👍 Here is my fixed code snippet:

//This is really important for working with background queues and core data
                        let moc = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
                        moc.persistentStoreCoordinator = DBContext.persistentStoreCoordinator

                        let dtoCards = Cards(JSONDecoder(response.responseObject!))
                        for dtoCard in dtoCards.cards! {
                            var dboCard = DBOCard.firstOrCreateWithAttribute("cardNbr", value: dtoCard.cardNbr!,context:moc) as DBOCard
                            var mappedDboCard = Mapper.Map(dtoCard, dbo: dboCard,context: moc)
                        }
                        DBContext.saveContextAndWait(context: moc)

@tadija
Copy link
Owner

tadija commented Jan 10, 2015

Hello,

I'm sorry I didn't have time to deal with this in more details,
but I just didn't fully understand your problem, so I couldn't help very much.

As I can see now, you somehow managed to sort that stuff out, so I'll close this issue.

Cheers,
// T

@tadija tadija closed this as completed Jan 10, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants