Explore Free iOS App Tutorials – GameDev Academy https://gamedevacademy.org Tutorials on Game Development, Unity, Phaser and HTML5 Mon, 20 Feb 2023 21:14:36 +0000 en-US hourly 1 https://wordpress.org/?v=6.1.1 https://gamedevacademy.org/wp-content/uploads/2015/09/cropped-GDA_logofinal_2015-h70-32x32.png Explore Free iOS App Tutorials – GameDev Academy https://gamedevacademy.org 32 32 Free eBook – iOS App Development for Human Beings https://gamedevacademy.org/free-ebook-ios-app-development-for-human-beings-2/ Mon, 06 Feb 2017 02:47:52 +0000 https://swiftludus.org/?p=2075 Read more]]> Are you interested in iOS app development? working on some apps for you or your clients?

We are stoked to announce the release of our new ebook iOS App Development for Human Beings, by app developer and computer scientist Mohit Deshpande, in collaboration with Pablo Farias Navarro.

This books includes 11 chapters covering topics such as how to get started with Xcode, building a unit converted, working with different UI elements, accessing the user’s location and creating a project management app. In the last chapter, the book covers the basics of the SpriteKit framework, used to create 2D games.

We hope this free ebook gives you the skills you are looking for and that it helps you in your road to become a great iOS developer!

This book is provided at no cost in PDF format.

Download the ebook ]]>
The Complete iOS Game Course Using SpriteKit And Swift 3 https://gamedevacademy.org/the-complete-ios-game-course-using-spritekit-and-swift-3/ Mon, 06 Feb 2017 02:21:39 +0000 https://swiftludus.org/?p=2066 Read more]]> This is THE course for learning IOS game development!  Learn:

  • Basic Swift Programming
  • Intermediate Swift Programming
  • Advanced Swift Programming
  • Importing And Organizing Assets
  • Animating Sprites
  • Creating Menus
  • Creating Cool Animations
  • Understand And Use SpriteKit’s Physics System
  • Saving Game Data Using NSKeyedArchiver And NSKeyedUnarchiver
  • Using Game Controllers To Control The Workflow Of Your Game
  • Play Music In Your Game
  • Using Basic And Intermediate Features Of SKActions
  • That And Much More Is Awaiting For You In This Course

And when you finish you will have these 3 popular games to start your portfolio :

  • Game #1: Jack The Giant
  • Game #2: Flappy Bird
  • Game #3: Cowboy Runner

If You ever had an idea for an awesome game, then enroll in this course and learn the tools that you need to develop your next hit game!!!

Access this course on Zenva Academy

]]>
The Complete iOS 10 Developer Course https://gamedevacademy.org/the-complete-ios-10-developer-course/ Mon, 06 Feb 2017 02:21:28 +0000 https://swiftludus.org/?p=2062 Read more]]> This course is the only course you will need to become a IOS 10 Developer! An impressive 80 hours of content written by industry experts. It covers developing apps for TVOS, iPad, iPhone and the incredible Apple Watch. It simply has it all to get you up to speed FAST with iOS 10 specifics.

This course covers:

  • Master iOS 10 development with 80 hours of content
  • Learn the basics of coding in Swift & building apps in iOS 10
  • Work with iOS 10’s new facial recognition capability
  • Use SiriKit to build apps that function better with Siri
  • Optimize iOS 10’s significantly improved artificial intelligence
  • Build games with SpriteKit Games
  • Develop apps for TVOS, iPad, iPhone & Apple Watch

Access this course on Zenva Academy

]]>
Learn to Build a Chess Game in iOS 10 https://gamedevacademy.org/learn-to-build-a-chess-game-in-ios-10-2/ Mon, 06 Feb 2017 02:21:16 +0000 https://swiftludus.org/?p=2053 Read more]]> If you have basic knowledge of Xcode and programming, then you are ready to build a chess game in IOS 10!

This is a project-based course so you will NOT be spending time learning a bunch of useless coding practices. AND upon completion, you will have a real world app to use in your portfolio. Taking this course means that you learn practical, employable skills that can be used immediately.

Learning how to code is a great way to jump in a new career or enhance your current career. Coding is the new math and learning how to code will propel you forward for any situation. Learn it today and get a head start for tomorrow.

Access this course on Zenva Academy

]]>
How to Make a Freaking iPhone App – iOS 10 and Swift 3 https://gamedevacademy.org/how-to-make-a-freaking-iphone-app-ios-10-and-swift-3-2/ Mon, 06 Feb 2017 02:19:19 +0000 https://swiftludus.org/?p=2046 Read more]]> Have you ever wanted to make a killer iPhone app? You have a great idea, just no idea how to go about it. You have watched a ton of YouTube videos, read numerous blog post and all that happened was you ended up with a headache.

This course is what you need!  Learn from someone who has been exactly where you are right now. Get a solid understanding of Swift 3 and create real apps for the iPhone. Here is a list of some of the apps that you will have created by the end of this course:

  • Emoji Dictionary – A simple app to help us learn about TableViews
  • Do It! – A todo app that teaches us about CoreData
  • Game Collector – Learn to work with the camera as we make an app to catalog a collection of something (video games, movies, stamps, etc…)
  • Soundboard – Record sounds using the microphone and play them back. This one is fun!
  • Snapchat Clone – We’ll use Firebase to make a simple version of Snapchat. Very involved
  • Pokemon Go Clone – Learn how to work with maps as we make a clone of most popular iOS game right now

Don’t wait any longer to start learning iPhone app development!

Access this course on Zenva Academy

]]>
Face Detection with Core Image https://gamedevacademy.org/face-detection-with-core-image/ Wed, 18 Jan 2017 13:39:09 +0000 https://swiftludus.org/?p=2011 Read more]]> In this post we’re going to see how to use Core Image to build a face detector. We’ll use a static image that we load into Xcode using the asset manager and draw on the region that we’ve identified as a face.

BUILD GAMES

FINAL DAYS: Unlock 250+ coding courses, guided learning paths, help from expert mentors, and more.

Download the source code for this post here.

Before we delve into the Swift source code. Let’s first discuss what face detection is and how it is commonly implemented. Face detection answers the question “is there a face in this image?” DO NOT confuse it with face recognition which answers the question “whose face is this?” Face detection is generally simpler than face recognition since face recognition usually has face detection as a stage. Before identifying whose face this is, we need to know if there is a face in the image to begin with!

