nburk nburk - 8 months ago 24
iOS Question

Automatic UI updates with Apollo in Swift not working

I have the following setup for a small Apollo iOS app where I display a list of conferences in a table view and want to be able to add a conference to the list:

GraphQL:

query AllConferences {
allConferences {
...ConferenceDetails
}
}

mutation CreateConference($name: String!, $city: String!, $year: String!) {
createConference(name: $name, city: $city, year: $year) {
...ConferenceDetails
}
}

fragment ConferenceDetails on Conference {
id
name
city
year
attendees {
...AttendeeDetails
}
}

fragment AttendeeDetails on Attendee {
id
name
conferences {
id
}
}


ConferencesTableViewController:

class ConferencesTableViewController: UITableViewController {

var allConferencesWatcher: GraphQLQueryWatcher<AllConferencesQuery>?

var conferences: [ConferenceDetails] = [] {
didSet {
tableView.reloadData()
}
}

deinit {
allConferencesWatcher?.cancel()
}

override func viewDidLoad() {
super.viewDidLoad()

allConferencesWatcher = apollo.watch(query: AllConferencesQuery()) { result, error in
print("Updating conferences: ", result?.data?.allConferences)
guard let conferences = result?.data?.allConferences else {
return
}
self.conferences = conferences.map { $0.fragments.conferenceDetails }
}
}

// ...
// standard implementation of UITableViewDelegate
// ...

}


AddConferenceViewController:

class AddConferenceViewController: UIViewController {

// ... IBOutlets

@IBAction func saveButtonPressed() {
let name = nameTextField.text!
let city = cityTextField.text!
let year = yearTextField.text!

apollo.perform(mutation: CreateConferenceMutation(name: name, city: city, year: year)) { result, error in
if let _ = result?.data?.createConference {
self.presentingViewController?.dismiss(animated: true)
}
}

}
}


I also implemented
cacheKeyForObject
in AppDelegate like so:

apollo.cacheKeyForObject = { $0["id"] }


My question is whether it is possible to benefit from automatic UI updates with this setup? Currently when the
CreateConferenceMutation
is performed, the table view is not updated. Am I missing something or am I hitting the limitation that is mentioned in the docs:


In some cases, just using
cacheKeyFromObject
is not enough for your application UI to update correctly. For example, if you want to add something to a list of objects without refetching the entire list, or if there are some objects that to which you can’t assign an object identifier, Apollo cannot automatically update existing queries for you.

Answer Source

This is indeed a limitation of automatic UI updates. Although Apollo uses cacheKeyFromObject to match objects by ID, and this covers many common cases, it can't automatically update lists of objects.

In your schema, there is no way for Apollo to know that a newly added conference should be added to the allConferences list. All it knows is that allConferences returns a list of conference objects, but these could be arbitrarily selected and ordered.

So in cases like these, you will have to refetch the query from the server yourself, or change the mutation result to include the updated list.

Another option would be to manually add the new conference to the list in the client store. For this, the next version of Apollo iOS will include a manual update option similar to updateQueries in the Apollo JavaScript client.