Monday, 26 January 2015

Core Data Relationships and Fetching

Core Data Relationships and Fetching

I hope you have learned about NSManagedObject and how easy it is to create, read, update, and delete records using Core Data. However, I didn't mention relationships in that discussion. Aside from a few caveats you need to be aware of, relationships are just as easy to manipulate as attributes. In this article, we will focus on relationships and we'll also continue our exploration of NSFetchRequest.
Relationships are, just like attributes, accessed using key-value coding. Remember that the data model we created earlier in this series defines a Person entity and an Address entity. A person is linked to one or more addresses and an address is linked to one or more persons. This is amany-to-many relationship.
To fetch the addresses of a person, we simply invoke valueForKey: on the person, an instance of NSManagedObject, and pass in addresses as the key. Note thataddresses is the key we defined in the data model. What type of object do you expect? Most people new to Core Data expect a sorted NSArray, but Core Data returns an NSSet, which is unsorted. Working with NSSet has its advantages as you'll learn later.
Enough with the theory, open the project from the previous article or clone it fromGitHub. Let's start by creating a person and then link it to an address. To create a person, update the application:didFinishLaunchingWithOptions: method as shown below.
This should look familiar if you've read the previous article. Creating an address looks similar as you can see below.
Because every attribute of the Address entity is marked as optional, we don't need to assign a value to each attribute. In the above example, we only set the record'sstreet and city attributes.
To link the newAddress to the newPerson, we invoke setValue:forKey:, passing in addresses as the key. The value that we pass in is a NSSet that containsnewAddress. Take a look at the following code block for clarification.
We call save: on the managed object context of the newPerson object to propagate the changes to the persistent store. Remember that calling save: on a managed object context saves the state of the managed object context. This means that newAddress is also written to the backing store as well as the relationships we just defined.
You may be wondering why we didn't link newPerson to newAddress, because we did define an inverse relationship in our data model. Core Data creates this relationship for us. If a relationship has an inverse relationship, then Core Data takes care of this automatically. You can verify this by asking the newAddress object for its persons.
Updating a relationship isn't difficult either. The only caveat is that we need to add or remove elements from the immutable NSSet instance Core Data hands to us. To make this task easier, however, NSManagedObject declares a convenience method mutableSetValueForKey:, which returns an NSMutableSet object. We can then simply add or remove an item from the collection to update the relationship.
Take a look at the following code block in which we create another address and associate it with newPerson. We do this by invoking mutableSetValueForKey: onnewPerson and adding otherAddress to the mutable set. There is no need to tell Core Data that we've updated the relationship. Core Data keeps track of the mutable set that it gave us and updates the relationship accordingly.
Deleting a relationship is as simple as invoking setValue:forKey:, passing innil as the value and the name of the relationship as the key. This unlinks every address from newPerson.
Even though our data model doesn't define a one-to-one relationship, you've learned everything you need to know to work with this type of relationship. Working with a one-to-one relationship is identical to working with attributes. The only difference is that the value you get back from valueForKey: and the value you pass to setValue:forKey: is an NSManagedObject instance.
Let's update our data model to illustrate this. Open Core_Data.xcdatamodeld and select the Person entity. Create a new relationship and name it spouse. Set the Person entity as the destination and set the spouse relationship as the inverse relationship.



As you can see, it's perfectly possible to create a relationship in which the destination of the relationship is the same entity as the entity that defines the relationship. Also note that we always set the inverse of the relationship. As thedocumentation states, there are very few situations in which you would want to create a relationship that doesn't have an inverse relationship.
Do you know what will happen if you were to build and run the application? That's right, the application would crash. Because we changed the data model, the existing store, a SQLite database in this example, is no longer compatible with the data model. To remedy this, remove the application from your device or iOS Simulator and run the application. Don't worry though, we'll solve this problem more elegantly in a future installment using migrations.
If you can run the application without problems, then it's time for the next step. Head back to the application delegate class and add the following code block.
To set anotherPerson as the spouse of newPerson, we invokesetValue:forKey: on newPerson and pass in anotherPerson and @"spouse"as the arguments. We can achieve the same result by invoking setValue:forKey:on anotherPerson and passing in newPerson and @"spouse" as the arguments.
Let's finish with a look at one-to-many relationships. Open Core_Data.xcdatamodeld, select the Person entity, and create a relationship named children. Set the destination to Person , set the type to To Many, and leave the inverse relationship empty for now.