Face detection can not only tell is if there is a face in an image, but it can also tell us more information about the face, like if the face is winking or smiling. We will first discuss some of the most common techniques of face detection, then see how Core Image helps abstract away some of those lower-level algorithms.

The most common technique used for face detection is called Viola-Jones Face Detection. It uses Haar-like features to distinguish between the two possible outcomes: face or no face. Below are some of the Haar-like features used to make this distinction.

facedetect-1

Notice that these features line up with facial features like noses or eyes.

facedetect-2

We take these features, slide them across the image, and perform a mathematical operation called convolution. Using convolution, we can tell if certain areas of the image conform to those features. Using many thousands of example images, we can train a machine learning classifier on the images using the Haar-like features and measure the response from the convolution operation. We compare that with the true outcome of the training image, and our model will learn the orientation, dimensions, and the combination of features required to determine if an image has a face in it or not.

In most cases, this model is usually very slow to learn! Because of this, we usually don’t train our own model from scratch. Many other people have pre-trained models that we can download and use directly.

Now that we understand how face detection works on the surface, let’s use Core Image and build our own face detector! Open up Xcode and create a Single-View application called FaceDetector. We’ll need to add an image of a face to our project. Xcode has its own image assets system. In the project pane, there is a file called Assets.xcassets. Open it up and drag-and-drop the picture of the face into the main pane.

facedetect-3

Now that our image asset is registered in the Assets.xcassets file, it is much easier to use it in our code, Interface Builder, and Xcode in general. Now let’s open up our main storyboard. To make our image as large as possible and our UI as clean as possible, click on the view controller and embed it in a navigation controller so we get a top navigation bar. Then drag out a UIImageView and resize it so that it fits the entire screen excluding the top navigation bar.

facedetect-4

Click on the UIImageView and in the Attributes Inspector and change the image attribute to the image that we added. Since we added the image to the Assets.xcassets, Xcode will provide us with autocomplete options.

facedetect-5

We might need to change the Content Mode attribute depending on our image. By default, it will resize and warp our image so that it fits snugly inside of our UIImageView. However, for some smaller images, this may warp the image so much that the face detection algorithm might not accurately detect a face! To prevent the image from being rescaled, change the Content Mode attribute to be Center or Aspect Fill.

Now let’s add a Bar Button Item to the right side of the top navigation bar with the text “Detect”. When the user presses this button, we will run the face detection algorithm and draw on the image.

facedetect-6

Now that our UI is completed, let’s create an outlet to the UIImageView called imageView and an action to the UIBarButtonItem called detect.

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func detect(_ sender: UIBarButtonItem) {
    }

}

Now let’s build our face detector! First we need to import Core Image by adding an import statement at the top under the one for UIKit: import CoreImage . Now add a method called detectFaces that will do the actual detection.

We first need to get the image from the UIImageView and convert it into a CIImage for use in Core Image (hence the CI prefix to Image).

func detectFaces() {
    let faceImage = CIImage(image: imageView.image!)

}

Then we need to create our face detector.

let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])

We specify that this detector is for faces and pass in the dictionary of options where we specify the accuracy.  Now to detect the faces, we simply call the features:in method.

let faces = faceDetector?.features(in: faceImage!) as! [CIFaceFeature]

We forcibly cast the result to an array of CIFaceFeature since we know we’re using the face detector and not some other detector (like QR code for example). Now we can iterate through the faces and report the bounds of the face and other face properties! To verify that our image has a face, we can check the length of this faces array. Here is the entire method.

func detectFaces() {
    let faceImage = CIImage(image: imageView.image!)
    let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
    let faces = faceDetector?.features(in: faceImage!) as! [CIFaceFeature]
    print("Number of faces: \(faces.count)")
}

It took only a few lines of code to have working face detection! Now we can simply call this method in our UIBarButton action.

@IBAction func detect(_ sender: UIBarButtonItem) {
    detectFaces()
}

Let’s run our app to see if we have a face! In the console at the bottom of Xcode, we should see a number greater than zero.

facedetect-7

Excellent! Now we can get more information about those faces and even draw on the image! Let’s draw a red box around the face! To do this, we can create a generic UIView around the same bounds as the face, add a red border, make it transparent, and add it to the image.

for face in faces {
    let box = UIView(frame: face.bounds)
    box.layer.borderColor = UIColor.red.cgColor
    box.layer.borderWidth = 2
    box.backgroundColor = UIColor.clear
    imageView.addSubview(box)
}

Now let’s run our app and see the box!

facedetect-8

That’s not quite right! Why is our box is a little off? This is because the UIView coordinate system for placing views on the screen is different than the Core Image coordinate system!

facedetect-9

For Interface Builder, the origin is at the top left, but, for Core Image, the origin is at the bottom left. The X directions are both increasing to the right, but the Y directions are different. This is what is causing the issue in positioning our view.

We have to make a transform to convert from the Core Image coordinate system to the UIView coordinate system. We could do this manually, but The CGAffineTransform class exists solely for the purpose of these kinds of transforms. We first define the transform fully; then we can apply it to points or rectangles. We also have to consider the case where the image is smaller than the UIImageView that is holding it in either height or width. To account for this, we have to execute the image transform, but we also have to do some additional scaling and repositioning to move the bounding rectangle of the face to the appropriate position. Here is the completed code for the detectFaces method.

func detectFaces() {
    let faceImage = CIImage(image: imageView.image!)
    let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
    let faces = faceDetector?.features(in: faceImage!) as! [CIFaceFeature]
    print("Number of faces: \(faces.count)")
    
    let transformScale = CGAffineTransform(scaleX: 1, y: -1)
    let transform = transformScale.translatedBy(x: 0, y: -faceImage!.extent.height)
    
    for face in faces {
        var faceBounds = face.bounds.applying(transform)
        let imageViewSize = imageView.bounds.size
        let scale = min(imageViewSize.width / faceImage!.extent.width,
                        imageViewSize.height / faceImage!.extent.height)
        
        let dx = (imageViewSize.width - faceImage!.extent.width * scale) / 2
        let dy = (imageViewSize.height - faceImage!.extent.height * scale) / 2
        
        faceBounds.applying(CGAffineTransform(scaleX: scale, y: scale))
        faceBounds.origin.x += dx
        faceBounds.origin.y += dy
        
        let box = UIView(frame: faceBounds)
        box.layer.borderColor = UIColor.red.cgColor
        box.layer.borderWidth = 2
        box.backgroundColor = UIColor.clear
        imageView.addSubview(box)
    }
    
}

