Welcome to Day 10 of #The12DaysOfSwiftmas
Today’s prompt…

Swiftmas 2023 GitHub repo: https://github.com/thecodingsprite/Swiftmas-2023/tree/main

Now if you’re not British you may not fully get this prompt as I have been made aware that Christmas Crackers are not a worldwide thing! (I cannot believe this, they are the highlight of the Christmas meal!!!) Anyhow let me explain, they are tubes that 2 people have a tug of war over until a loud snap & the winner ends up with the tube half that contains a paper hat, a joke & sometimes a gift. Cracker jokes are some of my most favourite kinds of jokes, so a Cracker Joke App here I come!
In my head, at the initial stage I knew I wanted a good amount of jokes for people to peruse through. I knew I wanted a previous & next button & ideally a randomise button for fun. But seeing all that code line up in my head, the jokes would have to be stored separately in a different file to begin with otherwise it would just be a mess. But for now we will style out our ContentView file.
We will use placeholder text for now & add some todo comments.
var body: some View {
ZStack {
// Background
Image("bg")
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
.opacity(0.8)
VStack {
Spacer()
// Joke
ZStack {
RoundedRectangle(cornerRadius: 15)
.padding()
.frame(height: 300)
.foregroundStyle(Color("joke-box"))
.opacity(0.8)
// TODO: Dynamically show jokes
Text(" Joke goes here, probably on multiple lines, testing text, testing text, testing text.")
.foregroundStyle(.white)
.clipShape(RoundedRectangle(cornerRadius: 15))
.padding(.horizontal, 30)
}
Spacer()
HStack {
// Previous
Button {
// TODO: Previous joke
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Previous")
.foregroundStyle(.white)
}
}
// Next
Button {
// TODO: Next joke
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Next")
.foregroundStyle(.white)
}
}
}.frame(width: 350)
Button {
// TODO: Randomise joke
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Random")
.foregroundStyle(.white)
}
.padding(.bottom, 50)
}
}
.frame(width: 350)
}
}
}
So after creating your UI layout right click in the file navigator > New File > Swift File
Now we can store our jokes in this new struct we are going to create. But we need to think about access to these jokes. I personally thought the best way would be one array containing the jokes but the strings needed to be multi-line strings so we use """ instead of "". I also for the sake of this app went with using the static keyword as jokes aren’t a big deal for privacy in this application.
import Foundation
struct Jokes {
static var jokes = [
"""
Q: Why didn't Santa put the tree lights up?
A: Because of Elf & Safety!
""",
"""
Q: Why can't the Christmas tree stand up?
A: It doesn't have legs!
""",
"""
Q: Who is Santa's favourite actor?
A: William Dafoe-ho-ho!
""",
"""
Q: What's ever elf's favourite type of music?
A: Wrap!
""",
"""
Q: Who tells the best Christmas jokes?
A: Reindeer. They sleigh every time!
""",
"""
Q: What do you call Santa when he takes a break?
A: Santa Pause!
""",
"""
Q: Who is a Christmas tree's favourite singer?
A: Spruce Springsteen!
""",
"""
Q: What do you get if Santa goes down the chimney when a fire is lit?
A: Crisp Kringle!
""",
"""
Q: Why is the turkey never hungry at Christmas?
A: Because it's stuffed!
""",
"""
Q: What does Santa use to bake cakes?
A: Elf-raising flour of course!
""",
"""
Q: Why did the choir have to cancel their carol concert?
A: They caught tinsel-itis!
""",
"""
Q: What do call a penguin in the Sahara desert?
A: Lost!
""",
"""
Q: What do Santa's little helpers learn at school?
A: The elf-abet!
""",
"""
Q: What do you get if you put a bell on a skunk?
A: Jingle smells!
""",
"""
Q: How did Darth Vader know what Luke Skywalker got for Christmas?
A: He felt his presents!
""",
"""
Q: What is white & minty?
A: A Polo Bear!
""",
"""
Q: What do you call an old snowman?
A: Water!
""",
"""
Q: What do sheep say to each other at Christmas time?
A: Merry Christmas to ewe!
""",
"""
Q: What comes at the end of Christmas Day?
A: The letter "Y"!
""",
"""
Q: What did the farner get for Christmas?
A: A cowculator!
""",
"""
Q: What can you call a polar bear which wears ear muffs?
A: Anything you like, they can't hear you!
""",
"""
Q: What do you get if Santa forgets to wear his undercrackers?
A: St Nickerless!
""",
"""
Q: Why is it getting harder to buy advent calendars?
A: Because their days are numbered!
""",
"""
Q: What do you get if you cross Santa with a duck?
A: A Christmas Quacker!
""",
"""
Q: Who's Rudolphs favourite pop star?
A: Beyon-sleigh!
""",
"""
Q: Why did no one bid for Rudolph & Dasher on eBay?
A: Because they were two deer!
""",
"""
Q: What do snowmen wear on their heads?
A: Ice caps!
""",
"""
Q: Why couldn't the skeleton go to the Christmas party?
A: Because he had no body to go with!
""",
"""
Q: What do you call an obnoxious reindeer?
A: Rude-olph!
""",
"""
Q: Why was the snowman looking through the carrots?
A: He was picking his nose!
"""
]
}
I went with a total of 30 jokes. Some my own from memory & some I went googling for. This was probably the hardest part as it was just so mundane. But 30 jokes later & we can go back to ContentView & begin fixing our UI to dynamically show these jokes. First off we are going to need a @State property that can update with the UI so we can keep track of where we are in the list of jokes.
@State var jokeTracker = 0
Now to add the jokes to the UI using this state property to set the index within that array to show.
// Dynamically show jokes
Text(Jokes.jokes[jokeTracker])
Now to update those todo comments in the buttons. For previous we can -1 of our @State property & next we can +1 but first we need to check we are not already at 0. For the randomise button we can use the Int.random(in: 0...29) to select our place within the array (remember arrays start at 0 not 1 so we only count up to 29).
Button {
// Previous joke
if jokeTracker != 0 {
jokeTracker -= 1
} else {
// Do nothing as we can't go any further back
}
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Previous")
.foregroundStyle(.white)
}
}
// Next
Button {
// Reset jokes back to start if at the end
if jokeTracker >= 29 {
jokeTracker = 0
} else {
// Next joke
jokeTracker += 1
}
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Next")
.foregroundStyle(.white)
}
}
}
.frame(width: 350)
// Randomise joke
Button {
jokeTracker = Int.random(in: 0...29)
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Random")
.foregroundStyle(.white)
}
.padding(.bottom, 50)
}
}
.frame(width: 350)
}
}
Now you should have a fully functional app that can make you laugh out loud whenever you want it to!
The complete functional code is at the bottom of this post.
Our Completed Application
See you tomorrow for Day 11
Complete Code
ContentView
import SwiftUI
struct ContentView: View {
@State var jokeTracker = 0
var body: some View {
ZStack {
// Background
Image("bg")
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
.opacity(0.8)
VStack {
Spacer()
// Joke
ZStack {
RoundedRectangle(cornerRadius: 15)
.padding()
.frame(height: 300)
.foregroundStyle(Color("joke-box"))
.opacity(0.8)
// Dynamically show jokes
Text(Jokes.jokes[jokeTracker])
.foregroundStyle(.white)
.clipShape(RoundedRectangle(cornerRadius: 15))
.padding(.horizontal, 30)
}
Spacer()
HStack {
// Previous
Button {
// Previous joke
if jokeTracker != 0 {
jokeTracker -= 1
} else {
// Do nothing as we can't go any further back
}
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Previous")
.foregroundStyle(.white)
}
}
// Next
Button {
// Reset jokes back to start if at the end
if jokeTracker >= 29 {
jokeTracker = 0
} else {
// Next joke
jokeTracker += 1
}
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Next")
.foregroundStyle(.white)
}
}
}
.frame(width: 350)
// Randomise joke
Button {
jokeTracker = Int.random(in: 0...29)
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Random")
.foregroundStyle(.white)
}
.padding(.bottom, 50)
}
}
.frame(width: 350)
}
}
}
Jokes Struct
import SwiftUI
struct ContentView: View {
@State var jokeTracker = 0
var body: some View {
ZStack {
// Background
Image("bg")
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
.opacity(0.8)
VStack {
Spacer()
// Joke
ZStack {
RoundedRectangle(cornerRadius: 15)
.padding()
.frame(height: 300)
.foregroundStyle(Color("joke-box"))
.opacity(0.8)
// Dynamically show jokes
Text(Jokes.jokes[jokeTracker])
.foregroundStyle(.white)
.clipShape(RoundedRectangle(cornerRadius: 15))
.padding(.horizontal, 30)
}
Spacer()
HStack {
// Previous
Button {
// Previous joke
if jokeTracker != 0 {
jokeTracker -= 1
} else {
// Do nothing as we can't go any further back
}
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Previous")
.foregroundStyle(.white)
}
}
// Next
Button {
// Reset jokes back to start if at the end
if jokeTracker >= 29 {
jokeTracker = 0
} else {
// Next joke
jokeTracker += 1
}
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Next")
.foregroundStyle(.white)
}
}
}
.frame(width: 350)
// Randomise joke
Button {
jokeTracker = Int.random(in: 0...29)
} label: {
ZStack {
RoundedRectangle(cornerRadius: 10)
.frame(height: 50)
.foregroundStyle(Color("button-box"))
Text("Random")
.foregroundStyle(.white)
}
.padding(.bottom, 50)
}
}
.frame(width: 350)
}
}
}
