How to Make a Waiting Game Like Farmville with SpriteKit and Swift

In this SpriteKit tutorial, you’ll learn how to make your very own waiting game — just like Farmville — with SpriteKit and Swift. By Kevin Colligan.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Asking for User Permission

The first step is to ask the player to authorize your app to use notifications. Open AppDelegate.swift and add the following to the import statements at the top of the file:

import UserNotifications

Then add the following to application(_:didFinishLaunchingWithOptions:):

UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
  if granted {
    print("notifications granted")
  } else {
    print(error?.localizedDescription)
  }
}

The UserNotifications framework is new in iOS 10, replacing previous platform-specific interfaces for local and remote notifications. Using UNUserNotificationCenter, you ask for permission to send an alert, play a sound and add a badge number to the Kookie Kiosk app icon. The print statements are just for debugging purposes.

Build and run, and you'll see the following dialog:

NotificationPermission

Tap OK to allow notifications. (If you tap Don't Allow, notifications will not appear).

Note that after the first run, this dialog won't show up again. Instead, the app will use the value stored in the Settings app.

Scheduling Notifications

Since most of your notifications will be similar in structure, you'll create a small helper method to schedule a notification.

Open GameScene.swift and add another import statement:

import UserNotifications

Then add the following method:

func scheduleNotificationWith(body: String, intervalInSeconds: TimeInterval, badgeNumber: Int) {
  // 1
  let localNotification = UNMutableNotificationContent()

  // 2
  localNotification.body = body
  localNotification.sound = UNNotificationSound.default()
  localNotification.badge = badgeNumber as NSNumber?

  // 3
  let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: intervalInSeconds, repeats: false)
  let request = UNNotificationRequest.init(identifier: body, content: localNotification, trigger: trigger)

  // 4
  let center = UNUserNotificationCenter.current()
  center.add(request)
}

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 UNMutableNotificationContent().
  2. Then, set the properties of your notification, which in this tutorial, means the body text, a sound alert and badge number. (You could also include a title, subtitle and media if so inclined.)
  3. Define a trigger for your notification based on the timeInterval.
  4. Finally, you can schedule the notification!

Calling your new scheduleNotificationWith(body:intervalInSeconds:badgeNumber:) method lets you easily schedule a single 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: notification text and the time to show the notification.

Add the following method to StockItem.swift:

func notificationMessage() -> String? {
  switch state {
  case .selling:
    return String(format: "Your %@ %@ sold out! Remember to restock.", flavor, type)
  case .stocking:
    return String(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 current state and includes the flavor and type of the item.

Although there are four states in your app, only two of them — selling and stocking — are dependent on time. The other two states depend on user interaction, so they don't need scheduled notifications.

Now add the following method to calculate the time until the next state-switch:

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

In this method, you determine how long it takes to complete selling or stocking an item. You can now schedule a notification for every stock item.

Add the following method to GameScene.swift:

func scheduleNotifications() {
  let itemsSortedByNotificationTime = stockItems.sorted(by: { $0.notificationTime() < $1.notificationTime() })
  var count = 1
  for stockItem in itemsSortedByNotificationTime {
    let notificationMessage = stockItem.notificationMessage()
    if notificationMessage != nil {
      scheduleNotificationWith(body: notificationMessage!, intervalInSeconds: stockItem.notificationTime(), badgeNumber: count)
      count += 1
    }
  }
}

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, and 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, and it doesn't know about your GameScene.

A great solution for this problem is to use NotificationCenter, which provides you with a mechanism to broadcast information within your app.

Open AppDelegate.swift and add the following code to applicationDidEnterBackground(_:):

NotificationCenter.default.post(name: NSNotification.Name(rawValue: "scheduleNotifications"), object: nil)

This will broadcast out a notification through the NotificationCenter 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 didMove(to:):

NotificationCenter.default.addObserver(self, selector: #selector(scheduleNotifications), name: NSNotification.Name(rawValue: "scheduleNotifications"), object: nil)

This registers the GameScene as an observer — you tell NotificationCenter to call scheduleNotifications() when an event with the name "scheduleNotifications" triggers.

Build and run, start making some cookies, and hit Command-L a couple times (if using the simulator) to go to the lock screen. When the cookies finish baking, you should see a notification appear on the screen:

lockScreen

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

AfterNotification

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 lines of code to applicationDidBecomeActive(_:):

let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
UIApplication.shared.applicationIconBadgeNumber = 0

First, we need a reference to UNUserNotificationsCenter, then we can clear old notifications. The last 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 provides a great foundation for waiting games. It also scratches the surface of state machines and user notifications. For more on those topics, you can:

  • Dive into GameplayKit and GKStateMachine
  • Learn about the new User Notifications capabilities in iOS 10
  • Learn how to monetize your game with (clever) In-App Purchases

Or you can stick with what you already learned and add fun features to the game, such as:

  • Charge the player rent on the kiosk.
  • Add more types and flavors of tasty snacks...
  • ... but have them spoil if they sit around too long before going on sale.
  • Add patience levels to your customers — leave them hanging for too long they're liable to storm off and leave catty Yelp reviews! :[

If you have any questions or comments about this tutorial, please join the forum discussion below!

Kevin Colligan

Contributors

Kevin Colligan

Author

Kyle Gorlick

Tech Editor

Chris Language

Final Pass Editor

Tammy Coron

Team Lead

Over 300 content creators. Join our team.