To convert to the Interface Builder coordinate system, we have to move the box up by the height of the image and negate the height, which is what we do in this code above. Then we can apply the transform to the bounds of the face. If our image fit perfectly within the UIImageView, we would be done!

However, we also account for cases where this doesn’t happen. In that case, we have to appropriately scale our rectangle and translate it so that we account for the smaller (or larger) height of the UIImage inside of the UIImageView.

Finally, let’s see the results of our code. Run this code and see the results below!

facedetect-10

Note that there are many properties on face that we can leverage to gain even more information about the face: winking, smiling, etc.

In this post, we discussed a popular method of face detection: Viola-Jones Face Detector. We applied this to Core Image and built a face detector for iOS. Given a static image, or one taken from a camera, it draws the bounding box around what it detects as a face.

]]>
Create a Queryable Music Library using SQLite Databases https://gamedevacademy.org/create-a-queryable-music-library-using-sqlite-databases-2/ Wed, 11 Jan 2017 13:34:46 +0000 https://swiftludus.org/?p=1998 Read more]]> In this post, we’re going to build a small app that will present a music library and allow us to query the data using a pre-existing database. We’ll learn about the SQLite database in particular and how to perform different kinds of queries on it. The first part of this post will be a very quick introduction to databases and SQLite in general. Then we’ll move on to building our music library.

Download the source code for this post here.

BUILD GAMES

FINAL DAYS: Unlock 250+ coding courses, guided learning paths, help from expert mentors, and more.

A database is an organized and structured collection of related data. Databases allow us to define a structure and relationships between structures for performing various operations at a high level. They are becoming more and more prevalent as the size of datasets grows. Most of the apps and web services that we use daily make use of databases under-the-hood.

Databases hold structured data in tables. Think of a table as a very simple, property-only Swift class. Tables can have any number of attributes which correspond to Swift properties. An attribute has a type like INTEGER (like Int), REAL (like Double), and VARCHAR (like String).

We also have a primary key attribute on each table which allows us to uniquely identify rows. Suppose we had a table of people. It is entirely possible that we could have two people with the same first and last name! But when we insert them into the database, they would have a different value for their primary key attribute so we can differentiate them!

We can also have relationships between tables using foreign keys. For example, consider a song. Songs are usually published on an album. We can have two separate tables for songs and albums and create a relationship between the two. Since songs belong to an album, we can have an attribute in the songs table that refers to the unique id of the album, in other words, album’s primary key. This allows us to effectively link the two using the unique primary key value. Like the person table, we use the primary key attribute because we could have two albums with the same name!

We store data in rows in a table. Each row in a table must have a value for each attribute (or a NULL value). Rows correspond to creating an instance in Swift. We can have many rows in a particular database and can perform complicated queries very fast. This is the major advantage that databases hold over plaintext files: fast querying!

Think of a query like a question: “what is the length of this song?”, “who wrote this song?”, “what is the genre of this song?”. We can have more complicated questions that involve multiple tables like “given this current song, what other songs has this artist performed?”. Using a database, we can answer these questions very quickly and easily!

We’re going to be using a pre-built database called the Chinook database. That database is in the starter project here. Inside of the Chinook Music Library folder, we’ll see a file called chinook.sqlite. This is our database file! SQLite was installed when we installed Xcode and we can use it in the Terminal app to look at our database.

Copy the chinook.sqlite file to your Desktop. Open up Terminal and navigate to your Desktop like this.

cd ~/Desktop/
sqlite3 chinook.sqlite

Notice that our prompt changes to sqlite>. This means that we’re using the SQLite prompt! We can run the following command to get a list of tables.

sqlite> .tables
Album          Employee       InvoiceLine    PlaylistTrack
Artist         Genre          MediaType      Track        
Customer       Invoice        Playlist

We’ll be interacting with only a few of these tables, particularly the Track, Album, and Artist. Now we can use SQL to access the rows stored in these tables. For example, let’s get a list of all of the track names. (If we run .schema , we can get a list of the attributes that each table has).

SELECT Name FROM Track;

Each SQL statement ends in a semicolon, and this one will retrieve all of the values in the Name attribute from the Track table. We could add an additional WHERE clause to apply constraints on the results. For example, suppose we wanted to retrieve all of the attributes about the Overture from Carmen. We can use an asterisk * to retrieve all attributes and a WHERE clause to specify the name of the song.

sqlite> SELECT * FROM Track WHERE Name = 'Carmen: Overture';
3447|Carmen: Overture|313|2|24|Georges Bizet|132932|2189002|0.99

We get all of the information about that particular song. We can generalize the SELECT statement syntax as the following.

SELECT <attribute names>
FROM <table names>
WHERE <conditions>;

We can also perform aggregate functions like counting.

SELECT COUNT(*) FROM Track;

This will give us the count of all of the tracks! Aggregate functions are quite advanced topics in SQLite so we don’t go into more detail than what is required to make our music library.

That’s enough SQL and databases for now! If you haven’t already, download the starter project here. To open it, OPEN THE .xcworkspace FILE, NOT THE .xcodeproj FILE!

If we look at the storyboard, we have 2 UITableViewControllers wrapped in a navigation controller. The first UITableViewController represents the albums and the second represents the tracks or songs.

dbs-1  For the AlbumViewController, we want to display the album name and the number of tracks in that album. We first have to get the file path to the database and connect to it. To access a file in our Xcode project, we use Bundle.main.path(forResource:ofType:). Then we can use a Connection object to do this. We have to put it in a do-catch block however. We can write all of this code in the viewDidLoad method.

override func viewDidLoad() {
    super.viewDidLoad()
    let path = Bundle.main.path(forResource: "chinook", ofType: "sqlite")!
    do {
        let db = try Connection(path, readonly: true)
        
    } catch {
        print("Cannot connect to database!")
    }
}

