Custom Core Data migration example

This post is a tutorial on custom Core Data migrations. There is no starter project that you can download and work on following the action steps outlined in this post. But there are action steps in this post that you can follow while developing your own app. And there is an example – my own app – of which you will see relevant parts of code and some screenshots. You can also download my app from the App Store to see what it’s all about. Throughout this post I’ll be telling you what to do, referring all the time to the example of my app. So, it’s going to be “Do this, do that. And by the way, here is how I’ve done it in my app”. 

A little bit of history

I’d been wanting to learn custom Core Data migrations for a very long time and here is why. I have an old app (written in Objective-C) which I wrote when I started learning iOS development. Actually, I was learning iOS development by writing that app. With very little previous experience in programming, I’d made some funny decisions about the data structure of the app. I released several versions with some minor changes and design improvements. Then I had some ideas on how to improve the app dramatically by adding new functionality. But in order to do that I needed to change the data structure completely. Since the change would be dramatic, I had to implement custom Core Data migration. The lightweight migration just wouldn’t do.

And because this app is just a part-time hobby for me, I allowed myself to procrastinate for months on this particular update. And one of the reasons for procrastination was a lack of good tutorials on custom Core Data migrations on the internet. There are some examples but they tend to talk about the details without giving you the overall picture of the migration process.

Here are some questions that had been bothering me before I learned custom Core Data migrations.

If I implement NSEntityMigrationPolicy classes do I have to do anything else, or will the migration occur automatically from then on? Answer: Nothing will happen automatically, you start the migration manually.

Do I have to implement all attribute mappings for all entity mappings? Answer: No, I didn’t have to implement any of those.

I use MagicalRecord, how is it going to affect the process of implementing custom migration. Answer: you don’t start using MagicalRecord until after the migration is completed

Core Data Model Versioning and Data Migration Programming Guide is talking about Core Data migrations in general. There is a section about customising the migration but it’s very short and leaves you with a lot of questions unanswered. I had to use information from several different sources on the internet to finally understand custom migrations.

Overall, the process of figuring out how to implement custom Core Data migration felt like collecting a jigsaw puzzle without knowing beforehand what the end picture would look like. So, what I intend to do with this post is to give you a bird’s eye view on the work that is needed to be done in order to implement custom Core Data migration.

I’ll use my app as an example, but this post is not so much about the little details and code snippets as it is about the overall strategy of implementing custom migrations. You can find all the code snippets you need on Stack Overflow. And I don’t talk much about theory either. This is not an in-depth guide to Core Data migrations. Hopefully, you’ve already read some theory, but maybe still need an example of how it’s done from start to finish. You will find such an example here.

As for me personally, when I was learning all this stuff, what I found myself lacking, is an overview or some king of a big picture. I hope this post will give you this big picture.

This post focuses only on custom Core Data migrations. So, before reading further, make sure you have general understanding of Core Data and lightweight migrations.

The original project

First let’s take a look at my app. We’ll see what its data structure was before and after the migration. The original app had a list of questions which you were supposed to answer on a daily basis.

This is what a question list looked like

And these are the entries in the diary

I told you that I’d made some funny decisions about the data structure of the app. Here’s what it looked like.

For the list of questions I had a class called Questions. There was only one object of that class. It had one attribute called questions of type Binary data. An archived array of NSStrings was stored there.

For daily records I had a class called Records (for some reason the name of the class was plural). It had three attributes:

  • date – I stored dates as strings that looked like this 2017-12-30. The reason I didn’t use NSDates was that at the time I wasn’t comfortable with timezones. I needed dates to always be the same after the entry was created. What if I used NSDates and a user changed the timezone? Some dates could change as a result. I figured it would be easier to store the date as a string and don’t have that problem. In the new version I fixed this, and you’ll see how later.
  • questions – an archived array of strings with question names;
  • answers – an archived array of strings with answers to the questions. I made sure in code that questions and answers arrays always had the same number of items. Question of index i had a corresponding answer in answers array, also at index i. If there was a question which had no answer, I stored [NSNull null] in the questions array in place of an answer to that question. I know, very strange decisions, but that was my first experience in app development.

Data structure after the update

The main reason for the update was to have multiple lists of questions. So, instead of one list of questions I now have several lists of question. Each list has a name and a bunch of questions. And each day record in the diary can now contain multiple entries, like this:

