🛠️ 1. Project Setup & File Organization
Step 1: Create a New Xcode Project
Instruction: Launch Xcode. On the welcome screen, click "Create a new Xcode project". Select the iOS platform and the App template. Click Next.
On the next screen, enter the following options:
- Product Name:
RMITCasino
- Interface:
SwiftUI
- Language:
Swift
Explanation:
This process creates a new, blank SwiftUI project. Xcode generates the essential files, including ContentView.swift
, which is where we will start building our user interface.

Step 2: Prepare Assets (Images & Colors)
Instruction: First, you will need to gather the image assets for the game. For this tutorial, all required images can be found in the Asset Gallery and also on Canvas. You will need to find the following files:
- Logos:
rmit-casino-welcome-logo
- UI Elements:
reel
,spin
,casino-chips
- Slot Icons:
apple
,bar
,cherry
,clover
,diamond
,grape
,heart
,horseshoe
,lemon
,melon
,money
,orange
- Open
Assets.xcassets
in the Xcode project navigator. - Create two new folders by right-clicking in the assets list and selecting "New Folder". Name them
Graphics
andRMIT-Logos
. - Drag the
rmit-casino-welcome-logo
image into theRMIT-Logos
folder. - Drag all other images into the
Graphics
folder.
- In
Assets.xcassets
, create another new folder namedColors
. - Inside the
Colors
folder, right-click and select "New Color Set". Create six color sets with the following names:ColorBlackTransparentRMIT
ColorBlueRMIT
ColorPurpleRMIT
ColorRedRMIT
ColorWhiteRMIT
ColorYellowRMIT
- For each color set, select it, open the Attributes Inspector on the right, and set the "Appearances" to "Any, Dark".
- Click on the color swatch and set the RGB values as follows:
- ColorBlackTransparentRMIT: Red: 0, Green: 0, Blue: 0, Opacity: 35%
- ColorBlueRMIT: Red: 0, Green: 0, Blue: 84
- ColorPurpleRMIT: Red: 184, Green: 24, Blue: 121
- ColorRedRMIT: Red: 230, Green: 30, Blue: 42
- ColorWhiteRMIT: Red: 227, Green: 229, Blue: 224
- ColorYellowRMIT: Red: 250, Green: 200, Blue: 0
You can also use a color palette tool like Paletton to help you choose a set of colors that go well together. When selecting colors, consider using color theory to create a harmonious scheme based on your primary colors (e.g., RMIT Blue and Red):
- Adjacent/Analogous Colors: These are colors next to each other on the color wheel. They create a serene and comfortable design. For example, with RMIT Red, you could use adjacent oranges and purples.
- Triad: This scheme uses three colors that are evenly spaced around the color wheel, forming a triangle. It offers high contrast while retaining harmony. For example, a triad with RMIT Red might include a yellow and a blue.
- Tetrad (Rectangular): This scheme uses four colors arranged into two complementary pairs. It offers plenty of possibilities for variation but can be difficult to balance. For example, with RMIT Red and Blue, you could find their complementary colors (a green and an orange) to form a tetrad.
Explanation:
The Asset Catalog (Assets.xcassets
) is where we manage all our visual assets like images and colors. By defining custom colors here, we can easily reference them throughout our app by name (e.g., Color("ColorRedRMIT")
), making our code cleaner and easier to maintain. Organizing images into folders keeps the project tidy.

🎨 2. Building the User Interface
Step 3: Add the Background Color
Instruction:
Open ContentView.swift
. Replace the default content inside the body
with a ZStack
containing a LinearGradient
.
Explanation:
A ZStack
is a container that layers its child views on top of each other. We'll use it to place a gradient background behind all other UI elements. LinearGradient
creates a smooth transition between multiple colors. In this case, it blends from ColorRedRMIT
at the top (.top
) to ColorPurpleRMIT
at the bottom (.bottom
), creating a vibrant background. The .edgesIgnoringSafeArea(.all)
modifier ensures the gradient extends to the very edges of the screen, including the notch and home indicator areas.
// ContentView.swift
struct ContentView: View {
var body: some View {
ZStack {
// MARK: - Background
LinearGradient(gradient: Gradient(colors: [Color("ColorRedRMIT"), Color("ColorPurpleRMIT")]), startPoint: .top, endPoint: .bottom)
.edgesIgnoringSafeArea(.all)
}
}
}
#Preview {
ContentView()
}