Try running this code. We shouldn’t have any errors! Now we can start formulating the query. We have to use two separate queries: one to retrieve all of the album titles and another to retrieve the count of all of the tracks on that album.

We have to do some setup with SQLite.swift by declaring all of our tables and attributes ahead of time.

override func viewDidLoad() {
    super.viewDidLoad()
    let albumId = Expression<Int>("AlbumId")
    let title = Expression<String>("Title")
    
    let albumsTable = Table("Album")
    let tracksTable = Table("Track")
    
    let path = Bundle.main.path(forResource: "chinook", ofType: "sqlite")!
    ...
}

Now that we’ve setup our variables, we can actually run the query. We want to iterate over each item in the albums table and count all of the tracks whose AlbumId is the AlbumId of the current album we’re at. We also want to iterate in alphabetical order.

for row in try db.prepare(albumsTable.order(title)) {
    let count = try db.scalar(tracksTable.filter(albumId == row[albumId]).count)
    albums.append((x[albumId], x[title], count))
}

where albums is defined as a property var albums = [(albumId: Int, albumTitle: String, numberOfTracks: Int)]() . We need to store the albumId for that album as well because we’re re-ordering all of the entries! We can’t rely on the UITableView’s ordering to be in order of the album IDs.

The AlbumId attribute of the Track table is the same as the AlbumId attribute of the Album table because it is a foreign key in the Track table referring to the Album table. This means we can use it for comparisons. Since we’re using this as a readonly database, the database will guarantee that the value of each AlbumId attribute of each row in the Track table maps to a valid row in the Album table.

Now that we have our items, we can easily implement the UITableView methods!

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return albums.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "albumReuseIdentifier", for: indexPath)
    let (_ title, count) = albums[indexPath.row]
    cell.textLabel?.text = title
    if count == 1 {
        cell.detailTextLabel?.text = "\(count) song"
    } else {
        cell.detailTextLabel?.text = "\(count) songs"
    }
    return cell
}

We report the number of items in the albums array and decompose the tuple to set to the title and detail text. Now let’s run our app! We see all of the album names along with the number of songs!

dbs-2

The next and final step is to display the tracks given the ID of the album. We’ve already executed a similar query that we can reuse! When we retrieved the count of all of the tracks on one album, we were returning all of the tracks on that album, but we took a sum. This time, we’ll do the same query, but won’t take the sum!

Let’s jump over to the TrackViewController. Add a member variable called tracksAlbumId at the top so we can set it during the segue to this view controller. We also need another array of tuples to hold track information. Additionally, we get the name of the album so we can display it in the navigation item’s title.

var tracksAlbumId = -1
var albumTitle = ""
var tracks = [(String, String)]()

In the same viewDidLoad method, we’re going to setup our variables and connect to our database. We do not need as many variables since we only want the name, albumId for reference, and the length of the track. We also only need the Track table since it has the AlbumId foreign key we are going to use.

override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationItem.title = albumTitle
    let albumId = Expression<Int>("AlbumId")
    let name = Expression<String>("Name")
    let time = Expression<Int>("Milliseconds")
    
    let tracksTable = Table("Track")
    
    let path = Bundle.main.path(forResource: "chinook", ofType: "sqlite")!
    do {
        let db = try Connection(path, readonly: true)
        
    } catch {
        print("Cannot connect to database!")
    }
}

Now let’s think about the query we want to make. We want to retrieve the name and length of all tracks from the Track table whose AlbumId matches the one we were given. We also want to sort the tracks alphabetically by name as well. We can formulate this query in SQLite.swift like this.

let query = tracksTable.select([name, time]).filter(albumId == tracksAlbumId).order(name)

Now we can execute it and format the output.

override func viewDidLoad() {
    super.viewDidLoad()
    let albumId = Expression<Int>("AlbumId")
    let name = Expression<String>("Name")
    let time = Expression<Int>("Milliseconds")
    
    let tracksTable = Table("Track")
    
    let query = tracksTable.select([name, time]).filter(albumId == tracksAlbumId).order(name)
    
    let path = Bundle.main.path(forResource: "chinook", ofType: "sqlite")!
    do {
        let db = try Connection(path, readonly: true)
        for row in try db.prepare(query) {
            let seconds = row[time] / 1000
            tracks.append((row[name], "\(seconds / 60):\(seconds % 60)"))
        }
    } catch {
        print("Cannot connect to database!")
    }
}

For duration, we convert milliseconds into seconds first. Then we take the integer division, which truncates, to figure out how many minutes. Finally we perform a modulo to determine how many seconds are left over. Now that we have this information, filling out the UITableView methods is easy!

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return tracks.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "trackReuseIdentifier", for: indexPath)
    let (trackName, duration) = tracks[indexPath.row]
    cell.textLabel?.text = trackName
    cell.detailTextLabel?.text = duration
    return cell
}

We’re almost finished! We need to make sure we pass the ID of the selected album in AlbumViewController. Let’s head back there and use the prepare:for segue method.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let trackViewController = segue.destination as? TrackViewController
    let album = albums[tableView.indexPathForSelectedRow!.row]
    trackViewController?.tracksAlbumId = album.albumId
    trackViewController?.albumTitle = album.albumTitle
}

Here we get the destination view controller and the album information for the row that was selected. We set the tracksAlbumId of the destination view controller to be the albumId of the selected row.

Now let’s run our app! We can see our list of albums! When we press on an album, we get the tracks that are on that album and their durations!

dbs-3 dbs-4

 

]]>
A Guide to Grand Central Dispatch and Concurrency in iOS https://gamedevacademy.org/using-grand-central-dispatch-and-concurrency-in-ios-2/ Wed, 07 Dec 2016 13:00:52 +0000 https://swiftludus.org/?p=1953 Read more]]> In this tutorial, we’re going to explore grand central dispatch (GCD) and discuss concurrency topics and their applications. We’ll learn about concurrency, challenges with concurrency, and the application of GCD to help solve those problems.

BUILD GAMES

FINAL DAYS: Unlock 250+ coding courses, guided learning paths, help from expert mentors, and more.

Download the source code for this post here.

Grand Central Dispatch (GCD) is Apple’s library for concurrent code on iOS and macOS. We can use GCD to improve the responsiveness of our app and speed up our app by delegating expensive tasks to the background.  For example, consider networking. Usually, we put networking operations in the background because they may take a long time.