Create another relationship named father, set the destination to Person, and set the inverse relationship to children. This will automatically populate the inverse relationship of the children relationship we left blank a moment ago. We've now created a one-to-many relationship, that is, a father can have many children, but a child can only have one father.



Head back to the application delegate and add the following code block. We create another Person record, set its attributes, and set it as a child of newPerson by asking Core Data for a mutable set for the key children and adding the new record to the mutable set.
The following code block accomplishes the same result by setting the fatherattribute of anotherChildPerson. The result is that newPerson becomes the father of anotherChildPerson and anotherChildPerson becomes a child ofnewPerson.
The data model of our sample application has grown quite a bit in terms of complexity. We've created one-to-one, one-to-many, and many-to-many relationships. We've seen how easy it is to create records, including relationships. However, if we also want to be able to pull that data from the persistent store, then we need to know more about fetching. Let's start with a simple example in which we see how to sort the results returned by a fetch request.
To sort the records we get back from the managed object context, we use theNSSortDescriptor class. Take a look at the following code snippet.
We initialize a fetch request by passing in the entity that we're interested in, Person. We then create an NSSortDescriptor object by invoking sortDescriptorWithKey:ascending:, passing in the attribute of the entity we'd like to sort by, first, and a boolean indicating whether the records need to be sorted in ascending or descending order.
We tie the sort descriptor to the fetch request by invoking setSortDescriptors: on the fetch request, passing in an array that includes the sort descriptor. Because setSortDescriptors: accepts an array, it is possible to pass in more than one sort descriptor. We'll take a look at this option in a moment.
The rest of the code block should look familiar. The fetch request is passed to the managed object context, which executes the fetch request when we invoke executeFetchRequest:error:. It's important to always pass in a pointer to an NSError object to know what went wrong if the execution of the fetch request fails.
Run the application and inspect the output in Xcode's console. The output should look similar to what is shown below. As you can see, the records are sorted by their first name.
If you see duplicates in the output, then make sure to comment out the code we wrote earlier to create the records. Every time you run the application, the same records are created, resulting in duplicate records.
As I mentioned earlier, it's possible to combine multiple sort descriptors. Let's sort the records by their last name and age. We first set the key of the first sort descriptor to last. We then create another sort descriptor with a key of age and add it to the array of sort descriptors that we pass to setSortDescriptors:.
The output shows that the order of the sort descriptors in the array is important. The records are first sorted by their last name and then by their age.
Advertisement
Sort descriptors are great and easy to use, but predicates are what really makes fetching powerful in Core Data. While sort descriptors tell Core Data how the records need to be sorted, predicates tell it what records you're interested in. The class we'll be working with is NSPredicate.
Let's start by fetching every member of the Doe family. This is very easy to do and the syntax will remind some of you of SQL.
We haven't changed much apart from creating an NSPredicate object by invokingpredicateWithFormat: and tying the predicate to the fetch request by passing it as an argument of a setPredicate: call. The idea behindpredicateWithFormat: is similar to stringWithFormat: in that it accepts a variable number of arguments.
Note that  the predicate format string uses %K for the property name and %@ for the value. As stated in the Predicate Programming Guide, %K is a variable argument substitution for a key path while %@ is a variable argument substitution for an object value. This means that the predicate format string of our example evaluates to last == "Doe".
If you run the application one more time and inspect the output in Xcode's console, you should see the following result:
There are many operators we can use for comparison. In addition to = and ==, which are identical as far as Core Data is concerned, there's also >= and =>,<= and =>, != and <>, and > and <. I encourage you to experiment with these operators to learn how they affect the results of the fetch request.
The following predicate illustrates how we can use the >= operator to only fetch Person records with an age attribute greater than 30.
We also have operators for string comparison, CONTAINS, LIKE, MATCHES,BEGINSWITH, and ENDSWITH. Let's fetch every Person record whose nameCONTAINS the letter j.
If you run the application now, the array of results will be empty since the string comparison is case sensitive by default. We can change this by adding a modifier like so:
You can also create compound predicates using the keywords AND, OR, and NOT. In the following example, we fetch every person whose first name contains the letterj and is younger than 30.
Predicates also make it very easy to fetch records based on their relationship. In the following example, we fetch every person whose father's name is equal to Bart.
The above predicate works as expected, because %K is a variable argument substitution for a key path, not just a key.
What you need to remember is that predicates enable you to query the backing store without you knowing anything about the store. Even though the syntax of the predicate format string is reminiscent of SQL in some ways, it doesn't matter if the backing store is a SQLite database or an in-memory store. This is a very powerful concept that isn't unique to Core Data. Rails's Active Record is another fine example of this paradigm.
There is much more to predicates than what I've shown you in this article. If you'd like to learn more about predicates, I suggest you take a peak at Apple's Predicate Programming Guide. We'll also work more with predicates in the next few articles of this series.
We now have a good grasp of the basics of Core Data and it's time to start working with the framework by creating an application that leverages its power. In the next article, we meet another important class of the Core Data framework,NSFetchedResultsController. This class will help us manage a collection of records, but you'll learn that it does quite a bit more than that.