The lists of questions are now called templates. You can see them in a list on the Templates screen.

When you edit each template you can see that now there are 3 types of questions: text, number and Yes/No. These are really the types of answers a question can have. To better understand all this, I suggest you download the app and explore “My diary” and “Templates” sections in its menu.

The new data model looks like this:

There are Templates now. Each Template has a name and a list of TemplateQuestions. Each DayRecord has a list of SectionRecords, and each SectionRecord has a list of questions.

When to implement the migration in your development process

Now that you’ve seen ‘before’ and ‘after’ versions of the app let’s do the migration. But first let’s talk a bit about when to do the migration. At first I had this crazy idea of releasing the version of the app that would look and feel just like the old version but would have the new data model. I thought I’d do the migration, see that everything’s OK and then start developing the new UI in the next version of the app (the migration part was kind of mysterious for me at that time, and I just wanted to figure it out first and then continue with the rest of the development process). I started out with this approach but soon realised that it just wasn’t right. First of all, there is no reason to release an update without any new functionality. Second, while developing the new UI there may arise a need for changes in the data model, which it did, by the way.

So, if you have some other stuff to do, like changing the UI and adding new functionality just delete the old store by removing and reinstalling your debug app and pretend for a while that there’s been no previous version of the app. Develop and test the new version of your app. And then, when you are sure that the new data model won’t change, start implementing the migration.

Preparation for migration

The migration development process starts just like it would start for a lightweight migration. You add a model version. Select your data model file and then in Editor select Add Model Version.

You will see something like this

Now select one of these two versions and in File Inspector set new version as current version. 

Now your data model files should look like this (the green dot is on the second version):

And that’s where the similarities between custom and lightweight migrations end. After you created new data model you can forget about data migration for a while and develop your app as I suggested earlier. 

Actually implementing custom Core Data migration

When you are finally ready for data migration, the first thing you need is a mapping model. You create it just like you create any other file in Xcode, by clicking Command+N.

When you create it you specify the source store, destination store and a name. In my case source and destination store names were “Simple Q&A Diary” and “Simple Q&A Diary 2”. I named my mapping model SQADMappingModel1_2.xcmappingmodel. Once you have a mapping model you don’t have to configure anything in it right away. Instead you can leave it alone for a while, go to the AppDelegate and work on the migration code. Later you will return to the mapping model and customise it. You start with creating a mapping model because you will need to refer to it in your data migration code.

The intermediate result you are going to achieve after writing some code in AppDelegate is this: the migration will succeed without crashing the app but the data will be lost in the process (because you haven’t set up the mappings yet).

Should you migrate at all?

Typically you start the migration at startup. The best place for migration code is -application:didFinishLaunchingWithOptions: of AppDelegate.

But you don’t migrate data every time the app starts. You only need to do it once. Also, if the user is relatively new and has never had the older version of the app, the migration is not needed at all. You can find out if the user is new by checking the persistent store file. In the case of my app it is “Simple Q&A Diary.sqlite”. If this file does not exist at startup, it means that the user is new and this is the very first launch of the app. In this case you create a store and start using Core Data as usual. If the file existed at startup you must check if it is compatible with the new data model version. If it isn’t, it means that migration is needed. I made this ugly diagram to help you visualise the logic I just described.

Of course, using MagicalRecord is optional. And here is what this logic looks like in code (this piece of code is located in application:didFinishLaunchingWithOptions: method):

We’ll look into each of these methods. But first I want to tell you that the code I show you is the actual code from my app. I didn’t adapt it in any way for this post. So there may be some lines that are not relevant to the topic we are discussing here. For example, you can see this line

It just creates the initial list of questions, so the users can start adding their entries right away even before they created their own questions. You can see also, that I create initial data not only for the new users but also after the migration in some cases:

The reason is that in the old app the initial data is hard-coded, and is not saved in Core Data until the user changes it. So it is possible that some users will have no initial data after the migration.

Now lets look at the methods. Let’s start with helper methods and then look at the method responsible for the migration itself. Helper methods:

Here you can see that inside these methods I use other helper methods:

Method [SQADTracker logEventWithName:] is used for sending events into Firebase analytics. 

What migrates where

Before we look into migrateCoreData method there is one thing you need to understand about the migration: the data migrates from one store file into another store file. Prior to figuring out custom Core Data migrations I only had experience with lightweight migrations and I never needed to worry about store files. So, naturally I thought that the data inside my “Simple Q&A Diary.sqlite” file will just be transformed so that it will be compatible with the new data model.