Introduction to Concurrency

Before we get into using GCD, we have to learn about concurrency. Tasks can be executed serially or concurrently (think of a task as just a closure in Swift). A sequence of tasks is executed serially if the tasks are executed one after the other, with the next task waiting for the previous one to finish. On the other hand, a sequence of tasks is executed concurrently if multiple tasks can be executed at the same time. Imagine a bank with one bank teller and a line of people. If we wanted to go through the people in line serially, we would have one long line of people. In a concurrent setup, we would have multiple lines to one bank teller.

This also leads to another topic: parallelism. The concurrent setup with the bank is an example of a non-parallel setup. If we wanted a concurrent parallel setup, we would have multiple bank tellers so that many people could be served independently.

We can have concurrent setups that are not parallel. Imagine ordering food at a restaurant. The waiter will take your order, relay it to the chef, and move on to the next person. Whenever the chef is finished, the waiter will bring out the food. If this were a serial setup, then no one else could order food until the chef finished with your order! If we added parallelism, then we would have multiple waiters and chefs.

Parallelism requires concurrency, but concurrently does not imply parallelism.

Here’s a chart to illustrate the differences. The people, or tasks, are the blocks and the tellers/waiters are the circles.

gcd-1

There are a few more terms that we should be familiar with when discussing concurrency. The first is the distinction between synchronous and asynchronous code. Let’s suppose that the code is wrapped in a function, which is often the case. A function is synchronous if it returns after finishing its task. A function is asynchronous if it returns immediately and sends its task elsewhere to be completed. It does not wait for the task to complete to continue execution.

This distinction is important because synchronous functions block the main thread of execution so our user will not be able to interact with our app while a synchronous function is executing. These functions should be kept fairly small and execute fair quickly. For longer tasks or tasks whose time is unpredictable, we should use an asynchronous function and queue the work. These functions do not block the main thread of execution, and our user can still interact with our app while they wait.

Dispatch Queues

queue is exactly like a queue in the real-world: the first thing in line gets served first. Dispatch queues are called that because we send, or dispatch, tasks to these queues to be executed in the background.

Serial queues execute tasks serially. From our previous discussion of serially executing tasks, we know that one task is executed after the other, and the next task waits for the previous one to finish. The order in which the tasks enter the serial queue is the order in which they will execute and finish.

Concurrent queues executes tasks concurrently. As tasks enter this queue, they will be started but may finish in a different order, depending on how long the tasks take.

Below is a chart that illustrates the difference between these two queues. For the serial queue, Tasks 1 – 4 execute and finish in that order. For the concurrent queue, the tasks are started in the same order, but they finish in a different order. 

gcd-2

 

The last thing we have to discuss is the different queues that Apple provides us out-of-the-box. We offload tasks to these queues. Each queue has its Quality of Service (QoS) class/category, and we have to decide which QoS class best fits our task.

Below is a table that shows the different QoS classes, the types of tasks we should assign to a QoS class, and the approximate amount of time that task should take.

gcd-3

Note that these queues are system global queues; our tasks won’t be the only tasks in these queues! Also it is important to note that these queues are concurrent queues. Apple does provide us with a serial queue called the main queue. This is the queue that we use to update our UI, for example, and, in fact, it is the only queue in which we can update any UI elements.

Working with GCD in Swift

Let’s see how we can use GCD to apply what we’ve learned about concurrency. Let’s create a simple Single-View Application called Multitaskr. We’re going to demonstrate performing some miscellaneous work in the background. To visibly see progress, let’s add a UILabel to the top-left of our screen to act as a display for status text. Add a button underneath it so that we can start the task when the button is pressed.

gcd-4

We’ll create an outlet to the label so we can use it later.

@IBOutlet weak var statusLabel: UILabel!

We need to simulate performing some kind of work so let’s create a method that does some time-intensive task.

func performWork() {
    let n = 5000
    var matrix = Array(repeating: [Int](repeating: 0, count: n), count: n)
    for i in 0..<n {
        for j in 0..<n {
            matrix[i][j] = i + j
        }
    }
}

This method creates a 2D array, or matrix, with 5000 rows and 5000 columns. Initially, it is populate with all zeros, but we change each element to the result of an arbitrary operation. This purpose of this function is to simulate a long task. We can change the size of the n constant if we wanted this task to take longer.

Now let’s create an action to the button so that we can execute the code when we click on the button. To execute code in the main serial queue, we call DispatchQueue.main.async and use a trailing closure. The async method does not take any parameters except for a closure so we can even omit the empty parentheses! (The [unowned self] in is to ensure we’re using good memory management)

@IBAction func startWork(_ sender: UIButton) {
    DispatchQueue.main.async {[unowned self] in
        self.performWork()
        self.statusLabel.text = "Finished!"
    }
}

Inside of the closure, we’re going to call performWork and change the status label when we finish. We can change the label because we’re in the main queue so changes to UI elements are valid! Let’s run our app and notice the changing label.

gcd-5

After a few seconds, we should see the label change to “Finished!” to signify that we’ve finished the task. This is work that has been done in the main queue in the background!

Now let’s see how we can use those concurrent queues with the QoS classes that we discussed. Using those queues is almost identical to using the main serial queue, except we have to specify which QoS class we want.

@IBAction func startWork(_ sender: UIButton) {
    DispatchQueue.global(qos: .userInitiated).async {[unowned self] in
        self.performWork()
        DispatchQueue.main.async {
            self.statusLabel.text = "Finished!"
        }
    }
    self.statusLabel.text = "Started work!"
}

In the above code, we’re using the user-initiated concurrent queue because our operation will take only a few seconds. We use the async method to run a task on that queue. To demonstrate that this concurrent asynchronous queue returns immediately, we change the text of the label right after the call to the async method. Since the async method should return immediately, we expect the label’s text to change to “Started work!” immediately! Then it should change to “Finished!” after the work is finished.

One very important thing to note is that, inside of the task sent to the user-initiated QoS concurrent queue, we have another task that we dispatch to the main queue to update the view. This is because we should never update views in a background task! All of the view updating should occur on the main serial queue.

Let’s run our app and see the results!

gcd-6