Step 4: Add LogoView
Instruction:
- Create a new file for the logo. Right-click on the
RMITCasino
folder in the Project Navigator, select "New File...", choose "SwiftUI View", and name itLogoView.swift
. - In
LogoView.swift
, add the code to display the logo image with some basic sizing. - Go back to
ContentView.swift
and add aVStack
inside theZStack
. Inside theVStack
, add your newLogoView
.
Explanation:
Breaking down the UI into smaller, reusable views like LogoView
is a core concept in SwiftUI. It keeps our ContentView
clean and makes the LogoView
easy to reuse or modify later.
// LogoView.swift
import SwiftUI
struct LogoView: View {
let logoFileName: String
var body: some View {
Image(logoFileName)
.resizable()
.scaledToFit()
.frame(minWidth: 250, idealWidth: 280, maxWidth: 320, alignment: .center)
}
}
#Preview {
LogoView(logoFileName: "rmit-casino-welcome-logo")
}
// ContentView.swift
struct ContentView: View {
var body: some View {
ZStack {
// MARK: - Background
LinearGradient(gradient: Gradient(colors: [Color("ColorRedRMIT"), Color("ColorPurpleRMIT")]), startPoint: .top, endPoint: .bottom)
.edgesIgnoringSafeArea(.all)
VStack {
// MARK: - Logo
LogoView(logoFileName: "rmit-casino-welcome-logo")
Spacer() // Add a spacer to push content to the top and bottom
}
.padding()
}
}
}
#Preview {
ContentView()
}

✨ 3. Creating Reusable Modifiers
Step 5: Create the ShadowModifier
Instruction:
- Create another new file. Right-click on the
RMITCasino
folder, select "New File...", choose "Swift File", and name itModifiers.swift
. - Inside
Modifiers.swift
, add the code forShadowModifier
.
Explanation:
A ViewModifier
is a reusable set of modifiers. We will apply a specific shadow style to many UI elements. Creating a custom ShadowModifier
saves us from repeating the same .shadow(...)
code everywhere and ensures a consistent look.
// Modifiers.swift
import Foundation
import SwiftUI
struct ShadowModifier: ViewModifier {
func body(content: Content) -> some View {
content
.shadow(color: Color("ColorBlackTransparentRMIT"), radius: 10)
}
}

Step 6: Apply ShadowModifier to LogoView
Instruction:
Open LogoView.swift
and apply your new ShadowModifier
to the Image
.
Explanation:
We apply a custom modifier using the .modifier()
syntax. This attaches the shadow effect defined in ShadowModifier
to our logo.
// LogoView.swift
struct LogoView: View {
let logoFileName: String
var body: some View {
Image(logoFileName)
.resizable()
.scaledToFit()
.frame(minWidth: 250, idealWidth: 280, maxWidth: 320, alignment: .center)
.modifier(ShadowModifier()) // <-- ADDED: Applies our custom shadow style
}
}

🏗️ 4. Building the Static UI
Step 7: Add Score Boards
Instruction:
In ContentView.swift
, below the LogoView
, add an HStack
to create the layout for the player's money and high score boards. Use hardcoded text for now.
Explanation:
We use an HStack
to arrange the two scoreboards horizontally. Each scoreboard is itself an HStack
containing a label and a score. Spacer()
is a flexible view that expands to fill available space, pushing the two scoreboards to opposite sides of the screen.
// Inside the VStack, after LogoView
// MARK: - Score boards
HStack {
// Left-side score board
HStack{
Text("Your\nMoney".uppercased())
.multilineTextAlignment(.leading)
Text("100")
}
Spacer() // Pushes the two HStacks apart
// Right-side score board
HStack{
Text("200")
Text("High\nScore".uppercased())
.multilineTextAlignment(.trailing)
}
}

