Xylophone App
In roughly an hour or two, I created an iOS app for our son who loves playing his Xylophone. I came upon this old repo for inspiration and reference. This was still developed using Storyboard and UIKit. So, I set out to write my own using SwiftUI.
I also borrowed their sound files. The owner of these files belongs to them.
The code looks like this initially.
//
// PlayerView.swift
// Xylo
//
// Created by Lawrence Gimenez on 4/23/23.
//
import SwiftUI
import AVFoundation
var audioPlayer: AVAudioPlayer!
struct PlayerView: View {
var body: some View {
VStack {
Button(action: {
play(sound: "C")
}) {
Text("C")
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 90)
}
.background(Color.red)
Button(action: {
play(sound: "D")
}) {
Text("D")
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 90)
}
.background(Color.orange)
Button(action: {
play(sound: "E")
}) {
Text("E")
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 90)
}
.background(Color.yellow)
Button(action: {
play(sound: "F")
}) {
Text("F")
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 90)
}
.background(Color.green)
Button(action: {
play(sound: "G")
}) {
Text("G")
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 90)
}
.background(Color.teal)
Button(action: {
play(sound: "A")
}) {
Text("A")
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 90)
}
.background(Color.indigo)
Button(action: {
play(sound: "B")
}) {
Text("B")
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 90)
}
.background(Color.purple)
Button(action: {
play(sound: "C")
}) {
Text("C")
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 90)
}
.background(Color.red)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
private func play(sound: String) {
let _ = print("Play \(sound)")
let url = Bundle.main.url(forResource: sound, withExtension: ".wav")
audioPlayer = try! AVAudioPlayer(contentsOf: url!)
audioPlayer.prepareToPlay()
audioPlayer.play()
}
}
struct PlayerView_Previews: PreviewProvider {
static var previews: some View {
PlayerView()
}
}
And here’s what it looks like on the device.
If you notice, the code is so long and redundant. We can do better. Let’s refactor our code by creating a model class so we can use it inside a loop.
Refactor Time
Let’s create a Key model. I imported SwiftUI since I need the Color class.
import SwiftUI
struct Key: Identifiable {
var id: Int
var note: String
var color: Color
}
Go back to our PlayerView
class and let’s create an array based on the Key model.
private var arrayKeys = [
Key(id: 0, note: "C", color: Color.red),
Key(id: 1, note: "D", color: Color.orange),
Key(id: 2, note: "E", color: Color.yellow),
Key(id: 3, note: "F", color: Color.green),
Key(id: 4, note: "G", color: Color.teal),
Key(id: 5, note: "A", color: Color.indigo),
Key(id: 6, note: "B", color: Color.purple),
Key(id: 7, note: "C", color: Color.red)
]
Let’s update our body based on our newly created array.
var body: some View {
VStack {
ForEach(arrayKeys) { key in
Button(action: {
play(sound: key.note)
}) {
Text(key.note)
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 90)
}
.background(key.color)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
Much better. Code is now shorter, if I want to add another key, I would just add another Key object to our array.
You can check the whole source code here. The project is compiled using the latest Xcode 14.3.