Notice that the label changes first to “Started work!” and then to “Finished!” after some time!

In addition to the usual async method, we can also add a delay using another method asyncAfter.

DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 2) {[unowned self] in
    self.performWork()
    DispatchQueue.main.async {
        self.statusLabel.text = "Finished!"
    }
}

This will wait for 2 seconds from the time of that method call to start executing the task.

Too Much Concurrency?

There is such a thing as having “too much” concurrency. It is not to our benefit to offload all work into the background. In fact, this will actually slow down our code! This is because our device is spending more time switching between tasks! This reduces the time that our device spends on actually executing the task itself! This is called CPU thrashing.

We should only offload tasks that we know have the potential to take long periods of time. For example, networking requests may take a long time depending on our user’s connectivity. These tasks should almost always be offloaded into the background.

To figure out what should be put in the background, we can use Xcode’s Instruments tool to isolate code that takes a long time to execute and offload that code.

In addition to the points mentioned above, excessive concurrency makes our code more complex and difficult to debug as well! These complications should be weighted against the benefits of concurrency for our particular case.

Remember: don’t go crazy with concurrency!

In this post, we’re learned more about grand central dispatch (GCD). We discussed concurrency topics like synchronousness, dispatch queues, parallelism, and quality of service. We also applied these concepts to writing Swift code that used GCD. Next, we discussed some interesting challenges with concurrency and when and when not to use it. Finally, we built an app that demonstrated these concepts.

]]>
The Complete Beginners Guide to View Animations in iOS https://gamedevacademy.org/the-complete-beginners-guide-to-view-animations-in-ios-2/ Mon, 28 Nov 2016 13:00:51 +0000 https://swiftludus.org/?p=1942 Read more]]> In this tutorial, we’re going to discuss how to perform different kinds of animations on views. There are many properties on views that we can animate to delight our user. Using subtle animations, we can help make a boring app more exciting.

Download the source code for this post here.

BUILD GAMES

FINAL DAYS: Unlock 250+ coding courses, guided learning paths, help from expert mentors, and more.

Let’s get started by creating a Single View application called Animator. Open up the storyboard and let’s add a few UI elements to our app. Let’s start simple and have a UILabel in the center of the screen with the text Hello World.

animations-1

The first property we’ll look at is the alpha level. The alpha level is a decimal number between 0 and 1 that describes how transparent/opaque or “see-through” our view is. An alpha level of 0 means that our view is completely invisible, or transparent, and an alpha level of 1 means that our view is completely visible, or opaque. Let’s see how we can animate this in our Swift code.

Create an outlet to this view called helloLabel in ViewController.

@IBOutlet weak var helloLabel: UILabel!

After we have an outlet to it, click on the UILabel and set its alpha level to 0 in the Attributes Inspector. Our UILabel immediately disappears! It is still there but invisible. We’ll animate this UILabel to slowly appear after the screen loads.

In the viewDidLoad method, let’s animate our UILabel to appear when the view is loaded. To do this, we use a method on UIView called animate:withDuration:animations.

UIView.animate(withDuration: 2) {
    self.helloLabel.alpha = 1
}

As arguments, we supply the duration we want this animation to appear over and a trailing closure that describes which properties to change over that time interval. It is important to remember that the time interval in in seconds! Let’s run our app!

animations-2

This animation is called fade in because the view “appears” on the screen, even though it has always been on the screen. In general, fade in animations are when a view animates from an alpha of 0 to a nonzero alpha level (it doesn’t necessarily have to be exactly 1 though it usually is). We could do a fade out animation if we started at a nonzero alpha (usually 1) and animated to an alpha of 0.

In addition to animating the alpha level, we can also animate the screen position of our views. Let’s demonstrate this by adding a UITextField to the top of our screen.

animations-3

We can animate it into view by initially having it off of the screen. It is difficult to position it out of bounds so we can do something else instead: position the UITextField at the final position, and we can move it out of the screen before the view is shown to the user in the viewWillAppear method.  First, add an outlet to it.

@IBOutlet weak var textField: UITextField!

Then override the viewWillAppear method and move it by some fixed value like the size of the screen.

override func viewWillAppear(_ animated: Bool) {
    textField.center.y -= view.bounds.width
}

The coordinate system that iOS uses is the top-left coordinate system. This means that the origin, or point (0,0) actually lies at the very top-left corner of the screen. The x direction increases to the right, and the y direction increases down, not up! So by subtracting the y position of this view by a large amount, like the screen width, this UITextField will appear to animate from the top to its final position, from the user’s perspective.

To animate this view, let’s use the same method in viewDidLoad. We’ll have to undo the change in viewWillAppear by adding view.bounds.width over a period of time.

UIView.animate(withDuration: 2) {
    self.textField.center.y += self.view.bounds.width
}

Let’s run our app and see this animation!

animations-4

The animate method on UIView has several overloads that give us finer grain control over our animation and some addition features!

UIView.animate(withDuration: 2, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.1, options: [], animations: {
    self.textField.center.y += self.view.bounds.width
}, completion: nil)

The additional features we get are a delay, spring motion, another list of options, and a completion handler closure. The spring parameters allows the views to have some “bounciness” when they animate. We’ll see this if we run the app.

animations-5

Notice that when the UITextField animates, it overshoots its final position by some amount, bounces back, and finally ends at its final position. This is the effect of the spring parameters on the UIView. By adjusting these parameters, we can add delightful spring animations to our views!

One interesting thing to note is that the animations closure is an escaping closure. Let’s have a small digression to discuss escaping and non-escaping closures. In Swift, closures are treated as first-class citizens. We know we can pass a method into another method like this:

func bakeCake(ovenTemp: Int, prepareIngredients: () -> Void) {
    prepareIngredients()
    // bake the cake
}

...

bakeCake(ovenTemp: 350) {
    // prep ingredients
}

We’re using a trailing closure to provide the method for the prepareIngredients argument. It should be no surprise that we can create a variable whose value is the value of that closure. We can do this outside of the method as well, but something interesting happens.

var prep: () -> Void = {}

func bakeCake(ovenTemp: Int, prepareIngredients: () -> Void) {
    prep = prepareIngredients
    // bake the cake
}

If you type this code into Xcode, we’ll get an error about “Parameter prepareIngredients is implicitly non-escaping.” We’ve reached the central point!

