kodeco.com Forums

How to Make a Game Like Candy Crush with SpriteKit and Swift: Part 1

Updated for Xcode 9.3 and Swift 4.1. Learn how to make a Candy Crush-like mobile game, using Swift and SpriteKit to animate and build the logic of your game.

This is a companion discussion topic for the original entry at https://www.raywenderlich.com/55-how-to-make-a-game-like-candy-crush-with-spritekit-and-swift-part-1
1 Like

Thanks for the tutorial. I have a question about MVC here. If “Cookie” is a model, why does it have a reference to a SpritKitNode? Wouldn’t that be like a CoreData model object having a reference to a UIView? I thought the M and V were supposed to stay decoupled via the C. I can see how it makes things convenient in this case though. Thanks

You’re welcome. And thanks for the question — it’s a good one!

Each instance of the Cookie class includes a reference to its corresponding sprite name. So a croissant Cookie, for example, knows the name of the croissant sprite and highlighted croissant sprite.

But that doesn’t affect anything on the screen. It’s just a property of this croissant cookie (or data in the Model.)

Sprites are added to the scene via the addSprites() function GameScene.swift, which is part of the View.

Thanks for tutorial.
Sadly i encounter this error:
Value of type ‘GameScene’ has no member ‘addSprites’
This is happening when beginGame() is called.
Any help is apreciated.

Very nice tutorial.

I did notice that the CookieCrunchPart1-Final/CookieCrunch/GameScene.swift does not include the cropLayer and maskLayer which are implemented in the Making the Tiles Visible step.

@kevcol Can you please help with this when you get a chance? Thank you - much appreciated! :]

Hi there, @melcu!

So that error tells me the function “addSprites()” is not found in your GameScene.swift. Take a look at the tutorial again and make sure you didn’t accidentally skip this bit:

If you did add this code to GameScene.swift, check for typos and make sure the function is not nested inside another function.

If that doesn’t help you solve the problem, post your GameScene.swift file and I’ll be happy to take a look.


Nice catch, @billm. Thanks for the heads-up. I’ve updated the “CookieCrunchPart1-Final” project to include this code.


import SpriteKit
import GameplayKit

class GameScene: SKScene {
// Sound FX
let swapSound = SKAction.playSoundFileNamed(“Chomp.wav”, waitForCompletion: false)
let invalidSwapSound = SKAction.playSoundFileNamed(“Error.wav”, waitForCompletion: false)
let matchSound = SKAction.playSoundFileNamed(“Ka-Ching.wav”, waitForCompletion: false)
let fallingCookieSound = SKAction.playSoundFileNamed(“Scrape.wav”, waitForCompletion: false)
let addCookieSound = SKAction.playSoundFileNamed(“Drip.wav”, waitForCompletion: false)

required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder) is not used in this app”)
override init(size: CGSize) {
super.init(size: size)

func addSprites(for cookies: Set) {
for cookie in cookies {
let sprite = SKSpriteNode(imageNamed: cookie.cookieType.spriteName)
sprite.size = CGSize(width: tileWidth, height: tileHeight)
sprite.position = pointFor (column: cookie.column, row: cookie.row)
cookie.sprite = sprite

func pointFor(column: Int, row: Int) -> CGPoint {
  return CGPoint(
    x: CGFloat(column) * tileWidth + tileWidth / 2,
    y: CGFloat(row) * tileHeight + tileHeight / 2)

anchorPoint = CGPoint(x: 0.5, y: 0.5)

let background = SKSpriteNode(imageNamed: "Background")
background.size = size


let layerPosition = CGPoint(
  x: -tileWidth * CGFloat(numColumns) / 2,
  y: -tileHeight * CGFloat(numRows) / 2)

cookiesLayer.position = layerPosition


var level: Level!

let tileWidth: CGFloat = 32.0
let tileHeight: CGFloat = 36.0

let gameLayer = SKNode()
let cookiesLayer = SKNode()


Hello Kevin,

Sorry for late replay, i got a lot of RL issues.
I paste up here my GameSwift.swift

Value of type ‘GameScene’ has no member ‘addSprites’ i got this in GameViewController.swift.

If i put that function before required init?(coder aDecoder: NSCoder) then i get Use of unresolved identifier ‘pointFor’

Any help is really apreciated.

Thank you.

@kevcol Do you have any feedback regarding this? Thank you - much appreciated! :]

I even try to run the CookieCrunchPart1-Final as i downloaded whitout anything modified.
I got 2 errors.
Something is wrong in my opnion.

If help come this way… i’ll probably finish this tutorial when xcode 2o will be out…

Sorry for the delay, folks. I’ll take a look at this tonight and see if I can’t get it sorted.

Hi @melcu: Are you running in the simulator or your phone? If your phone, check to make sure you specify a development team in Xcode. I get two errors when I try to run the Part 1 - Final project on my phone without setting a dev team:

But I am able to run when I use the simulator (AND you don’t need a development team selected):

I am using Xcode 9.41

BTW, you can choose the simulator in the top-left of Xcode:

Hi @melcu:

I think your addSprites function is nested inside your init.

It appears you have an opening curly bracket, but not a closing one for override init(size: CGSize):

override init(size: CGSize) {
super.init(size: size  // <- MISSING CLOSING BRACKET

func addSprites

Checkout the override init func in the Part 1 starter file:

override init(size: CGSize) {
super.init(size: size)

anchorPoint = CGPoint(x: 0.5, y: 0.5)

let background = SKSpriteNode(imageNamed: "Background")
background.size = size


Let me know if that doesn’t work. Use @kevcol in your reply and I won’t miss it (and sorry for keeping you waiting so long.)

Hope that helps.


Hey @kevcol,

Great tutorial. I believe there is a bug. If there are 2 vertical cookies in the last column, and you try to make a chain by moving a cookie from the same column (so moving it vertically, not horizontally from another column), it is an invalid swap, but it shouldn’t be. Can you help with this?

@kevcol Can you please help with this when you get a chance? Thank you - much appreciated! :]

1 Like

Hi @c0nman: Are you trying to move a cookie across the gap? That is not allowed, so would rightly be an invalid swap. In the last column, there are a pair of three vertical slots (there’s a gap, three vertical cookies, a gap, three vertical cookies, a gap), so I don’t think it would be possible to make any vertical move that makes a vertical match. Am I misunderstanding you?


Hi. I find some bug.

When you make vertical swap in last cols mechanic can’t detect possible swap.
Need add some logic to Level.swift in detectPossibleSwaps()
Extend If add :

 else if column == numColumns - 1, let cookie = cookies[column, row] {
      if row < numRows - 1,
        let other = cookies[column, row + 1] {
        cookies[column, row] = other
        cookies[column, row + 1] = cookie

        // Is either cookie now part of a chain?
        if hasChain(atColumn: column, row: row + 1) ||
          hasChain(atColumn: column, row: row) {
          set.insert(Swap(cookieA: cookie, cookieB: other))

        // Swap them back
        cookies[column, row] = cookie
        cookies[column, row + 1] = other

if column < numColumns - 1,
let cookie = cookies[column, row]

@kevcol Do you have any feedback about this? Thank you - much appreciated! :]