Step 8: Create and Apply Score Board Modifiers
Instruction:
- Open
Modifiers.swift
and add the three newViewModifier
structs:scoreLabelStyle
,scoreNumberStyle
, andscoreCapsuleStyle
. - Open
ContentView.swift
and apply these new modifiers to the score board views as shown in the code snippet.
Explanation:
We are creating three distinct styles: one for the text labels ("Your Money"), one for the numbers ("100"), and one for the capsule-shaped background. This approach keeps styling logic separate from the layout code in ContentView
, making it much cleaner.
// Add these structs to Modifiers.swift
// Styles the text labels like "Your Money"
struct scoreLabelStyle: ViewModifier {
func body(content: Content) -> some View {
content
.foregroundColor(Color("ColorWhiteRMIT"))
.font(.system(size: 10, weight: .bold,design: .rounded))
}
}
// Styles the score numbers like "100"
struct scoreNumberStyle: ViewModifier {
func body(content: Content) -> some View {
content
.foregroundColor(Color("ColorWhiteRMIT"))
.font(.system(size: 20, weight: .heavy,design: .rounded))
}
}
// Creates the capsule background for the score boards
struct scoreCapsuleStyle: ViewModifier {
func body(content: Content) -> some View {
content
.padding(.vertical,10)
.padding(.horizontal,16)
.background(
Capsule()
.foregroundColor(Color("ColorBlackTransparentRMIT"))
)
}
}
// Replace the previous score board HStack with this
HStack {
HStack{
Text("Your\nMoney".uppercased())
.modifier(scoreLabelStyle()) // <-- APPLY MODIFIER
.multilineTextAlignment(.leading)
Text("100")
.modifier(scoreNumberStyle()) // <-- APPLY MODIFIER
}
.modifier(scoreCapsuleStyle()) // <-- APPLY MODIFIER
Spacer()
HStack{
Text("200")
.modifier(scoreNumberStyle()) // <-- APPLY MODIFIER
Text("High\nScore".uppercased())
.modifier(scoreLabelStyle()) // <-- APPLY MODIFIER
.multilineTextAlignment(.trailing)
}
.modifier(scoreCapsuleStyle()) // <-- APPLY MODIFIER
}

Step 9: Add Slot Machine and Reel Modifiers
Instruction:
- First, add the
ReelImageModifier
andIconImageModifier
to yourModifiers.swift
file. - Then, in
ContentView.swift
, inside the mainVStack
and below the score boards, add the code for the slot machine reels. UseZStack
to place an icon image on top of a reel background image. - Apply the new modifiers to the images as you create them.
Explanation:
We are building the visual structure of the slot machine. Each reel is a ZStack
containing the background reel image and the icon. The ReelImageModifier
and IconImageModifier
ensure all images are sized correctly and have our standard shadow. For now, we'll use hardcoded icon images like "apple" and "cherry".
// Add these structs to Modifiers.swift
// Styles the background reel images
struct ReelImageModifier: ViewModifier {
func body(content: Content) -> some View {
content
.scaledToFit()
.frame(minWidth: 140, idealWidth: 200, maxWidth: 220, alignment: .center)
.modifier(ShadowModifier())
}
}
// Styles the icon images (apple, cherry, etc.)
struct IconImageModifier: ViewModifier {
func body(content: Content) -> some View {
content
.scaledToFit()
.frame(minWidth: 50, idealWidth: 60, maxWidth: 70, alignment: .center)
.modifier(ShadowModifier())
}
}
// Inside the main VStack, after the score boards
// MARK: - Slot Machine
VStack {
// MARK: - First Reel
ZStack {
Image("reel")
.resizable()
.modifier(ReelImageModifier())
Image("apple") // Hardcoded for now
.resizable()
.modifier(IconImageModifier())
}
HStack {
// MARK: - Second Reel
ZStack {
Image("reel")
.resizable()
.modifier(ReelImageModifier())
Image("cherry") // Hardcoded for now
.resizable()
.modifier(IconImageModifier())
}
Spacer()
// MARK: - Third Reel
ZStack {
Image("reel")
.resizable()
.modifier(ReelImageModifier())
Image("bar") // Hardcoded for now
.resizable()
.modifier(IconImageModifier())
}
}
}

