Core Data: Beyond the Basics

Jul 26 2022 · Swift 5.5, iOS 15, Xcode 13.3.1

Part 2: Advanced Core Data

15. Saving Launches with Batch Operations

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 14. Asynchronously Loading Launches Next episode: 16. Saving Launches Concurrently

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Heads up... You’re accessing parts of this content for free, with some sections shown as obfuscated text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Now that you have data from the SpaceX API, it’s time to put that data in the database. There are two techniques you’ll use together to do this: one is batch operations, which you’ll learn about in this episode, and asynchronous Core Data, which you’ll learn about in the next episode.

private func createBatchInsertLaunchRequest(from launchCollection: [SpaceXLaunchJSON]) -> NSBatchInsertRequest {

}
var index = 0
let total = launchCollection.count
let batchInsertRequest = NSBatchInsertRequest(entity:  
  SpaceXLaunch.entity(), dictionaryHandler: { dictionary in
  guard index < total else { return true }
  dictionary.addEntries(from: launchCollection[index].dictionaryValue as [AnyHashable: Any])
    index += 1
    return false
  })
return batchInsertRequest
var dictionaryValue: [String: Any] {
	[
	  "reused": reused as Any,
	  "recoveryAttempt": recoveryAttempt as Any,
	  "recovered": recovered as Any,
	  "ships": ships,
	  "id": id
	]
}
protocol BatchInsertable: Codable {
  var dictionaryValue: [String: Any] { get }
}
let taskContext = container.viewContext
let fairings = launchCollection.map { ($0.id, $0.fairings) }
let links = launchCollection.map { ($0.id, $0.links) }
var list: SpaceXLaunchList!
let fetchRequest = SpaceXLaunchList.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "title == %@", listName)
let results = try taskContext.fetch(fetchRequest)
if let fetchedList = results.first {
	list = fetchedList
}
let batchInsertRequest = createBatchInsertLaunchRequest(from: launchCollection)
if let fetchResult = try?
  taskContext.execute(batchInsertRequest),
  let batchInsertResult = fetchResult as? NSBatchInsertResult,
  let success = batchInsertResult.result as? Bool, 
  success {
	  return
	} else {
		throw LaunchError.batchInsertError
	}
private func createBatchInsertRelationshipRequest<T: BatchInsertable, E: NSManagedObject>(from relationshipCollection: [(String, T?)], for type: E.Type) -> NSBatchInsertRequest {
	var index = 0
	let total = relationshipCollection.count
	
	// Provide one dictionary at a time when the closure is called.
	let batchInsertRequest = NSBatchInsertRequest(entity: E.entity(), dictionaryHandler: { dictionary in
	  guard index < total else { return true }
	  guard let value = relationshipCollection[index].1 else { index += 1; return false }
	  dictionary.addEntries(from: value.dictionaryValue as [AnyHashable: Any])
	  index += 1
	  return false
	})
	
	return batchInsertRequest
}
let batchInsertRequest2 = createBatchInsertRelationshipRequest(from: fairings, for: SpaceXFairings.self)
if let fetchResult = try? taskContext.execute(batchInsertRequest2),
let batchInsertResult = fetchResult as? NSBatchInsertResult,
let success = batchInsertResult.result as? Bool, success {
  return
} else {
throw LaunchError.batchInsertError
}
// Setup the fairing relationships
for (id, fairing) in fairings {
	guard let fairing = fairing else { continue }
	let fairingFetchRequest = SpaceXFairings.fetchRequest()
	fairingFetchRequest.predicate = NSPredicate(format: "id == %@", argumentArray: [fairing.id])
	
	let launchFetchRequest = SpaceXLaunch.fetchRequest()
	launchFetchRequest.predicate = NSPredicate(format: "id == %@", argumentArray: [id])
	
	let returnedFairing = try taskContext.fetch(fairingFetchRequest) as [SpaceXFairings]
	let launch = try taskContext.fetch(launchFetchRequest) as [SpaceXLaunch]
	guard !returnedFairing.isEmpty, !launch.isEmpty else { continue }
	let matchedFairing = returnedFairing[0]
	let matchedLaunch = launch[0]
	matchedFairing.launch = matchedLaunch
}
try taskContext.save()
// Use a batch insert request to add the links
let batchInsertRequest3 = createBatchInsertRelationshipRequest(from: links, for: SpaceXLinks.self)
if let fetchResult = try? taskContext.execute(batchInsertRequest3),
let batchInsertResult = fetchResult as? NSBatchInsertResult,
let success = batchInsertResult.result as? Bool, success {
  return
} else {
	throw LaunchError.batchInsertError
}

// Setup the link relationships
for (id, links) in links {
	let linksFetchRequest = SpaceXLinks.fetchRequest()
	linksFetchRequest.predicate = NSPredicate(format: "id == %@", argumentArray: [links.id])
	
	let launchFetchRequest = SpaceXLaunch.fetchRequest()
	launchFetchRequest.predicate = NSPredicate(format: "id == %@", argumentArray: [id])
	
	let returnedLinks = try taskContext.fetch(linksFetchRequest) as [SpaceXLinks]
	let launch = try taskContext.fetch(launchFetchRequest) as [SpaceXLaunch]
	guard !returnedLinks.isEmpty, !launch.isEmpty else { continue }
	returnedLinks[0].launch = launch[0]
	launch[0].addToSpaceXList(list)
}
try taskContext.save()