Turns out this is not the case. To migrate data you need another, temporary store file. The data will be migrated from the old store to the temporary store. After the migration completes you will delete the old store and put the temporary store in its place. After everything is done you will end up with the store file which has the same name as the old file, but the data in it will be compatible with the new version of data model. Then you start to use Core Data as usual (for example you can initialise MagicalRecord at this point).

Note that on step 2 on the image you don’t actually create a temporary file yourself. Instead, this file is created on step 3 during the migration. You only need to specify it’s name.

Now let’s look into the migrateCoreData method:

Note how the mapping model is referenced here (with “cdm” extension). You can also see that the temporary store file name is specified (“SQAD_NEW.sqlite”). I could have chosen any other name for this file. After the migration succeeds the store replacement method is called. Here is the implementation of this method:

I leave all the tracking code (-[SQADTrackerlogEventWithName:] method calls) in these code snippets to give you an idea about the things you might want to track in your own app.

Intermediate testing

Now we can test this code by installing the previous version of the app, adding some data into it and then installing the new version on top of the old version. In the logs you should see that migration was successful. But there will be no data in  the store. Well, in the case of my app there will be initial data created after the migration, but the data that was entered in the previous version of the app will be lost.

Configure the mappings

What you need now is mappings. Go back to the mapping model that you created earlier. In my case it’s a file called SQADMappingModel1_2.xcmappingmodel. Inside it you will see a list of mappings. The names of the mappings will be the same as the names of the entities in the new version of your data model. In my case they looked like this:

Now let’s think a little bit about what we are trying to accomplish. You want to turn thisinto this

In the new data model there is a bunch of entities connected to each other through relationships. It’s easy to get confused about what should map to what. This is how you should think about it. If you look at new data model, you will see that entities connected to each other through a relationships can be viewed as hierarchies with parent entities and child entities. What you should be interested in is the topmost parent entities. They are Template and DayRecord.

So, when you configure the mappings in your mapping model you will specify that Questions entity (old data model) maps to Template entity (new data model)

and Records (old data model) maps to DayRecord (new data model) 

Reminder: I called Records entity so by mistake. I should have named it Record (without s). So think of it like this: each Record turns into a DayRecord.

What about the other entities (TemplateQuestion, SectionRecord, Question)? The child entities will be created programmatically, and I’ll show you how later.

In my project I selected Template mapping and set Questions as it’s source. Then I selected DayRecord mapping and set Records as it’s source. Then I created a subclass of NSEntityMigrationPolicy for each of the two mappings and specified the names of those classes in the mapping model. So, for Template I ended up with these settingsAnd these are the settings for DayRecord

Note that when you specify a source for the mapping, the mapping’s name changes. In my case Template turned into QuestionsToTemplate and DayRecord turned into RecordsToDayRecord. In user info you can see that I added a modelVersion key with a value of 2. This will be used later in the code. Also note that of five mappings I used only two.

I guess I could have deleted the other three, but I just kept them in the mapping model because they were harmless. And here is what relevant files look like in Project navigator

Implementing mapping policies in code

If you now test the migration you will see that nothing has changed: the migration succeeds but the data is lost. What you need now, is to implement createDestinationInstancesForSourceInstance: entityMapping: manager: error: method in each of the entity migration policy subclasses. 

This is how I did that in SQADQuestionsToTemplatePolicy.m file:

I don’t use MagicalRecord or NSManagedObject subclasses at this point. Instead I use key value coding. Xcode complained that method addQuestionsObject: was undeclared, so I had to import Template+CoreDataClass.h. You can see that I take all the data I need from the source instance called sInstance. It’s a Questions object from the old data model. Then I create a Template object and populate it with data, create a bunch of TemplateQuestion objects and connect them to the Template object. Note how I check model version before doing anything of it. This is the key-value pair I set in my mapping model previously.

Here is the contents of SQADRecordsToDayRecordPolicy.m file:

I pretty much do the same thing here: I get the data from source instance, create destination instance, and populate it with data . As you can see, I did some funny stuff with sortField. You don’t need to understand it. It’s just something I had to do to sort SectionRecrods according to their date and their order in their DayRecord.

Precautions

