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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to Make a Waiting Game Like Farmville with SpriteKit and Swift
30 mins
- Getting Started
- Implementing the State Machine
- Cleaning up the Interface
- Switching States
- Updating States Over Time
- Stocking Your Items
- Selling Items
- Introducing Customers
- Sending User Notifications
- Local vs. Remote Notifications
- Asking for User Permission
- Scheduling Notifications
- Resetting the App Badge
- Where to Go From Here?
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:
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:
- First, you create an empty notification using
UNMutableNotificationContent()
. - 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.)
- Define a trigger for your notification based on the
timeInterval
. - 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:
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 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!