Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data, CloudKit - Remove Duplicates Function deletes more than expected

I setup a little test app for CloudKit with preloaded Objects in Core Data. When the app gets installed on multiple devices, CloudKit collects duplicate data, due to the preload. I wrote some duplicate check functions to delete duplicates and relink relationships.

My test app hast three Entities. Book, Author, Pub(Publisher).

  • A Book has a relationship to many Authors and to one Publisher.
  • Publishers and Authors have relationships to many Books.

My duplicate checks actually work beside the fact that my Books have only one remaining Author (the first one) after the functions perform . It's probably something very obvious but I'm scratching my head for a while now..

Does someone see something that could cause that?

Here is a comparison screenshot: (The data is fictive, I know that these are no books :D ) enter image description here

These are the three functions I perform accordingly to the duplicate data I detect.

private func remove(duplicatedBooks: [Book], winner: Book, performingContext: NSManagedObjectContext) {
    for book in duplicatedBooks {
        // Update Authors
        if let authors = book.authors {
            for case let author as Author in authors {
                if let authorsBooks = author.books as? NSMutableSet {
                    if authorsBooks.contains(book) {
                        authorsBooks.remove(book)
                        authorsBooks.add(winner)
                    }
                }
            }
        }
        // Update Publishers
        if let publisherBooks = book.publisher?.books as? NSMutableSet {
            if publisherBooks.contains(book) {
                publisherBooks.remove(book)
                publisherBooks.add(winner)
            }
        }
        performingContext.delete(book)
    }
}

private func remove(duplicatedAuthors: [Author], winner: Author, performingContext: NSManagedObjectContext) {
    for author in duplicatedAuthors {
        // Update Books
        if let books = author.books {
            for case let book as Book in books {
                if let bookAuthors = book.authors as? NSMutableSet {
                    if bookAuthors.contains(author) {
                        bookAuthors.remove(author)
                        bookAuthors.add(winner)
                    }
                }
            }
        }
        performingContext.delete(author)
    }
}

private func remove(duplicatedPublishers: [Pub], winner: Pub, performingContext: NSManagedObjectContext) {
    for pub in duplicatedPublishers {
        // Update Books
        if let books = pub.books {
            for case let book as Book in books {
                book.publisher = winner
            }
        }
        performingContext.delete(pub)
    }
}

That's an example of how I get to the remove functions. The duplicateBooks in this case come from a fetch request that compared the names of the books and therefore decided that they are duplicates.

if var duplicateBooks = duplicateData as? [Book] {
    let winner = duplicateBooks.first!
    duplicateBooks.removeFirst()
    remove(duplicatedBooks: duplicateBooks, winner: winner, performingContext: performingContext)
}
like image 906
wildcard Avatar asked Mar 15 '26 08:03

wildcard


1 Answers

Yep, it was as obvious and stupid as I thought.

Instead of removing the duplicate and adding the remaining object as a relationship, I only removed and added it to the set.

It was a coincidence that only the first author remained.

Instead of

for case let author as Author in authors {
    if let authorsBooks = author.books as? NSMutableSet {
        if authorsBooks.contains(book) {
            authorsBooks.remove(book)
             authorsBooks.add(winner)
        }
    }

}

It should have been

if let authors = book.authors as? Set<Author> {
    for author in authors {
        author.removeFromBooks(book)
        author.addToBooks(winner)
    }
}
like image 187
wildcard Avatar answered Mar 16 '26 20:03

wildcard



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!