By default, all Swift closures are non-escaping or @noescape, which means that we can only call or use that closure inside of the method. In other words, the closure cannot escape the method. This guarantees to us that the closure must be used in that method. We won’t be able to store it and execute it later if we have a non-escaping closure.

If we wanted to pass in a closure and execute it later, we need to add @escaping to our parameter like this.

func bakeCake(ovenTemp: Int, prepareIngredients: @escaping () -> Void) {
    prep = prepareIngredients
    // bake the cake
}

Now we don’t get an error and we can run everything! To bring the digression to an end, notice that the animations closure of the animate method is an escaping closure. This means that our animation code might not be run inside the animate method, but stored in a member variable or in an animation queue and run after some processing. However, this processing is so small that we don’t really notice it.

This means that there’s no guarantee that our animation code is run in that same animation method, which doesn’t affect the way we write that code, but it is  something to be aware about.

Moving on with animations, notice we have an options parameter that we can configure. Let’s add a UIButton to our view right under the UITextField and we’ll animate it downwards while exploring the different animation options. Let’s create an outlet for it.

@IBOutlet weak var button: UIButton!

Now let’s take a look at some of the animation options. For example, we can ease in and out of the animation. That will make our view appear like it is accelerating at the start and decelerating at the end. It gives a sense of real-world motion. Objects just don’t go from standing still to moving instantaneously! They accelerate to that speed and decelerate back down.

Add .curveEaseInOut inside of the array brackets. We’re animating our button down the screen by some distance.

UIView.animate(withDuration: 4, delay: 0, options: [.curveEaseInOut], animations: {
    self.button.center.y += self.view.bounds.width
}, completion: nil)

Now let’s see that button animate!

animations-6

See how our button accelerates and decelerates? That’s natural motion! Other interesting options are the .autoreverse option, which will automatically undo the effects of the animation as soon as the first animation finishes, and .repeat, which will keep executing the animation.

There are many other properties that we can change to delight our user with animations. This does not mean that we should take every view in our app and add some animation to it! Animations are not meant to be applied to each view or even frequently to a particular view.

There are some considerations that we have to take into account when asking ourselves if we even need an animation. For example, consider a login screen with a username, password field, and a button. There’s no need to have an animation to make those views appear. However, it might be delightful to have a transition animation that moves from one view controller to the next.

Animations should not be very long either. If we had long animations, then our user is going to be waiting for the animation to finish before interacting with our app, and that is annoying to users to have to wait for our animation to finish. Ideally, animations should be fairly short and not too complicated. The purpose of animations is to delight our user, not to make the most flashy appearance!

Apple has design guidelines (called the iOS Human Interface Guidelines) that help developers better understand when animations are appropriate and in what manner to execute animations so they do not conflict with their guidelines.

In this post, we discussed how to perform different kinds of animations on views. We saw how to perform some of the simple animations like fading in and out. Then, we discussed some more complicated animations like spring motion. Next, we learned about some of the animation options for natural motion. Finally, we briefly discussed when to use animations and how to use them well. We built a small app that demonstrates the different kinds of animations.

]]>
Create a Contacts Manager using Core Data https://gamedevacademy.org/create-a-contacts-manager-using-core-data/ Mon, 21 Nov 2016 02:42:51 +0000 https://swiftludus.org/?p=1927 Read more]]> In this tutorial, we’re going to delve into Core Data and build an app to manage our contacts. We’re going to learn about creating a data model, add records to the model, fetch data, and display data using a list.

BUILD GAMES

FINAL DAYS: Unlock 250+ coding courses, guided learning paths, help from expert mentors, and more.

Download the source code for this post here.

Let’s create a new Single View Application called Contacts and select the Core Data check box.

core-data-1

Let’s set up our UI first. Let’s drag out a Table View Controller and embed it in a Navigation Controller by selecting the new UITableViewController and going to Editor -> Embed In -> Navigation Controller. Our storyboard should now look like the following.

core-data-2

We need to change the superclass of ViewController to UITableViewController instead of UIViewController and set ViewController to be the Class attribute (in the Identity Inspector) of the Table View Controller in our storyboard. Now we’ve wired up our view controllers!

Let’s start setting up the Table View Controller. First, select a prototype cell in the UITableView and change it’s identifier to contactsCell so that we can properly dequeue it. For the Style attribute, change it to Basic. We’ll just need a label to add the name of our contact. Change the title of the navigation bar to say Contacts by double-clicking on the center of it.

Speaking of adding contacts, let’s scroll to the bottom of the Object Library and add a Bar Button Item to the top navigation bar on the right in the shape of a plus symbol. Change the System Item attribute to Add to get a plus symbol.

core-data-3

We’ll add an action in ViewController that executes when that Bar Button Item is pressed. Control-Drag to create an action called addContact.

@IBAction func addContact(_ sender: UIBarButtonItem) {
}

Now we need to write Swift code to set up this UITableView. We need a String array to keep track of all of our contacts.

var contacts: [String] = []

We need to override the two primary methods for a UITableView to be populated. The implementation is simple because of our list of contacts!

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return contacts.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "contactsCell", for: indexPath)
    cell.textLabel?.text = contacts[indexPath.row]
    return cell
}

We simply return the size of the list of contacts for the first method. For the second method, we need to dequeue the cell using the identifier that we configured earlier! Then we need to set the text of the cell to be the corresponding entry in the contacts list.

The only thing left to implement, functionality-wise, is adding contacts to this list! We can use the action for the plus UIBarButtonItem to add content to that list of contacts using a dialog box. This is such a common thing to do that Apple created UIAlertController, and we can use it out-of-the-box!

We need to add an action to cancel and an action to add. We can add a UITextField to the main body of the dialog using addTextField(). The most involved part of this code will be the add action.

@IBAction func addContact(_ sender: UIBarButtonItem) {
    let dialog = UIAlertController(title: "New Contact", message: "Add the contact's name", preferredStyle: .alert)
    
    dialog.addTextField()
    dialog.addAction(UIAlertAction(title: "Cancel", style: .cancel))
    
    let addAction = UIAlertAction(title: "Add", style: .default) {
        [unowned self] action in
        
        if let contactName = dialog.textFields?.first?.text {
            self.contacts.append(contactName)
            self.tableView.reloadData()
        }
    }
    
    dialog.addAction(addAction)
    
    present(dialog, animated: true)
}

