I have the following data model:
And I'd like to have a property on the playlist object that points to its list of songs. Right now I have something like:
self.songs = [self.playlistMemberships valueForKey:#"song"];
But I'm noticing that this is very slow and core data seems to be firing off faults for each individual song. Any thoughts on how to make a single call to pull all the song info? Thanks!
PlaylistMembership just seems to get in the way -- can you relate Playlist to Song directly and cut out the PlaylistMembership? I think that'd let Core Data fetch all the songs at once rather than fetching all the memberships and then having to go back to get each song.
If you can't or prefer not to get rid of the membership entity, use setRelationshipKeyPathsForPrefetching: to specify that song should be fetched along with the membership.
Related
Im trying to find the best way to get the latest entity from a relationship with swift and core data.
For example I have a Conversation with many Messages:
Conversation Entity:
id = 1, name = "Test"
Message Entity:
id = 1, body = "Test", conversation = 1
id = 2, body = "Test", conversation = 1
In my SwiftUI View I have this FetchRequest:
#FetchRequest(sortDescriptors: []) var conversations: FetchedResults<Conversation>
What would be the best way to access the relation and get the latest result while looping over the conversations in View like this:
List(conversations) { conversation in
Text(conversation.name ?? "")
Text(conversation.messages.latest.body ?? "") // <--- Trying to do something like this
}
The best way is to store the data you need in the entity you are fetching. So in your case make a latestMessageBody property on the conversation entity. Apple provide derived attributes to automate this.
It's a common mistake to think of Core Data as a relational database, instead just think of it as a way to persist what you see on screen and design your entities based on how you want to fetch and display them. If you are making too many fetches on one screen then its time to redesign the entities.
I.e. you wouldn't want to fetch the latest message for all the converstations you want to show that is too many fetches. And you don't want to load in all the messages for every conversation just to find the latest, that has too high memory overhead. This is why Core Data offers denormalisation via the derived attributes feature.
Another point is #FetchRequest (which is powered by NSFetchedResultsController by the way) only tracks changes to the entity that is currently fetched. So if you reach into relations to show data, that view won't update when the related object changes. You can work around this by passing the object into another View marked with #ObservedObject however that doesn't work for a 1-to-many relation.
There is a neat trick using entity inheritance to allow a NSFetchedResultsController to work on multiple entities however I don't think that is suitable for this use case.
I'd like to pass to my model the newest fetched data of my core data entities, in order to have them synched.
Is this possible?
The reason is that I have many variables that have to be calculated from the data saved in core data. These values are used in my views, so they should update at the same time.
(Until now I just found a way to pass them around every time with functions, but I find this very chaotic...)
Until now:
func doSomethingWithFetchedData(fetchedData: FetchedResults<Entity>) {
//return what I need
}
Thanks!
NSFetchedResultsController Subscribing to updates for many objects matching a fetch request has been easier than subscribing to updates from a single managed object, thanks to NSFetchedResultsController. It comes with a delegate that informs us about changes to the underlying data in a structured way, because it was designed to integrate with tables and collection views
Here is a good link to start with
My app's db has a many to many relationship between a Feed object and a Tweet object. This is to keep track of which feeds every tweet belongs in. If you're familiar with Twitter, imagine the main feed, a list feed, a user profile feed, etc.
How can I make a query using an NSPredicate to get a list of Tweets that exist in a specific Feed (and, inversely, get a list of Feeds that a Tweet exists in)? It seems that queries on inverse relationships does not work in Realm, so what are my options?
If I understand your question correctly this part of the documentation should be helpful:
Inverse Relationships Links are unidirectional. So if a to-many
property Person.dogs links to a Dog instance and a to-one property
Dog.owner links to Person, these links are independent from one
another. Appending a Dog to a Person instance’s dogs property, doesn’t
automatically set the dog’s owner property to this Person. Because
manually synchronizing pairs of relationships is error prone, complex
and duplicates information, Realm exposes an API to retrieve backlinks
described below.
With inverse relationships, you can obtain all objects linking to a
given object through a specific property. For example, calling
Object().linkingObjects(_:forProperty:) on a Dog instance will return
all objects of the specified class linking to the calling instance
with the specified property.
I guess you can do something like:
//assuming your Tweet object has a property like "let feeds = List<Feed>()"
someTweet.linkingObjects(Feed.self, forProperty: "feeds") //should return feeds your Tweet is in
But still I don't think I understand your question clearly. From my point of view your first requirement:
get a list of Tweets that exist in a specific Feed
should have a straightforward solution such as having a property in your Feed object like:
let tweets = List<Tweet>()
I wish you can clarify your situation further.
I wonder if it's possible to simplify the model a bit so many-to-many isn't necessary.
My understanding of Twitter is that tweets aren't 'owned' by any feeds. They simply exist on the platform, and are referenced by any number of feeds, but don't actually belong to any specific feed.
So a model setup like this should be appropriate:
class Tweet : Object {
}
class Feed : Object {
let tweets = List<Tweet>()
}
You can do a reverse lookup on a Tweet to see if there are any feeds in which it is currently visible, and you can simply use the tweets property of Feed objects to see which tweets they're displaying
Since the linkingObjects reverse lookup method of Realm simply returns a standard Swift Array, if you did want to filter that further, you could just use the system APIs (like filter or map) to refine it further.
Otherwise, if you really do want to be able to use Realm's NSPredicate filtering system both ways, then, as messy as it is, you would need to manually have each model linking to a list of the other:
class Tweet : Object {
let feeds = List<Feed>()
}
class Feed : Object {
let tweets = List<Tweet>()
}
While it's not recommended (Since it adds additional work), it's not disallowed.
Good luck!
I'm developing my first application with CoreData and I'm struggling with a common problem I think.
Let's say I have an entity Playlist which has a many to many relationship with another entity Song and also Video. So my Playlist object looks like this
class Playlist : NSManagedObject {
var songs: NSSet
var videos: NSSet
}
Song and Video entities both have a Boolean field "removed" that I use to track which song or video has been removed from the playlist. I later call my remote API to remove it on server's side but this is for offline purpose.
What I want is to retrieve from CoreData a Playlist object with its list of songs and videos that are not removed (so those with removed = false).
One solution I see would be to get all the elements by simply fetching on the Entity Playlist and then filter out manually but I'd like to know if I can do this more elegantly using Core Data.
Welcome to Core Data. The way this works is a follows:
First fetch the playlists. They have a to-many relationship with songs no matter if they are deleted of not. The removed property is not in the Playlist entity, so it does not matter when fetching playlists.
Then get the filtered attribute set by applying a predicate:
let validSongs = aPlaylist.songs.filteredSetUsingPredicate(
NSPredicate(format: "removed = false")
)
In other words, you do not have to worry about which songs exactly Core Data is retrieving with your playlist. There are a lot of optimizations under the hood, so most likely Core Data will be using a mechanism called "faulting" to just retrieve the data it needs. There is noting else for you to do!
IMO, this is also the most "elegant" solution. To make it even more concise, you can add a method to your Playlist entity that retrieves the valid songs.
func validSongs() -> NSSet {
if !self.songs.count { return NSSet() }
return self.songs.filteredSetUsingPredicate(
NSPredicate(format: "removed = false")
)
}
And then access the valid songs with
aPlaylist.validSongs
I've instantiated a query to request a collection of all the playlists on my iPod Touch. The result, returned from the MPMediaQuery collections property, contains 43 items. These include all 17 of the PlayLists that the native Music app shows. However, the collection also contains 26 "playlists" that are actually albums from my iPod library. That represents some(but not all) of the albums on the iPod.
I've tried to see if there was an MPMediaPlaylist attribute that might distinguish the albums from actual playlists, to no avail. I looked at the sections of the collection to see if real playlists are kept in a separate sections from the albums, but there was only a single section. Finally, I looked to see if the albums have a PersistantID, which the documentation indicates is unique to playlists. All the items in the array have a PersistantID.
The Music app has no trouble displaying just the 17 actual playlists, so there must be something I'm missing. Here is the code used to build the query:
MPMediaQuery * myPlaylistsQuery = [ MPMediaQuery playlistsQuery ];
NSArray * playlists = [ myPlaylistsQuery collections ];
Here is a sample of the data that gets logged when the contents of the "playlists" array is interrogated:
=====================================================
List of playlists
.... There are 01 sections in the playlist collection
.... There are 43 MPMediaPlaylist objects in the playlist collection
Bach
.... Persistant ID: 17002820337772537981
Best of Simon and Garfunkel
.... Persistant ID: 2965021518813542622
Blue
.... Persistant ID: 11514004552991695558
Blues
.... Persistant ID: 14243796139384893728
.... (etc)
In the above listing, the entries "Bach", "Best of Simon and Garfunkel" and "Blue" represent albums. The entry "Blues" represents the first of the true playlists that I've defined on iTunes.
I suppose that I could compare the items returned from the playlist query against a the items in an albums query, and eliminate any matches. Is there a more straightforward solution?
I'm using [[MPMediaQuery playlistsQuery] collections] frequently and never have get an album returned as result. I guess that there is an issue somewhere else in your code. For example you still can customize the playlistsQuery after creating it with adding/removing parameters manually and if you change to much you could even let the query return albums instead of playlists.
Especially check the groupingType property of the query. For the playlistQuery it should be set to MPMediaGroupingPlaylist per default, but maybe you have overridden or removed this property manually for your query.
Otherwise there only remains the possibility of an bug in your devices system API or iTunes library. But this is very, very, very unlikely!