π±
My first Memory
π€
π
An introduction to iOS development with Swift.
A memory game implementation fetching images from Instagram. This project aims to introduce you to iOS development with Swift disregarding of your current skill level.
The app defaults to fetch the Instagram images of Taylor Swift (Alex: "Hehe he he...
Installation
OBS! This project has been migrated to Swift 3.0, so you need Xcode 8 to open it
There is no need for any installation per se, you only need to download this project. You download the project by pressing the big green Clone or download button you see in the top right side of this page. You can either download the project as a zip, or you can use git to download the project by opening the terminal and entering:
git clone <PASTE_GITHUB_URL_HERE>
After you have download the project, open the file called SwiftIntro.xcworkspace (not SwiftIntro.xcodeproj).
iOS development
All the screens you see are called UIViewController
which consists of smaller view elements called UIView
. Buttons (UIButton
), text labels (UILabel
), textfield for text input (UITextField
) are all subclasses of the superclass UIView
. All instances of UIViewController
have a view (UIView
), which is the root view, the canvas in which you can add buttons, labels and lists (UITableView
).
iOS development follows the architecture called MVC by default, which stands for Model-View-Controller.
MVC
The Model, the View and the Controller are three different areas of responsibility within the app.
The idea is that the Controller acts as a coordinator, fetching data from either internet or a local database, stored in Models and passing those models into Views that can display the data.
ViewControllers (View and Controller)
For every screen in your app you create your own subclass of UIViewController
. To that you add all the views you want your screen to consist of. You can do this in two ways, either you do it using InterfaceBuilder or in code.
-
In InterfaceBuilder (IB in short), which is a great drag and drop tool which aims to be a WYSIWYG (What You See Is What You Get). Allthough in Xcode 7.x.x IB is not capable of rendering all the views and different styling of those (rounded corners, blur effect etc..). Xcode 8 will be more capable os this. There are two different ways to use IB you can either use Storyboards (UIStoryBoard) or you can use .xib files (also called nib files).
-
In a storyboard you can create several UIViewControllers, maybe you create a SignInViewController, SignUpViewController and a ResetPasswordViewController. In the Storyboard you can create flows between these ViewControllers, e.g. you can create a Reset Password UIButton in the SignInViewController and then you can define that when the user presses that button, the ResetPasswordViewController is shown to the user. These flows between UIViewControllers are called Segue (pronounced "segway").
-
The Xib file approach is an older technique which only allows for one UIViewController per Xib file. In Xib files you don't need to put UIViewControllers, you can instead put UIViews. The advantage of this is that they can be used by different UIViewControllers or UIViews in your project. So they are reusable through out the project. A good example of when to use Xib files is when you want to create a list (
UITableView
) or grid (UICollectionView
) of views. This project uses aUICollectionView
for displaying memory cards in a grid. The cards areUICollectionViewCells
(subclass ofUIViews
...). So each item in a list or grid of view is called a cell. It is recommended to create each unique cell class in a separate Xib file. -
Creating views in code (or programmatically as you often say...). All
UIButtons
,UILabels
,UITableviews
,UITextViews
,UITextField
etc you drag and drop in Interface Builder can also be created and added to your view using pure Swift code. The syntax for this is typically:
private func myMethodCreatingLabel() {
let resetPasswordLabel = UILabel()
resetPasswordLabel.text = "Have you forgot your password?"
resetPasswordLabel.textAlignment = .Center
resetPasswordLabel.textColor = UIColor.redColor()
view.addSubview(resetPasswordLabel)
/* Here we should proceed with creating NSLayoutConstraints
which are rules that tells iOS where to place the view and
how big the view should be.
*/
}
Model
A model is a struct
or class
that holds data. In this project we fetch data, sent over HTTP GET on the JSON format from Instagram. The images from Instagram are stored in a struct
called Cards.swift. Structs and classes may seem very similar, and in terms of syntax they are. But the behave very differently in terms of memory and reference, after you have worked with this project you can have a look at this WWDC video explaining the difference.
How to write good code
iOS apps actually have a quite confusing MVC pattern, because the UIViewController
is the controller, but it also has its own UIView
, so in a way the UIViewController
is also the view UIViewController
classes you create tend grow to many hundreds lines of code. This project aims to not have any Massive UIViewController
. The project has four UIViewControllers
(GameVC, SettingsVC, GameOverVC and LoadingDataVC) and the biggest is not even 100 lines of code. Try to aim for that less than 100 lines of code! Unfortunatly it's rare to work in a project where any UIViewController
is less than 100 lines of code. So if you make it a habbit then you will be a skilled iOS developer from start UIViewControllers
, or to use extensions
, here is a great article on how extensions
of UIViewController
can make your UIViewControllers
smaller.
Another general guideline is to try to keep under less than 200 lines of code for all files (classes, structs or enums). When you notice that a class grows, maybe you can try to split it into two or three classes instead. In fact all files in this project is less than 100 lines of code, with one exception - MemoryDataSourceAndDelegate - which still is less than 200 lines.
SwiftLint
A good way to enforce writing good code is to install a tool called SwiftLint which we have used durint the development of this project. If you have Homebrew installed you can install it using this terminal command:
brew install swiftlint
Tasks
π
This looks interesting
-
Change the color
β€οΈ π π π π of the Play! button. -
Change the the backgroundcolor of the cards.
(tip: check out CardCVCell.xib or CardCVCell.swift) -
Change the duration of the flip card animation.
-
Change the username placeholder.
(tip: check out the method setupLocalizableStrings() in SettingsVC, you also need to check out the file called Localizable.strings for each language) -
Add support for your third favourite language
π«π· πΈπΎ π―π΅ , you need to test this as well (by pressing β+β¬ +H in the simulator you go to its home screen, where you can find the Settings app where you have to change the system language to the one you just added.) -
Change the flip card animation from using a horizontal flip to a vertical.
(tip: check out the flipCard() method in the CardCVCell class. Here is the documentation for animations)
π°
I think I've got a good grip of it
-
Change the Quit button title, which currently is a text with the char X, to use an image
π instead. -
Set the background of the memory Card to be show an image
π instead of just a color.
(tip: check out CardCVCell.xib or CardCVCell.swift) -
In the section How to write good code we discussed the goal of writing small files, and the class MemoryDataSourceAndDelegate with its almost 200 lines of code was mentioned. Can you split this class into several smaller classes that makes sense, so that no class is more than 100 lines?
-
Switch the position of the Restart button with the Quit button.
(tip: don't delete the buttons...π then you have to recreate the Segues ...) -
Save the best score (lowest clickCount for each level) a user has scored and present this score in the GameOverVC.
(Tip: Checkout NSUserDefaults for saving.) -
It is currently possible for a user to flip a third card while the flip animation of the two previous cards has not yet finished. Address this issue.
-
Create a timer
β² NSTimer that counts the time for a game session. Display this time in the GameOverVC after the game has finished.
π¦
Bring it on
-
Display a timer
β² NSTimer (could be the one you created in task 8 in I think I've got a good grip of it ) that is counting upwards in the GameVC showing elapsed timeβ° since game start. -
When you press the Restart button from GameOverVC the cards will have the same position as before, this makes it easy to cheat! Your task is to shuffle the cards before restarting the game.
-
Implement white space handling for the username textfield.
-
Change the feedback message in GameOverVC from Well done to a dynamic title that changes according to how well it went. Some examples strings: Awesome, Not sooo bad, That was Horrible etc. This string should not be directly dependent on only Level, or only clickCount, but rather..?
-
Currently the project uses hard coded keys for localized strings (the key itself is a string), which is the standard iOS pattern - but it smells! Instead you can introduce an Enum for all the localized strings. So that you will be able to write something like this:
restartButton.setLocalizedTitle(LocalizedStrings.Restart)
(Tip: Either you do this by yourself, or you can use SwiftGen for this, if you know how to install it...)
-
Add some Error
β handling, e.g displaying a message if the Instagram username doesn't exist, or if no images could be loaded. -
Make it possible to set the number of cards to a custom number. Currently the number of cards are determined base on which difficulty level you chose in the SettingsVC.
-
Enable Landscape mode for all the views.
-
Fetch the images from another source than Instagram. Maybe you can fetch the images from FourSquare, using its API, Flickr or Twitter. Please note all the above mentioned alternative image sources require an API token/secret. So you have to create those in order to use the APIs. Then you have to change the JSON parsing of the Cards model. You also need to modify the Photos
π· route in the Router class to go against the correct URL.
Authors
Alexander Cyon @Redrum_237 LinkedIn
Alex started with app development in 2010, his first app project was the development of an Android app for one of the largest computer stores in Sweden. But it wasn't until 2013 that Alex started with iOS development. He got hooked on iOS development even though the syntax of Objective-C feels really outdated today. In 2014 he did his first Swift project for Avanza Bank and instantaneously fell in love
Miriam Tisander LinkedIn
Miriam has worked with iOS development since 2015, she loves Swift but is currently working with Objective-C