Tuesday, 4 November 2014

Variable-Sized Items in UICollectionView

Variable-Sized Items in UICollectionView

UICollectionView was added last year with iOS 6 and to this date I had no real chance to get acquainted with it since most of my apps were still supporting iOS 5. Doing a fresh app only supporting the latest iOS version finally allowed me to dig into it and share the journey with you.
The special scenario we want to look at today is how we could configure variable-sized collection view cells for items like tags. We want to have the cells adjust their size automatically based on the tag string and ideally we don’t want to have to write any layout code for determine the needed sizes.
Please forgive if the following has a few places where I stumbled. It is these temporary snags that I believe you learn from the most, so I left them in the final article.

Basic Training

We start out with a fresh project from the “Single View Application” template. On the ViewController.xib we add a UICollectionView to fill the entire view.
Adding a UICollectionView
The Collection View – identical to it’s grandfather UITableView – has a delegate and a dataSource outlet which we connect to “File’s Owner”, which is the ViewController class. This puts our view controller also in charge of the collection view’s contents and interactions. We also need an outlet for a reference to the collection view, so we add that with the assistant editor. The outlet property we add to ViewController.h can be weak because being a subview of the ViewController’s view means it is also sufficiently retained.

Cells

For the content we need to design a prototype cell. If you’re using the collection view in a view controller’s XIB you have to register it for use in the collection view in code. If you add a collection view inside a storyboard you create prototype cells inside the collection view’s area and thus won’t have to register the cell identifier. Since we started without a story board, let’s proceed with the manual steps.
We create a prototype cell in Interface Builder, give it a white background, add a UILabel and add some constraints to link the edges with the superview’s edges, 5 points distance. From the Editor menu we choose “Size to Fit Content” for the Label because we want the label text to determine the item size. We’ll see later if that works as we think.
Adding the Prototype Cell
We set the Identifier to “TagCell” so that we can reference our design.
Here we hit the first snag. If you are not using story boards there is no way to get the collection view to use a Collection View Cell from the same NIB file. These are the two methods available for registering a cell design:
  • – registerClass:forCellWithReuseIdentifier:
  • – registerNib:forCellWithReuseIdentifier:
The first instantiates a certain class to be used for a given reuse identifier. This we would use if we created the cell’s view hierarchy entirely in code. The second requires a NIB and thus requires that the cell needs to be the only item in this NIB. This is the second important reason why it is way less work using Collection Views with storyboards, to avoid this hassle.
To remedy this we create a new “empty Interface Builder Document”, which we name TagCollectionViewCell. Thankfully we can simply CMD+X the cell design from one ViewController NIB and CMD+V it into the empty document.
Cell Design moved to own NIB
Next we’ll try to register the cell for use in the collection view and want to see a number of items displayed.