What if migration was not successful? In some examples on Stack Overflow people suggested to not delete the old persistent store but to move it to another URL instead. I could have done it, but I thought about it and I couldn’t figure out how it would help me in any way if the migration failed for some users. I’d need to release an update where they could get their data from the backed up persistent store, which would be too much trouble for me.

But I still wanted to minimise the risk for users to lose all their data in case of a migration failure. I thought, if the migration failed there would be two persistent store files: the old one, and the temporary one. On the next launch there will be second attempt to migrate data. I didn’t know if the existing temporary file, possibly with corrupt data in it, would cause second and all the subsequent migration attempts to fail. So, I decided to just delete the temporary file (if it existed) at every launch. This is the method I used for it:

I called it in -application:didFinishLaunchingWithOptions: of AppDelegate before calling all the migration methods I showed you in this post. There is still a chance that after deleting the old store the attempt to move new store into its place will fail. But this never happened according to my analytics. Maybe you can suggest a better strategy for dealing with possible migration failures.

And this is the end. I hope this tutorial was helpful.

Facebooktwitterredditpinterestlinkedinmail

How to implement social sharing in your iOS app (Twitter and Facebook)

socialsharing

Just yesterday I implemented social sharing in my new app called Sigma. Here is how I did it in two easy to follow steps.

First, I added Social.framework to my project and imported it to my view controller.

Second, I wrote this code for Twitter (it’s in Swift 3.0):

 

And this code for Facebook:

 

That’s it!

Facebooktwitterredditpinterestlinkedinmail

How to add custom fonts to your Xcode project

To add custom fonts to your project you need to go through a few easy steps.

First, download the fonts you need. Usually, they are .ttf or .otf files. Drag and drop the files to the Project Navigator. It is best to put them all in one group named Fonts.

screen-shot-2016-11-04-at-19-59-11

Go to project’s Build Phases and make sure the files are included in Copy Bundle Resources.

screen-shot-2016-11-04-at-19-51-14

Then go to your Info.plist file and add the file names to the array called Fonts provided by application.

screen-shot-2016-11-04-at-19-50-35

At this point you can use your new fonts in your project. But there is one more thing, though. To use the fonts programmatically you need to know their names. Their names are not the same as the names of font files you just included into your project. Filenames can be anything ending with .otf or .ttf. Font names on the other hand look like these:

BebasNeueBold, BebasNeueBook, BebasNeueRegular, BebasNeue-Light, ProximaNova-Regular, ProximaNova-Bold, ProximaNova-Light, HelveticaNeue-Light, HelveticaNeue-Bold, CenturyGothic-Bold, CenturyGothic, MyriadPro-Regular

So, how do you know the proper font names? Just print all the font names to the console. Use this snippet:

 

Facebooktwitterredditpinterestlinkedinmail

Transparent navigation bar snippet

Put this code into viewDidLoad method of a view controller where you want to have transparent navigation bar.

Swift 3.0

 

Objective-C

 

Facebooktwitterredditpinterestlinkedinmail

How to resolve assets error in Xcode 8

Recently I started seeing this error:

Invalid Bundle – The asset catalog at ‘Payload/XXXXX/Assets.car’ can’t contain 16-bit or P3 assets if the app supports iOS 8 or earlier

When I saw it in my own app I just changed the deployment target to iOS 9.3. Next time I saw this error in the project that I do on my job. This time the client wanted to keep supporting iOS 8.0 so I had to find another solution. After googling for some time I found the solution here.

The rest of the post is just a copy-paste. I need it here on my site, so I don’t have to look for it again later.

1. Create an Inspectable .ipa file.  In the Xcode Organizer (Xcode->Window->Organizer), select an archive to inspect, click “Export…”, and choose “Export for Enterprise or Ad-Hoc Deployment”. This will create a local copy of the .ipa file for your app.

2. Locate that .ipa file and change its the extension to .zip.

3. Expand the .zip file. This will produce a Payload folder containing your .app bundle.

4. Open a terminal and change the working directory to the top level of your .app bundle

cd path/to/Payload/your.app

5. Use the find tool to locate Assets.car files in your .app bundle as shown below:

find . -name ‘Assets.car’

6. Use the assetutil tool to find any 16-bit or P3 assets, in each Assets.car your application has as shown below. :

sudo xcrun –sdk iphoneos assetutil –info /path/to/a/Assets.car > /tmp/Assets.json

