How to Make a Waiting Game Like Farmville with Sprite Kit and Swift

How to Make a Waiting Game Like Farmville with Sprite Kit and Swift
Give your players the power to build their own Kookie Kiosk by implementing a waiting game.

Give your players the power to build their own Kookie Kiosk by implementing a waiting game.

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:

This is what you should see when you run the template. What a mess!

This is what you should see when you run the template. What a mess!

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 ZPosition ensures the layers of the game are drawn in the correct order. TimeScale gives 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 DiscreteProgressBar it 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:

States of an item in the Kiosk and transitions between states

States of an item in the Kiosk and transitions between states

Each stock item can be in one of four separate states:

  1. 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.
  2. Stocking: Once you buy an item you’ll need to wait while the game replenishes your item stock.
  3. Stocked: When an item is fully stocked it switches to the stocked state.
  4. 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:

Visualization of 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:

StockItemData Plist

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. :]

Note: At the risk of being a bit obfuscated, you can reduce the need for temporary variables and write the above code in one line as shown below:

    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.

Note: If you see the error message unexpectedly found nil while unwrapping an Optional value just reset your simulator. You’ll see this error message when you try to load a variable from gamedata.plist that isn’t present in the file. You still have an old version of gamedata.plist loaded in the simulator — resetting the simulator removes this old file. If you’re working on a device then you’ll need to delete and reinstall the app entirely.

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:

Solution Inside SelectShow

The full method is shown below. In the final case neither the stocking timer, the sell button, nor the price tag are visible; hence you simply set hidden to true for all three.

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:
        stockingTimer.hidden = true
        sellButton.hidden = true
        priceTag.hidden = true

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:

Bringing order to the chaos is really satisfying!

Bringing order to the chaos is really satisfying!

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)
            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)
    case .stocked:
        switchTo(state: State.selling)

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!

This is how your player currently feels about your game!

This is how your player currently feels about your game!

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:

This time bar shows you the different intervals and absolute times you are using.

This time bar shows you the different intervals and absolute times you are using.

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:
        amount = min(Int(Float(timePassed) / stockingSpeed), maxAmount)
        if amount == maxAmount {
            switchTo(state: .stocked)

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.

Note: It’s much better to calculate the amount of items based on the amount of time elapsed like you’ve done here, rather than the naive approach periodically incrementing a count.

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 {

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.

Your player suddenly got a whole lot more patient.

Your player suddenly got a whole lot more patient.

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!

w00t I sold a cookie – I’m rich!

Note: Waiting several minutes until something happens might get a bit annoying. As a developer, you’re allowed to cheat a little. Simply change 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 {
    // 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)

This is a lot of code, but the logic is quite straightforward. Taking each numbered comment in turn:

  1. 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.
  2. 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.
  3. 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.
  4. Finally, make the customer appear from the right border of the screen. Using a simple SKAction you 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.

Serve me, serve me NOOOOOOW!

Serve me, serve me NOOOOOOW!

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)
    } else {
        var playSound = SKAction.playSoundFileNamed("hit.wav", waitForCompletion: true)
    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 = 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:

  1. 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.
  2. Next, remove the customer sprite using an instance of SKAction that 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 to nil.
  3. 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!

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:

Receiving a notification

Receiving a notification

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:

Hey you missed something!

Hey you missed something!

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:

Pick me!

Pick me!

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. :]

Note: If you’re interested in how to use push notifications have a look at the tutorial on this site covering Apple Push Notification Services.

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)
    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:

The player can change the rights granted to your application at any time.

The player can change the rights granted to your application at any time.

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

The above method builds the notification from a message, a time interval, and the updated badge number as follows:

  1. First, you create an empty notification using UILocalNotification().
  2. 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 of NSDate, but to get an NSDate object for a specific time in the future you can use dateByAddingTimeInterval which adds a time interval in seconds to another NSDate object.
  3. 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 soundName to UILocalNotificationDefaultSoundName.
  4. Finally, you can schedule the notification! Simply call scheduleLocalNotification() on the singleton instance of UIApplication.

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)
        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!

Solution Inside: Hints SelectShow

  • You only need to calculate times for the two states selling and stocking. The other two state switches only occur on direct user interaction.
  • The variables you need are selling and stockingSpeed, aong with amount and maxAmount. These give you all the information you need to to calculate the time until the next state change.
  • Remember that NSTimeInterval is a Float. You might need to cast some variables for your calculations.

Still stuck? Want to check your solution for correctness? Here you go:

Solution Inside: Solution SelectShow

func notificationTime() -> NSTimeInterval {
    switch state {
    case .selling:
        return NSTimeInterval(sellingSpeed * Float(amount))
    case .stocking:
        var stockingTimeRequired = stockingSpeed * Float(maxAmount - amount)
        return NSTimeInterval(stockingTimeRequired)
        return -1

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)

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().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.



Write a comment