Registering the Cell Design

We need to register the NIB to be used whenever we want a cell for identifier “TagCell”, so we add the following to ViewController.m in the viewDidLoad section.
UINib *cellNib = [UINib nibWithNibName:@"TagCollectionViewCell" bundle:nil];
[self.collectionView registerNib:cellNib forCellWithReuseIdentifier:@"TagCell"];
We only need to implement more methods belonging to the UICollectionViewDataSource protocol to get something to show.
#pragma mark - UICollectionView
 
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
   return 100;
}
 
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
   UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TagCell" forIndexPath:indexPath];
 
   return cell;
}
Launching the app now, we see 100 items all using our – probably not award-winning – cell design.
Cells showing up
The first observation to make is that all cells have the same size, 50×50 points to be exact. This comes from us not changing the default values in Interface Builder.
UICollectionView Cell Defaults
But before we proceed to giving the cells individual sizes, let’s first improve the design a bit. We remove the white background, add a new TagCollectionViewCell class and specify this as the cell’s class.

Prettier Cells

Instead of relying on iOS to fill a rectangle with the cell’s background color, instead we want to have a rounded rect around our labels.
- (void)drawRect:(CGRect)rect
{
   // inset by half line width to avoid cropping where line touches frame edges
   CGRect insetRect = CGRectInset(rect, 0.5, 0.5);
   UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:rect.size.height/2.0];
 
   // white background
   [[UIColor whiteColor] setFill];
   [path fill];
 
   // red outline
   [[UIColor redColor] setStroke];
   [path stroke];
}
Note: You need to set the cell’s contentMode to redraw because otherwise a modification of its size would not trigger a redraw of this good looking background.
This results in the following look, with parts of the black background shining through. Note that we need to inset the outline drawing slightly because otherwise Quartz would clip the red circle on the outside sections where it touches the view’s frame.
New Background
The next challenge will be to size the cells appropriately for the current Label contents.

Individual Item Sizing

Of course we are not content with using 1) a static size for all items and b) want to have the size automatically adjust to the contents of our cells. Remember that we specified for the cell’s label to use the text size to size it. The edges of the background view are all distanced 5 points away from the edges of the label. So it would be awesome if there was a method to retrieve a cells actual size. Then it would be even more awesome to tell the collection view to use these individual sizes for the items.
A quick test in LLDB shows that the cell returns an intrinsicContentSize of -1, -1 which is the same as not defined. We add an outlet for the label to the TagCollectionViewCell’s header and then we find that conveniently the UILabel returns the size needed by the currently set text/font.
Since we know the amount of space around the constraints we can add that to the label’s intrinsic content size to derive the cell’s size.
// cache for margins configured via constraints in XIB
static CGSize _extraMargins = {0,0};
 
@implementation TagCollectionViewCell
 
- (CGSize)intrinsicContentSize
{
   CGSize size = [self.label intrinsicContentSize];
 
   if (CGSizeEqualToSize(_extraMargins, CGSizeZero))
   {
      // quick and dirty: get extra margins from constraints
      for (NSLayoutConstraint *constraint in self.constraints)
      {
         if (constraint.firstAttribute == NSLayoutAttributeBottom || constraint.firstAttribute == NSLayoutAttributeTop)
         {
            // vertical spacer
            _extraMargins.height += [constraint constant];
         }
         else if (constraint.firstAttribute == NSLayoutAttributeLeading || constraint.firstAttribute == NSLayoutAttributeTrailing)
         {
            // horizontal spacer
            _extraMargins.width += [constraint constant];
         }
      }
   }
 
   // add to intrinsic content size of label
   size.width += _extraMargins.width;
   size.height += _extraMargins.height;
 
   return size;
}
 