7.  Examine the resulting /tmp/Assets.json and look for any contents containing “DisplayGamut”: “P3” and its associated “Name”.  This will be the name of your imageset containing one or more 16-bit or P3 assets.

8.  Replace those assets with 8-bit / sRGB assets, then rebuild your app.

Update:  If your Deployment Target is set to either 8.3 or 8.4 and you have an asset catalog then you will receive this same error message, even if you do not actually have 16-bit or P3 assets.  In this case you will either need to lower your Deployment Target to 8.2, or move it up to 9.x.

Facebooktwitterredditpinterestlinkedinmail

How to prevent double tap on UIButton

If you Google this question you will get a lot of wrong answers, which may be marked as right answers on Stackoverflow. The simplest “solution” is to disable the button temporarily inside its action method.

For instance:

But from my experience this approach doesn’t work. The reason is that UIKit framework doesn’t perform your commands immediately: it decides on its own when the right time to do what you ask it to do is. I read about it somewhere a long time ago and I don’t have any links now, so feel free to correct me if I’m wrong. But that’s what I have encountered many times in my work. And it’s strange that nobody on the internet is talking about it. That’s why I wonder if I overlooked something, but anyway…

Here is the solution that I use. I create a BOOL variable or a property on my view controller. Then I just use this flag instead of enabled property of UIButton.

Of course, you must enable the button later by setting the BOOL variable to NO.

Facebooktwitterredditpinterestlinkedinmail

Implementing a Custom URL Scheme (iOS)

Somebody asked me to add a custom URL scheme to my app, so that they could open the app from a launcher. Before that, I had only done it once, so it took me some time to Google this topic and remind myself of how it’s done.

This post will serve as another memo for me. This is how you add a custom URL scheme to your app:

Defining your app’s custom URL scheme is all done in the Info.plist file. Click on the last line in the file and then click the “+” sign off to the right to add a new line. Select URL Types for the new item. Once that’s added, click the grey arrow next to “URL Types” to show “Item 0”. Set your URL identifier to a unique string – something like com.yourcompany.yourappname.

After you’ve set the URL identifier, select that line and click the “+” sign again, and add a new item for URL Schemes. Then click the grey arrow next to “URL Schemes” to reveal “Item 0”. Set the value for Item 0 to be your URL scheme name.

The quote and the image are from this site.

Facebooktwitterredditpinterestlinkedinmail

Implementing scrolling behaviour without UIScrollView

Suppose you want to create a screen that looks something like this.

fortune_wheel

How would you do it? You may think that the labels on the right side can be put inside a UITableView. But there is a problem: each label belongs to a certain sector on the wheel, meaning that each label should be aligned vertically to a corresponding number on the wheel. So, as you rotate the wheel, the vertical distances between the labels will not stay the same. I’m not sure if you can dynamically change the row height in UITableView, but even if you could, this approach doesn’t seem to be right.

When I had to implement such a screen, I basically created my own version of a table view, with reusable cells for the labels on the right side as well as for the sectors of the wheel. I’m not going to go into detail about how I did it. The process was pretty straightforward. Actually there was no table view. It was just a bunch of reusable views which I called cells by analogy with UITableView.

What I want to tell you about, though, is how I implemented the scrolling. Because I had some problems with it.

There are two parts to scrolling. First part is when your finger is on the screen. The second part is the inertial movement of views after you lift your finger off the screen.

The first part you implement in the touchesMoved:withEvent: method. It may look something like this:

What happens here is easy to understand.

In the second part, at the moment when you lift your finger off the screen, you need to calculate the initial angular velocity with which the wheel will start its rotation. That velocity will then decrease with time. The problem I had was about calculating that initial velocity.

The solution was easy but I was thinking too hard.  I tried to implement strange use cases. I thought, what if you put your finger on the screen, then move, then instead of lifting it you wait for some time, and then with quick movement you swipe and lift your finger? Now, I thought, the velocity in this case should be calculated based only on the last swipe. And the first movement should be ignored.

What I did was I saved the position and time of the last touchesMoved event. Then I used it in touchesEnded:withEvent to calculate velocity. So, to calculate the initial velocity of inertial movement of the wheel I used only the last microscopic movement of the finger. And it sort of worked. But not always. In most cases that last little movement would take about 0.01 seconds. But in some cases it would take much less than that, something like 0.00001 seconds. The movement itself almost always was no less then 2 pixels. That would result in a very high speed. Imagine that you don’t do a swipe, and just gently lift your finger off the screen, expecting the wheel to stay still, but it starts rotating!

