Note from Ray: This is a brand new Swift tutorial released as part of the iOS 8 Feast. Enjoy!
Some gamers love fast-paced shoot-em-ups, while others love the anticipation of “waiting games” such as Farmville, Clash of Clans, or Tiny Tower.
The idea behind “waiting games” is that certain actions in your game – such as growing crops, building units, or constructing floors of a hotel, take time.
The user can only make progress if they return to the game frequently. This is good for two reasons:
- Good for small time bursts. For example, a player can farm their crops if they have a spare 30 seconds while waiting in line. This is great for mobile games.
- Good for the freemium monetization strategy. For example, since users are returning to your apps frequently, you have more opportunities for monetization – such as allowing users to skip waiting if they feel impatient!
In this tutorial you’ll implement a simple example of a waiting game — Kookie Kiosk — that sells shakes and cookies. Your players can get rich by buying and selling wares.
You’ll learn how to implement the basic mechanisms of any successful waiting game, including:
- How to create a simple state machine.
- How to handle timed events.
- How to send local notifications.
These skills should prove useful to you, even as you build more complicated simulations.
Note: This Swift tutorial assumes you have working knowledge of Sprite Kit and Swift. If you’re new to Sprite Kit, check out our Sprite Kit Swift Tutorial for Beginners our our full book, iOS Games by Tutorials. For an introduction to Swift, check out this beginner Swift tutorial here.
Getting Started
Download the template project here. Extract the file to a convenient location on your computer. Start up XCode, select File\Open, then select the KookieKiosk-Swift.xcproject file and click Open.
Select the iPad 2 simulator in the upper left corner of XCode and click Play to test the app; you’ll see a very crowded screen as shown below:
Don’t worry — you’ll soon clean this up and make it playable.
Here are the main files in this project:
- Constants.swift: This file contains all of the important constants for your project. The enum
ZPositionensures the layers of the game are drawn in the correct order.TimeScalegives you control over the speed of the game — you don’t want to wait around for what seems like forever to test your game, do you? :] - AppDelegate.swift: The only change to the default AppDelegate is a preload of two sound files to avoid any delay when the sound files are played the first time.
- GameScene.swift: This contains the core logic of your game. It contains a list of all items in the kiosk and how much money you have in the game. At startup, all on-screen elements are added to
GameScene. This class also loads and saves a .plist file containing important data about the items available in the kiosk. Finally, but most importantly, it responds to changes in the game state. - GameViewController.swift: This loads the initial scene. Nothing’s changed here; it’s still the basic Xcode template for a SpriteKit game.
- StockItem.swift: This class represents a single item in the kiosk. It stores important properties of the item like type, flavor, and amount, as well as a few constants like the maximum amount the player can have of that item, the position on the screen, prices for restocking and selling this item, and of course the speed at which this item is stocked and sold. The class already contains two helper methods which draw the price label and the timer while the item is stocking.
- Customer.swift: This class draws a customer on-screen and stores the item the customer wants to buy.
- GameStateDelegate.swift: This protocol defines a method that responds to changes in the amount of money you have in the game. You’ll add more methods to this protocol later.
- ProgressBar.swift: Kookie Kiosk makes use of several progress bars to show how much longer the player will have to wait for something. This protocol defines the structure every progress bar should have.
- DiscreteProgressBar.swift: This is a progress bar with a fixed amount of steps. Each step corresponds to one image. All images for a single progress bar must share the same base name followed by a number, such as “customer_1″.
- ContinuousProgressBar.swift: This progress bar has an almost infinite number of steps. In contrast to
DiscreteProgressBarit only uses two images: one image where the progress bar is empty and one where it is full.
Have a little read through the code, and when you think you’ve absorbed enough of it move on to the next section where you’ll implement the state machine for your game.
Implementing the State Machine
In Kookie Kiosk you make a living by selling shakes and cookies. But you can’t sell something you don’t own (out-of-cookies exception), or restock a cookie platter that is already full (cookie overflow exception). The easiest way to manage the varying conditions of your game is with a state machine.
A state machine defines several states and the transitions between them. The state machine of Kookie Kiosk can be represented by the following state diagram:
Each stock item can be in one of four separate states:
- Empty: At the start of the game, a stock item is always empty. If you have sufficient funds, you can buy a new item by tapping the item.
- Stocking: Once you buy an item you’ll need to wait while the game replenishes your item stock.
- Stocked: When an item is fully stocked it switches to the stocked state.
- Selling: Once an item is stocked, the player can tap the stock item and start selling it to customers. When an item is sold out it returns to the empty state.
You’ll also need to provide some visual prompts to show the various states. Here are the images that correspond to each state:
Enough background — it’s time to get coding.
Add the following code to the bottom of Constants.swift:
enum State: Int { case empty, stocking, stocked, selling } |
This defines an enum with a value for each of the four states.
Next, add the following property at the end of the list of properties in StockItem.swift (after the declaration of priceTag):
var state : State |
Note that you don’t specify an access modifier above such as public or private. This creates a property with internal visibility so that it can be accessed from anywhere within the target in which it’s defined — in this case the entire Kookie Kiosk project.
Note: At this point Xcode will display the error message “Property ‘self.state’ not initialized at super.init call”. Swift requires that you assign values to all properties of a class before you call the initializer of its superclass.
Add the following lines to init in StockItem.swift just before super.init():
var stateAsObject: AnyObject? = stockItemData["state"] var stateAsInt = stateAsObject as Int state = State.fromRaw(stateAsInt)! |
In the code above you retrieve the state from the dictionary stockItemData, which contains all information about the stock item loaded from gamedata.plist shown below:
In the first line above you retrieve the value from stockItemData stored under the key “state” and cast it to an Int. Then you map the value of the Int to the corresponding value of the enum State and assign the result to the state.
The exclamation mark here “unwraps” the optional value of fromRaw() and tells Swift you’re sure the result of this call will never be nil — if it ever was, your code would crash! When you give these guarantees to Swift you need to be completely certain that what you’re declaring is true. :]
state = State.fromRaw(stockItemData["state"] as AnyObject? as Int)! |
Now that you’ve loaded the state of the stock item, you’ll need to make sure it’s been stored.
Add the following line to data() in StockItem.swift, right before the return statement:
data["state"] = state.toRaw() |
This line sets the value for the key state to the raw Int value of the state for the stock item.
That takes care of loading and storing the states — but if you don’t actually display the states to your player they won’t have much fun in the game! Time to add some code for displaying the state changes.
Cleaning up the Interface
Add the following method to StockItem.swift:
func switchTo(#state : State) { self.state = state switch state { case .empty: stockingTimer.hidden = true sellButton.hidden = true priceTag.hidden = false case .stocking: stockingTimer.hidden = false sellButton.hidden = true priceTag.hidden = true case .stocked: stockingTimer.hidden = true sellButton.hidden = false priceTag.hidden = true progressBar.setProgress(percentage: 1) case .selling: NSLog("Try to do this last state on your own. You can do it!") } } |
This method cleans up the mess that greeted you at first run. It contains one switch statement that distinguishes between the four states of your stock item. For each state, it sets hidden appropriately for the stocking timer, sell button and price tag.
For example, only the price tag should be visible in the empty state; therefore you set the stocking timer and the sell button’s hidden property to true, while you set the price tag’s hidden property to false. The other states follow similar logic.
Eagle-eyed readers will have noticed that the final case of the switch hasn’t been implemented yet. Try to solve it on your own — you should have enough information to do so. Take a peek at the spoiler below to check your work:
Add the following line to the end of init() in StockItem.swift:
switchTo(state: state) |
This will to initialize the game to the default state.
Build and run your project; you see a much cleaner user interface with all stock items in the empty state, displaying their price tag, and waiting for the player to act as shown in the following screenshot:
The kiosk is all ready for some user interaction — fortunately you’re taking care of this next!
Switching States
Add the following method to StockItem.swift:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { switch state { case .empty: var enoughMoney = gameStateDelegate.gameStateDelegateChangeMoneyBy(delta: -stockingPrice * maxAmount) if enoughMoney { switchTo(state: State.stocking) } else { var playSound = SKAction.playSoundFileNamed("hit.wav", waitForCompletion: true) runAction(playSound) let rotateLeft = SKAction.rotateByAngle(0.2, duration: 0.1) let rotateRight = rotateLeft.reversedAction() let shakeAction = SKAction.sequence([rotateLeft, rotateRight]) let repeatAction = SKAction.repeatAction(shakeAction, count: 3) priceTag.runAction(repeatAction) } case .stocked: switchTo(state: State.selling) default: break } } |
The above method operates on the two states that allow user interaction: empty state and the stocked state.
In the empty state, you first notify gameStateDelegate that the player’s money will possibly change. You then checks whether the player has enough money to make the purchase. If so, then call switchTo to change to the stocking state. If not, then let the player know they are short on funds by playing a small sound effect and shaking the price tag.
Handling the stocked state is even easier; you simply call switchTo with the selling state. There are no additional conditions that need to be met for this transition, and as you will see shortly, this puts the item in a state where it will update over time.
Updating States as Time Progresses
So the player pushes the button to buy the item, but…nothing happens!
Your game should update the item stock as time passes. To do this, you’ll first need to know the last time the state changed — you can use this to calculate how much time has passed, and consequently, how far along the stocking process should be.
Add the following property to StockItem.swift, right after the state property:
private var lastStateSwitchTime : CFAbsoluteTime |
You use CFAbsoluteTime above to refer to a specific point in time. Even if the player restarts the game you’ll still need to know exactly when the event happened in order to update the stock properly.
Add the following line to init in StockItem.swift, just before super.init():
lastStateSwitchTime = stockItemData["lastStateSwitchTime"] as AnyObject? as CFAbsoluteTime |
This line probably looks familiar — it’s almost the same code that loaded the state of the stock item, but instead loads the time of the last state change.
Add the following line to data() in StockItem.swift, right before the return statement:
data["lastStateSwitchTime"] = lastStateSwitchTime |
This line adds an entry for the last state switch time to the data dictionary stored in gamedata.plist.
Now you need to make sure that lastStateSwitchTime is assigned the proper value while the game is running.
Add the following line of code to the beginning of switchTo() in StockItem.swift:
if self.state != state { lastStateSwitchTime = CFAbsoluteTimeGetCurrent() } |
This ensures that you’ve actually changed states. If so, then update lastStateSwitchTime to the current time. You can always get the current time using the ever-helpful CFAbsoluteTimeGetCurrent().
Stocking Your Items
Now that you know the absolute time of the last state switch, you can use it to show some progress indicators to your player. You’ll start by updating the countdown that shows the player how long they need to wait for a purchased stock item to restock.
Add the following method to StockItem.swift:
func updateStockingTimerText() { var stockingTimeTotal = CFTimeInterval(Float(maxAmount) * stockingSpeed) let currentTime = CFAbsoluteTimeGetCurrent() var timePassed = currentTime - lastStateSwitchTime var stockingTimeLeft = stockingTimeTotal - timePassed stockingTimer.text = String(format: "%.0f", stockingTimeLeft) } |
In this method you set the text of the stockingTimer to the time remaining until stocking is complete. First, you calculate the amount of time it takes to fully stock the item. You do so by multiplying stockingSpeed and the maximal amount of the stock item and then cast it to CFTimeInterval. Next, you store the current time in a temporary variable to calculate how much time has passed since the last state change.
The time to restock the item is now simply the total time minus the time that has passed to this point, as demonstrated in the following image:
Finally, you set set the text to the remaining time so the user can see when the item will be fully stocked. Since you only want to display whole seconds to your player you use the format specifier %.0f, which tells Swift to display a float variable with zero digits after the decimal.
Now you need a method to update the display of the stock item during stocking and selling.
Add the following method to StockItem.swift:
func update() { let currentTimeAbsolute = CFAbsoluteTimeGetCurrent() var timePassed = currentTimeAbsolute - lastStateSwitchTime switch (state) { case .stocking: updateStockingTimerText() amount = min(Int(Float(timePassed) / stockingSpeed), maxAmount) if amount == maxAmount { switchTo(state: .stocked) } default: break } } |
First, you calculate how much time has passed since the last state switch. Then you switch states depending on the current state of the stock item. If the state is stocking, you call the helper method updateStockingTimerText you created earlier.
Next, you update the item amount which is simply the time elapsed divided by the stocking speed. Of course, the player can never stock more items than maxAmount, so you use min to limit the amount to maxAmount. Finally, you check whether the new amount is equal to maxAmount. If so, then change the state to stocked.
This is because it will make certain game behaviors (such as calculating the game’s state after the player returns to your game after a long time) much easier, as you’ll see later in this tutorial.
The only thing left to do is call update() for every stock item.
Override update() in GameScene.swift as follows:
override func update(currentTime: CFTimeInterval) { for stockItem in stockItems { stockItem.update() } } |
update() is a SpriteKit method defined on every SKScene and called for every frame. Here you iterate over all stock items and call update() on each.
Build and run your project; tap on a stock item and you’ll see the timer count down slowly to zero.
When the countdown reaches zero, the stock item switches to the state stocked where the item appears as full with a coin displayed in front.

The coin indicates that the items are ready to sell – so let’s work on that next!
Selling Your Items
Stocking is, of course, only half of the timed interactions in your game. As soon as the item is fully stocked, you can start selling your wares.
Add the following code to update() in StockItem.swift, inside the case statement right before the default case:
case .selling: let previousAmount = amount amount = maxAmount - min(maxAmount, Int(timePassed / Double(sellingSpeed))) var amountSold = previousAmount - amount if amountSold >= 1 { gameStateDelegate.gameStateDelegateChangeMoneyBy(delta: sellingPrice * amountSold) progressBar.setProgress(percentage: Float(amount) / Float(maxAmount)) if amount <= 0 { switchTo(state: .empty) } } |
First, you store the current amount of the item in previousAmount. You then calculate the new amount by subtracting the quotient of timePassed and sellingSpeed from maxAmount. Again, you need to limit the number of items that can be sold to maxAmount. Now the number of items sold is simply is the difference between the previous amount and the new amount.
In order to limit the number of calls to progressBar and gameStateDelegate, you check whether at least one item has been sold since the last call to update. If so, notify gameStateDelegate about the change in the player’s funds, then set the progress bar to the value of the amount sold divided by the maximum amount available.
Finally, you check whether the stock item sold out by comparing the amount remaining to 0. If you’re sold out of the item, set the state back to empty. Your state machine is now complete!
Build and run, buy some cookies to sell, and then click the coin to start selling. You will see your cookies sell over time, increasing your money accordingly:

w00t I sold a cookie – I’m rich!
TimeScale in Constants.swift to something less than 1 and things will run a lot faster.
Introducing Your Customers
In order to spice things up, many waiting games have events that trigger at random points in time. For example, in Tiny Towers, specialists make an appearance from time to time to dramatically boost progress.
You’ll implement a similar concept for the Kooky Kiosk. Your player will have to serve demanding customers that appear randomly at the kiosk. Serving these customers will let the player earn money.
Add the following property to GameScene.swift, right below the moneyLabel property:
var customer : Customer? |
This stores the current customer using the Customer class already implemented in the starter project. For the moment you’ll only serve one customer at a time.
Now you need to handle the timing of your customers’ arrivals.
Add the following properties to GameScene.swift, right below the customer property:
var timeOfLastCustomer : CFAbsoluteTime = CFAbsoluteTimeGetCurrent() var timeTillNextCustomer : CFTimeInterval = CFTimeInterval(Float((arc4random() % 15 + 15)) * TimeScale) |
The initial time the last customer appeared will be the startup time of the app since no customers have appeared yet. You also store a time interval that indicates how many seconds it will take for the next customer to appear. For this, you use a random value between 15 and 30 seconds. You then multiply this interval by TimeScale to control the rate at which customers appear.
Add the following code to the end of update() in GameScene.swift:
// 1 Check whether it is time for a customer to appear let currentTimeAbsolute = CFAbsoluteTimeGetCurrent() if customer == nil && currentTimeAbsolute - timeOfLastCustomer > timeTillNextCustomer { // 2 Make a list of potential wishes the customer could have var potentialWishes = [StockItem]() for stockItem : StockItem in stockItems { if stockItem.state == State.selling || stockItem.state == State.stocked { potentialWishes.append(stockItem) } } // 3 Select one of the potential wishes randomly and spawn the customer with it if potentialWishes.count > 0 { var random = arc4random() % UInt32(potentialWishes.count) var randomStockItem = potentialWishes[Int(random)] customer = Customer(type: randomStockItem.type, flavor: randomStockItem.flavor) customer!.position = CGPoint(x: frame.size.width + customer!.calculateAccumulatedFrame().size.width / 2, y: customer! .calculateAccumulatedFrame().size.height / 2) // 4 Animate the customer var moveLeft = SKAction.moveBy(CGVector(dx: -customer!.calculateAccumulatedFrame().size.width, dy: 0), duration: 1) customer?.runAction(moveLeft) addChild(customer!) } } |
This is a lot of code, but the logic is quite straightforward. Taking each numbered comment in turn:
- First you check how much time has passed since the last customer appeared. If it’s greater than the generated time interval, it’s time to spawn a new customer.
- Your customers have very specific wishes. To keep the game fun, these wishes are limited to the types and flavors of items that you currently have available — that is, items that are either fully stocked or are currently not sold out. Add all items that match this criteria to the list of potential wishes.
- Select a random index of the stock items from the list of potential wishes, then create a new customer wish from the type and flavor of the randomly selected item.
- Finally, make the customer appear from the right border of the screen. Using a simple
SKActionyou move it from the outside of the screen just until it’s entirely on screen.
Build and run your app; when you have available items, a customer will appear randomly and place an order at your kiosk.
You’ll have to disappoint this customer, unfortunately, since you have no way to fulfill their wish at this point. You’d better add this functionality and hope your customer’s bark is worse than their bite!
Declare the following method in GameStateDelegate.swift:
func gameStateServeCustomerWithItemOfType(#type: String, flavor: String) |
At this point XCode will display the error message Type ‘GameScene’ does not conform to protocol ‘GameStateDelegate’.
To fix the error, implement the missing method in GameScene.swift as follows:
func gameStateServeCustomerWithItemOfType(#type: String, flavor: String) { // 1 Check if the player has served the correct item for the customer if customer?.type == type && customer?.flavor == flavor { gameStateDelegateChangeMoneyBy(delta: 50) var playSound = SKAction.playSoundFileNamed("coin.wav", waitForCompletion: true) runAction(playSound) } else { var playSound = SKAction.playSoundFileNamed("hit.wav", waitForCompletion: true) runAction(playSound) } if customer != nil { // 2 Clean up customer var moveRight = SKAction.moveBy(CGVector(dx: customer!.calculateAccumulatedFrame().size.width, dy: 0), duration: 1) customer!.runAction(moveRight, completion:{ self.customer?.removeFromParent() self.customer = nil }) // 3 Setup spawn of next customer timeOfLastCustomer = CFAbsoluteTimeGetCurrent() timeTillNextCustomer = CFTimeInterval(Float((arc4random() % 15 + 15)) * TimeScale) } } |
Again, this is a fair bit of code, but it’s relatively simple when explained out:
- You first check if the type and the flavor of the stock item correspond to the customer’s wish. If so, add $50 to the player’s funds and play a sound effect. Otherwise, play a sound effect indicating that you haven’t satisfied this customer’s wish. Play this sound as well if there’s no customer at the current time.
- Next, remove the customer sprite using an instance of
SKActionthat moves the customer off to the right and off the screen. As soon as the customer sprite is off the screen, remove the sprite from the scene and set it tonil. - As soon as the customer leaves the scene you also need to schedule the time when the next customer will arrive. Set the time of the last customer to the current time. The interval until the next customer appears will again be a random value between 15 and 30 seconds.
All that’s left here is to call your new method from touchesBegan() in StockItem.swift like so (add this inside the case statement right before the default case):
case .selling: gameStateDelegate.gameStateServeCustomerWithItemOfType(type: type, flavor: flavor) |
To try this out, build and run and build some cookies. When a customer arrives, tap the cookies once to start selling, and then again to give a cookie to the customer.
Now you can serve your customers in record time — and rack up the profits while you’re at it!

Serving your customers quickly is the key to success!
Sending Notifications
Your game looks and plays great — but eventually the player will leave the game and you’ll want to draw them back to play some more. You can send updates about changes in the game state to your player using notifications.
Notifications can do the following:
- Display a short text message
- Play a brief sound
- Set the badge on the app icon
Here’s what your notifications will look like at the moment they’re sent:
But what if the player isn’t looking at their the device when the notification is received? Don’t despair! Players can see a list of missed notifications by pulling down the context menu from the top like so:
And even if the player doesn’t open the context menu, you can still show them that there is something happening in the game by updating the badge on the icon, like so:
Time to draw your user back into the game — head into the next section to learn how to add these notifications to your game.
Comparing Local and Remote Notifications
There are two different ways to inform players about background changes in an app: local notifications and push notifications.
While both look and sound the same, there are major differences from the viewpoint of a developer:
- Local Notifications are triggered directly on the device. They are very easy to implement, but the number of scheduled messages is limited to 64 at one time.
- Push Notifications are triggered by a remote server and pushed to the device. Hence they are more complicated to implement. The number of scheduled messages is not limited.
For your Kookie Kiosk game local notifications will be more than sufficient. Even if you schedule a notification for each of the seven items in your kiosk, you’ll still have 57 notifications you could send. You’re barely making a dent at this point. :]
Asking for User Permission
Before you can send any notifications to your players, you need to ask for their permission — otherwise the notifications won’t be displayed. You could do this anywhere in your application, but it’s most useful to do it at start up.
Open AppDelegate.swift and replace the application:didFinishLaunchingWithOptions method with the following code:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool { // Override point for customization after application launch. let notificationSettings = UIUserNotificationSettings(forTypes: UIUserNotificationType.Alert | UIUserNotificationType.Sound | UIUserNotificationType.Badge, categories: nil) UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings) return true } |
You call this method on application launch. First, you define UIUserNotificationSettings to support alerts combined with sound and the privilege to set the app badge. Then you register those settings with your UIApplication using registerUserNotificationsSettings.
Build and run, and you’ll see the following dialog appear:

Tap OK to allow notifications. (If you tap Don’t Allow, notifications would not appear of course).
Note that after the first run, this dialog won’t show up again. Instead, the app will use the value from the Settings app, where the user can change the rights granted to your app at any time as shown below:
Scheduling Notifications
Equipped with the necessary rights, you are now free to schedule notifications. As most of your notifications are similar in structure, you’ll create a small helper method to schedule a notification.
Add the following method to GameScene.swift:
func scheduleNotificationWith(#message: String, intervalInSeconds: NSTimeInterval, badgeNumber: Int) { // 1 Create empty notification var localNotification = UILocalNotification() // 2 Calculate notification time using NSDate var now = NSDate() var notificationTime = now.dateByAddingTimeInterval(intervalInSeconds) // 3 Set properties of your notification localNotification.alertBody = message localNotification.fireDate = notificationTime localNotification.timeZone = NSTimeZone.defaultTimeZone() localNotification.applicationIconBadgeNumber = badgeNumber localNotification.soundName = UILocalNotificationDefaultSoundName // 4 Schedule the notification UIApplication.sharedApplication().scheduleLocalNotification(localNotification) } |
The above method builds the notification from a message, a time interval, and the updated badge number as follows:
- First, you create an empty notification using
UILocalNotification(). - Next, calculate the time of the notification. You need to store the time as an
NSDate. You can easily get the current time stamp using the empty constructor ofNSDate, but to get anNSDateobject for a specific time in the future you can usedateByAddingTimeIntervalwhich adds a time interval in seconds to anotherNSDateobject. - Now that you know the message, time, and badge number, simply set all those properties accordingly on your application. While you’re at it, set
soundNametoUILocalNotificationDefaultSoundName. - Finally, you can schedule the notification! Simply call
scheduleLocalNotification()on the singleton instance ofUIApplication.
Calling your new scheduleNotificationWith(message:intervalInSeconds:badgeNumber:) method lets you easily schedule one local notification. However, you’ll want to schedule a notification for the state change of every stock item.
For this, you’ll need two things: a notification text and the time to show the notification.
Add the following method to StockItem.swift:
func notificationMessage() -> String? { switch state { case .selling: return NSString(format: "Your %@ %@ sold out! Remember to restock.", flavor, type) case .stocking: return NSString(format: "Your %@ %@ is now fully stocked and ready for sale.", flavor, type) default: return nil } } |
In the above method you implement a switch on the state of the stock item. Then for each state you formulate a message that gives details about the state switch and about the flavor and type of item, that has been affected.
There are four states in your app: empty, stocking, stocked, and selling, but only two of them — selling and stocking — are dependant on time. The other two states depend on user interaction, so they don’t need scheduled notifications.
Ready for a challenge? Implement the method func notificationTime() -> NSTimeInterval that calculates the time until the next state switch. You should be able to do this with your current knowledge on NSTimeInterval. If you are stuck, feel free to peek at the hints below before you look at the solution!
Still stuck? Want to check your solution for correctness? Here you go:
With all this groundwork done you can now schedule a notification for every stock item.
Add the following method to GameScene.swift:
func scheduleNotifications() { let itemsSortedByNotificationTime = stockItems.sorted({$0.notificationTime() < $1.notificationTime()}) var count = 1 for stockItem in itemsSortedByNotificationTime { let notificationMessage = stockItem.notificationMessage() if notificationMessage != nil { scheduleNotificationWith(message: notificationMessage!, intervalInSeconds: stockItem.notificationTime(), badgeNumber: count) count++ } } } |
First, you sort the notifications by their notificationTime. Why is the order relevant? You can use it to manage the badge number, since this doesn’t happen automatically. Next you iterate over the list of stock items; for each item you retrieve the appropriate notification message. If the message is not nil, then schedule the notification. With every notification you send, increase the count of the badge number accordingly.
This finishes off the method that schedules a notification for every stock item. You still need a way to call it when the app enters the background. There is only one tiny problem here: only the AppDelegate knows that your app is entering the background, but it doesn’t know about your GameScene.
A great solution for this problem is to use NSNotificationCenter, which provides you with a mechanism to broadcast information within your app.
Open AppDelegate.swift add the following code to applicationDidEnterBackground():
NSNotificationCenter.defaultCenter().postNotificationName("scheduleNotifications", object: nil) |
This will broadcast out a notification through the NSNotificationCenter when your app enters the background state. All you need to do now is listen for this notification.
Open GameScene.swift and add the following code to the end of didMoveToView():
NSNotificationCenter.defaultCenter().addObserver(self, selector: "scheduleNotifications", name: "scheduleNotifications", object: nil) |
This registers the GameScene as an observer; you tell NSNotificationCenter to call scheduleNotifications when an event with the name “scheduleNotifications” triggers.
Build and run, start making some cookies, and hit Command-L a few times to go to the lock screen. When the cookies finish baking, you should see a notification appear on the screen:

You can swipe the notification to return to the app, and see your finished cookies!

Resetting the App Badge
You’re nearly done — all that’s left to do is cancel all notifications and set the number of the badge to zero when the player resumes their game. You don’t want to pester the player with the same information twice.
Open AppDelegate.swift and add the following two lines of code to applicationDidBecomeActive():
UIApplication.sharedApplication().cancelAllLocalNotifications() UIApplication.sharedApplication().applicationIconBadgeNumber = 0 |
The first line cancels all notifications that have not triggered since the last time you started the game. The second line sets applicationIconBadgeNumber to zero.
And that’s it — your kiosk game is complete!
Where to Go From Here?
You can download the completed project for this tutorial here.
This game is a great foundation on which to build future waiting-style games. Here are a few other fun features you could add to your app:
- Charge the player pay rent for the kiosk.
- Add more types and flavors of stock items.
- Let items rot if the player doesn’t sell them in a timely fashion to his customer.
- Add a patience level to your Customer object — if a customer isn’t served in time send them angrily away.
Have fun playing with the project! I am sure you will find fun ways to extend the game or to use the techniques learned here in your own games.
If you have any questions or comments about this tutorial, please come join the forum discussion below!
How to Make a Waiting Game Like Farmville with Sprite Kit and Swift is a post from: Ray Wenderlich
The post How to Make a Waiting Game Like Farmville with Sprite Kit and Swift appeared first on Ray Wenderlich.