We’re using a trailing closure to provide code that will be executed when this add action is pressed in the dialog. We need to check if the text field has any text, and, if it does, we add that text to the list of contacts and reload our table view so that we can display the new data. The [unowned self] is there to ensure that we’re using proper memory management.

Let’s run our app! We can add to our UITableView!

core-data-4

But note that there’s no real persistent storage! If we were to quit our emulator or reboot our device, all of our data would be gone! This is where Core Data can help us. We can use Core Data to build a model for our contacts app, and we can persist our data through rebooting.

The reason for using Core Data (and not just a text file) is that we can store more complicated data and access it easily. Under-the-hood, Core Data uses a SQLite database as a means of persistent storage. We don’t have to know anything about SQLite to use it however! We get the full power of a database with the intuitive syntax of Swift when we use Core Data.

Click on the Contacts.xcdatamodeld to bring up the model.

core-data-5

Before we get started, we should discuss some terminology so that we’re on the same page. Think of an entity as a Swift class: it encapsulates all of the properties of one “thing”. An attribute is then like a Swift property: it defines some information about an entity. Each entity can have any number of different attributes of different types.

Let’s start by creating an entity called Contact using the Add Entity button at the bottom. In the main pane, let’s add an attribute called name of type String.

core-data-6

This is all we need to setup our data model! We could add more attributes like phone number or email if we wanted to make our model more complex.

Now that we’ve created our data model, we need to be able to save and retrieve data to and from this model. We can do this using an NSManagedObject, which represents a single record stored in Core Data. We have to use this whenever interacting with Core Data.

For example, if we wanted to add a new contact to Core Data, we have to use NSManagedObject. If we wanted to fetch objects from Core Data, we’d get back an array of NSManagedObjects. Editing and deleting also involve interacting with NSManagedObject. In general, interacting with Core Data almost always requires an NSManagedObject.

To account for this, we need to start using NSManagedObjects in our Swift code. However, NSManagedObject is in the CoreData framework that we need to import at the top of our Swift file, right under import UIKit.

import CoreData

Now we can start using NSManagedObject. We can start by changing the contacts array to hold NSManagedObject instead of String. We also need to extract the name attribute from that NSManagedObject when we set the text of the cells. We can use the value(…) method to extract an attribute and cast it to the appropriate Swift type.

var contacts: [NSManagedObject] = []

...

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "contactsCell", for: indexPath)
    let contact = contacts[indexPath.row]
    cell.textLabel?.text = contact.value(forKeyPath: "name") as? String
    return cell
}

Let’s create some utility methods that will encapsulate retrieving from and inserting into Core Data. The first thing we need to do is to have a method that return an NSManagedObjectContext. This is our connection to Core Data. We need an NSManagedObjectContext to save and retrieve data. We can get one using our UIApplication.

func getContext() -> NSManagedObjectContext {
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    return appDelegate.persistentContainer.viewContext
}

Now that we have a context, we can add a method to insert into Core Data.

func storeContact(_ contactName: String) {
    let context = getContext()
    let entity = NSEntityDescription.entity(forEntityName: "Contact", in: context)
    let contact = NSManagedObject(entity: entity!, insertInto: context)
    
    contact.setValue(contactName, forKey: "name")
    
    do {
        try context.save()
        contacts.append(contact)
    } catch let error as NSError {
        let errorDialog = UIAlertController(title: "Error!", message: "Failed to save! \(error): \(error.userInfo)", preferredStyle: .alert)
        errorDialog.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        present(errorDialog, animated: true)
    }
}

We need to first grab a context and say which entity we want to create. Then we create an NSManagedObject! After we’ve instantiated a new NSManagedObject, we can start setting its attributes using the setValue(…) method. We supply the value we want to save for a particular attribute. Finally, we have to remember to call context.save()! This will actually commit the data to persistent storage! Don’t forget it!

Notice that we use a do-catch block. Since we’re dealing with a form of file I/O, there’s the chance that something could go wrong! If that is the case, we report that error to the user through an alert dialog.

We also need to retrieve all of our contacts from Core Data to put into the contacts array for the UITableView to get data from. We can create a method for this as well.

func fetchContacts() {
    let context = getContext()
    let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Contact")

    do {
        contacts = try context.fetch(fetchRequest)
    } catch let error as NSError {
        let errorDialog = UIAlertController(title: "Error!", message: "Failed to save! \(error): \(error.userInfo)", preferredStyle: .alert)
        errorDialog.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        present(errorDialog, animated: true)
    }
}

We can retrieve all contacts using NSFetchRequest and passing in the name of the entity we want to retrieve. This method will set the contacts array to the result. In the event of an error, we do the same thing as we did in the previous method: report the error to the user.

Now that we have these methods, we can use them. When adding a new contact, we can use the storeContact method in the addContact IBAction.

@IBAction func addContact(_ sender: UIBarButtonItem) {
    let dialog = UIAlertController(title: "New Contact", message: "Add the contact's name", preferredStyle: .alert)
    
    dialog.addTextField()
    dialog.addAction(UIAlertAction(title: "Cancel", style: .cancel))
    
    let addAction = UIAlertAction(title: "Add", style: .default) {
        [unowned self] action in
        
        if let contactName = dialog.textFields?.first?.text {
            self.storeContact(contactName)
            self.tableView.reloadData()
        }
    }
    
    dialog.addAction(addAction)
    
    present(dialog, animated: true)
}

Finally, we need to fetch the set of contacts before the UITableView loads so that it can use the contacts list. Since we’ve implemented the fetchContacts method, we simply need to call it somewhere in our code.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    fetchContacts()
}

We can call it in the viewWillAppear method so that the contacts are loaded first!

Now let’s run our app! Add a name to it and quit the emulator or reboot your device. When we re-open the app, our data is all still there! Core Data works!

core-data-7

In this post, we explored Core Data and built an app to manage our contacts that used Core Data. We learned about the importance of data persistence and how we can use Core Data to solve that problem. We built a Core Data model and used it to store data. We learned how to insert information into Core Data and how to fetch data from it as well. We built an app that uses Core Data for persistent storage.

]]>