I tried to artificially limit the velocity, but it resulted in stalling of the wheel from time to time. Finally, as an experiment I tried to use the whole movement of the finger, not just its tiny last part. And it worked! Then I compared the behaviour of my screen to that of a normal UITableView, and as far as I could see it was exactly the same. So, that lead me to believe that’s exactly how the scrolling is implemented in UITableView. Here is the code that I used (not exactly):

Update (September 6, 2016)

OK, it turned out that it wasn’t as simple as I described it in this post. I had to make one little adjustment.

Imagine, that you move your finger on the screen and then stop, wait a second, and then just lift your finger off the screen. In this case the wheel must not move. But in the simplistic model that I have described above you may produce an undesirable movement of the wheel.

How to fix this? You need to analyse the last part of your touch to learn if there was a significant enough movement of the finger in that last part. If there was no movement or it was less than a certain amount of points, you just set the angular velocity of the wheel to zero.

Facebooktwitterredditpinterestlinkedinmail

Creating a settings screen using UITableViewController

A settings screen is a screen that looks something like this:

When I started out with iOS development, I knew only one way of creating such a screen. I would create a UIScrollView and just put all the views and their constraints on it manually using the Interface Builder. That was a lot of work but at that time I didn’t know any better.

Once I had a job interview. And an interviewer asked me how I implemented a certain settings screen in my app. I told him it was a UIScrollView with a bunch of views in it. He asked me why I hadn’t used UITableView, that way I could have  implemented repeating elements as UITableViewCells. Good idea! – I thought.

Up until that point I only used UITableView with dynamic cells. So I tried table views with static sells. It turned out you can only use them inside UITableViewController, and not inside UIViewController with a table view. That got me confused. I couldn’t figure out what was the use of a UITableViewController, since there was no way to customise it. For instance, you can’t add a button at the bottom of the table view if that table view is inside UITableViewController.

Having no luck with static cells I tried using table views with dynamic cells to create settings screens.I used this approach in a couple of projects. It worked, but it wasn’t much of an improvement over using UIScrollView. With scroll view you have to do a lot of work in the Interface Builder. With table view with dynamic cells you have to do a lot of coding.   

Anyway, a couple of days ago I finally figured out how to use UITableView with static cells to create a settings screen. The solution is so simple, I can’t believe I figured it out only now, after 2 years of doing iOS programming.

This is how you do it. You create a UIViewController and a UITableViewController. Then you put your UITableViewController inside your UIViewController, as a child view controller. That’s it. If you want to put a button outside of the table view, just do it in the UIViewController. This is how it may look in your Storyboard. 

The cool thing is that once you created one cell, you can just copy it. Then in your UITableViewController you can make outlets for all your text fields, labels, buttons and other UI elements you have inside the table view. Also, when the keyboard goes up, the table view adjusts accordingly.

Unfortunately, I couldn’t customise the section headers right in the storyboard, so I had to do it programmatically. Here are the only three UITableViewDataSource methods that I implemented:

 

Facebooktwitterredditpinterestlinkedinmail

Adding and removing Child View Controllers

When you add or delete a child view controller, there is a number of things you need to do. For me it has always been  too easy to forget what things I needed to do and in which order. So I decided to create this post as a little reminder for myself. All the quotes and code examples are from Apple’s “View Controller Programming Guide for iOS”.

Adding a Child View Controller

  1. Call the addChildViewController: method of your container view controller. This method tells UIKit that your container view controller is now managing the view of the child view controller.
  2. Add the child’s root view to your container’s view hierarchy. Always remember to set the size and position of the child’s frame as part of this process.
  3. Add any constraints for managing the size and position of the child’s root view.
  4. Call the didMoveToParentViewController: method of the child view controller.

Example:

Removing a Child View Controller

  1. Call the child’s willMoveToParentViewController: method with the value nil.
  2. Remove any constraints that you configured with the child’s root view.
  3. Remove the child’s root view from your container’s view hierarchy.
  4. Call the child’s removeFromParentViewController method to finalize the end of the parent-child relationship.

Example:

 

Facebooktwitterredditpinterestlinkedinmail