Step 10: Add the Spin Button
Instruction:
In ContentView.swift
, at the end of the slot machine VStack
, add a Button
with the "spin" image. The action can just print a message for now.
Explanation:
This adds the primary interactive element to our game. We use a Button
view, and for its label, we provide our "spin" image, styled with the ReelImageModifier
to match the reels above it. The `action` is a closure—a piece of code that will run when the button is tapped.
// Inside the slot machine VStack, after the HStack for the reels
// MARK: - Spin Button
Button(action: {
// This code runs when the button is tapped
print("Spin!!!")
}, label: {
Image("spin")
.resizable()
.modifier(ReelImageModifier())
})

Step 11: Add the Footer and its Modifiers
Instruction:
- Add the
BetCapsuleModifier
andCasinoChipModifier
to yourModifiers.swift
file. - In
ContentView.swift
, at the bottom of the mainVStack
, add anHStack
for the footer. - Build the footer UI using
Text
andImage
views, and apply the new modifiers.
Explanation:
This step builds the static UI for the betting controls. The BetCapsuleModifier
creates a stylish, gradient-filled capsule for the bet amounts, and the CasinoChipModifier
sizes the chip images appropriately.
// Add these structs to Modifiers.swift
// Styles the bet amount text ("10", "20")
struct BetCapsuleModifier: ViewModifier {
func body(content: Content) -> some View {
content
.foregroundColor(Color("ColorWhiteRMIT"))
.font(.system(size: 25, weight: .heavy, design: .rounded))
.modifier(ShadowModifier())
.background(
// The capsule is filled with a gradient for a nice visual effect
Capsule()
.fill(
LinearGradient(gradient: Gradient(colors: [Color("ColorYellowRMIT"), Color("ColorRedRMIT")]), startPoint: .top, endPoint: .bottom)
)
.frame(width: 80, height: 50, alignment: .center)
)
}
}
// Styles the casino chip images
struct CasinoChipModifier: ViewModifier {
func body(content: Content) -> some View {
content
.scaledToFit()
.frame(height: 70)
.modifier(ShadowModifier())
}
}
// At the end of the main VStack, after the slot machine VStack
// MARK: - Footer
HStack {
// MARK: - Bet 20
HStack(spacing: 30) {
Text("20")
.modifier(BetCapsuleModifier())
Image("casino-chips")
.resizable()
.modifier(CasinoChipModifier())
}
Spacer()
// MARK: - Bet 10
HStack(spacing: 30) {
Image("casino-chips")
.resizable()
.modifier(CasinoChipModifier())
.opacity(0) // Hidden for now
Text("10")
.modifier(BetCapsuleModifier())
}
}
.padding(.horizontal, 20)