@end
This approach gets the actual margin widths from the constrains we have set in Interface Builder. This allows us to keep tweaking the amounts in IB without having to change some constant or #define in code. Since we don’t plan to modify any constraints, we’re lazily setting the static _extraMargins the first time this method is called, as those are constant.
Now the tricky part is dequeue a cell for reuse from the collection view to get the size. This is tricky because the method that calls our dataSource’s collectionView cellForItemAtIndexPath: is also calling collectionView:layout:sizeForItemAtIndexPath:. This prohibits us from dequeuing a cell in the latter method because that would lead to an endless loop.
Some people get their data from a model object and then have a class method on the cell to calculate the needed size. However this doesn’t make use of any constraints which we want to be able to configure in Interface Builder. The smartest solution I could come up with for this chicken and egg problem is to have a separate instance of one such cell as a template and then use its freshly overridden intrinsicContentSize method.
@implementation ViewController
{
   TagCollectionViewCell *_sizingCell;
}
 
- (void)viewDidLoad
{
   [super viewDidLoad];
 
   UINib *cellNib = [UINib nibWithNibName:@"TagCollectionViewCell" bundle:nil];
   [self.collectionView registerNib:cellNib forCellWithReuseIdentifier:@"TagCell"];
 
   // get a cell as template for sizing
   _sizingCell = [[cellNib instantiateWithOwner:nil options:nil] objectAtIndex:0];
}
 
#pragma mark - UICollectionView
 
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
   return 100;
}
 
- (void)_configureCell:(TagCollectionViewCell *)cell forIndexPath:(NSIndexPath *)indexPath
{
   if (indexPath.row%2)
   {
      cell.label.text = @"A";
   }
   else if (indexPath.row%3)
   {
      cell.label.text = @"longer";
   }
   else 
   {
      cell.label.text = @"much longer";
   }
}
 
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
   TagCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TagCell" forIndexPath:indexPath];
 
   [self _configureCell:cell forIndexPath:indexPath];
 
   return cell;
}
 
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
   [self _configureCell:_sizingCell forIndexPath:indexPath];
 
   return [_sizingCell intrinsicContentSize];
}
 
@end
Right after registering the NIB for use by the collection view I’m creating an instance of the cell and put it into my _sizingCell IVAR. To get an actual item’s cell we have a _configureCell:forIndexPath: method which we call on a dequeued reusable cell instance. For sizing we apply the same method on our sizing cell so that we get the correct intrinsicContentSize for that.

Once More With Auto Layout

When I asked my Tweeps about how I could ask a cell for how much size it would want based on the set constraints,Martin Pilkington was quick to point me towards  -systemLayoutSizeFittingSize:. At first I failed in its application, kept getting {0,0}. But after having written the above workaround I took a heart and tried it once more.
And it worked right away. All that’s needed is to replace this method:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
   [self _configureCell:_sizingCell forIndexPath:indexPath];
 
   return [_sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
}
This method determines the layout size that fits the passed size the closest. Two standard values are available, UILayoutFittingCompressedSize for getting the smallest size based on the layout constraints, UILayoutFittingExpandedSize to get the largest.
With this in please we can actually get rid of all the intrinsicContentSize stuff we wrote earlier. Also we can fully use the layout constraints to further restrict individual parts of our item cell. Say that you want the label to not go below a certain width for very short labels, and if the label gets too unwieldy then we would want to have the tail truncated.
Further Constrained
This results in the following result, proving that this is indeed what we want.
Custom Sizes
Isn’t that awesome? Let me stress that one more time: you do not need to calculate sizes in a cell class method, essentially duplicating the calculations auto layout would execute. You just ask the system .

Conclusion

In this blog post I have shown how you would create item cells for collections views coming from NIB files. An alternative – and in some aspects more convenient – method is to use the collection view in a storyboard. There the cell prototype would be positioned not in a separate XIB file, but right in the structure hierarchy of the collection view. Exploring how to get a sizing cell there is left as an exercise for the reader.
We found that UILabel conveniently exposes an intrinsicContentSize which is used by auto layout to size it if you specify that in Interface Builder. We explored a technique where we would use a cell instance as template for determine the optimal item size. Then we went one step further and employed a method that actually tells us the perfect size based on all layout constrains and intrinsic sizes.
The code for this example is available on the Cocoanetics Examples repo on GitHub.

203 Favorite JavaScript Utilities

https://1loc.dev/