🧠 5. Adding Game Logic
Step 12: Add Game Logic
Instruction: Now we will make the game interactive.
- Add Icon Array: In
ContentView.swift
, at the top of theContentView
struct, add a constant arrayicons
that holds the names of all your slot icon images. - Add State: Below the
icons
array, add a@State
variable namedreels
to store the current state of the three reels. - Update Reels: Go to the slot machine UI code and replace the hardcoded image names (
"apple"
,"cherry"
,"bar"
) with references to your new state variable, likeicons[reels[0]]
. - Create Spin Function: Add a
spinReels()
function inside theContentView
struct. This function will assign a new random index to each item in thereels
array. - Connect Button: Find the "spin"
Button
and change its action to call your newspinReels()
function.
Explanation: This is where the magic happens.
- The
icons
array gives us a single source for all possible outcomes. - The
@State
property wrapper tells SwiftUI to watch thereels
variable for any changes. - When the user taps the spin button, the
spinReels()
function is called, which changes the values in thereels
array. - Because
reels
is a@State
variable, SwiftUI automatically detects this change and re-draws the parts of the UI that depend on it (the icon images), showing the new random combination.
// ContentView.swift
import SwiftUI
struct ContentView: View {
// MARK: - PROPERTIES
// This array holds the names of all the icon images.
let icons = ["apple","bar","cherry","clover","diamond", "grape", "heart", "horseshoe","lemon","melon","money","orange"]
// @State tells SwiftUI to watch this variable for changes.
// When it changes, the UI will automatically update.
// It stores the index of the icon for each of the three reels.
@State private var reels = [0,1,2]
// MARK: - FUNCTIONS
func spinReels(){
// Randomize the icon for each reel.
reels[0] = Int.random(in: 0...icons.count - 1)
reels[1] = Int.random(in: 0...icons.count - 1)
reels[2] = Int.random(in: 0...icons.count - 1)
}
var body: some View {
ZStack {
// ... (Background code is unchanged)
VStack {
// ... (Logo and Score boards are unchanged)
// MARK: - Slot Machine
VStack {
// MARK: - First Reel
ZStack {
Image("reel")
.resizable()
.modifier(ReelImageModifier())
// CHANGED: Display the icon from our state array
Image(icons[reels[0]])
.resizable()
.modifier(IconImageModifier())
}
HStack {
// MARK: - Second Reel
ZStack {
Image("reel")
.resizable()
.modifier(ReelImageModifier())
// CHANGED: Display the icon from our state array
Image(icons[reels[1]])
.resizable()
.modifier(IconImageModifier())
}
Spacer()
// MARK: - Third Reel
ZStack {
Image("reel")
.resizable()
.modifier(ReelImageModifier())
// CHANGED: Display the icon from our state array
Image(icons[reels[2]])
.resizable()
.modifier(IconImageModifier())
}
}
// MARK: - Spin Button
Button(action: {
// CHANGED: Call the spinReels function
spinReels()
}, label: {
Image("spin")
.resizable()
.modifier(ReelImageModifier())
})
}
// ... (Footer is unchanged)
}
.padding()
}
}
}
#Preview {
ContentView()
}

🥳 Congratulations!
Awesome job! You've just brought the RMIT Casino app to life. Fire it up in the simulator, hit that spin button, and see your creation in action as the reels whirl and land on a random combination. This is the foundation of your game—and it's already looking great!
🚀 Level Up Your Skills: Bonus Challenges!
Challenge 1: Apple Dice Game 🎲
To warm up, let’s create a fun app to roll virtual dice with the functionality to increase or decrease the number of dice on the screen to play different kinds of games. Please follow Apple SwiftUI tutorial here: https://developer.apple.com/tutorials/develop-in-swift/update-the-ui-with-state

Challenge 2: RMIT Red & Blue Dice Game 🎲
You play blue dice. The AI opponent plays red dice. When you press the “Play” button, dice rolls are randomized. If your total sum of dice is greater than your opponent then you will win and see the winning message like in the demo screenshot below. Otherwise, you lose and see the lost message. Add some sound effects when you win or you lose. You can select free game sound effects here:
All image assets of this dice game can be downloaded on Canvas. You can also find the dice images in the Asset Gallery. However you are encouraged to choose your own image assets and colorsets.
Extra challenge: Add a total score text on the top of the game. Add +100 points for any winning game and -100 points for any losing game.

🎉 What's Next?
You've Mastered the Basics!
You've successfully built the foundation of the RMIT Casino app. Along the way, you've learned key SwiftUI concepts:
- Setting up a project and managing assets in the Asset Catalog.
- Building complex layouts with
ZStack
,VStack
, andHStack
. - Creating reusable styles with custom
ViewModifier
s to keep your code clean and consistent. - Making your UI interactive with
@State
variables andButton
actions.
Now that you have a working game, why not take on the Bonus Challenges? They are a great way to test your skills and learn even more.
Ready for more? In Part 2 of this guide, we will take this game to the next level by adding betting logic, calculating winnings, managing player scores, and adding sound effects to create a fully immersive casino experience. Stay tuned!