95: Milestone of 16-18

그루두·2024년 9월 2일
0

100 days of SwiftUI

목록 보기
103/108

Milestone: Projects 16-18

key points

higher-order functions

map(), filter(), compactMap()이 예시이다.

예시로 아래 코드가 있다.

// 1
let numbers = [1, 2, 3, 4, 5]
var evens = [Int]()

for number in numbers {
    if number.isMultiple(of: 2) {
        evens.append(number)
    }
}

// 2
let numbers = [1, 2, 3, 4, 5]
let evens = numbers.filter { $0.isMultiple(of: 2) }

2의 경우 filter를 사용해서 상수로 evens를 간단히 설정할 수 있다.

let numbers = ["1", "2", "fish", "3"]
let evensMap = numbers.map(Int.init)
let evensCompactMap = numbers.compactMap(Int.init)

거기에 더해 compactMap()은 nil을 제외하고 evensCompactMap을 생성한다.

Result

Result는 Optional과 닮았지만, if let과 같은 syntactic sugar가 없다. 그리고 throw 함수와 바꿔 사용할 수 있고, Result의method인 map(), mapError()를 사용할 수 있다.

challenge

Your challenge this time can be easy or hard depending on how far you want to take it, but at its core the project is simple: you need to build an app that helps users roll dice then store the results they had.

At the very least you should lets users roll dice, and also let them see results from previous rolls. However, if you want to push yourself further you can try one or more of the following:

  1. Let the user customize the dice that are rolled: how many of them, and what type: 4-sided, 6-sided, 8-sided, 10-sided, 12-sided, 20-sided, and even 100-sided.
  2. Show the total rolled on the dice.
  3. Store the results using JSON or SwiftData – anywhere they are persistent.
  4. Add haptic feedback when dice are rolled.
  5. For a real challenge, make the value rolled by the dice flick through various possible values before settling on the final figure.

solution

1. 사용자가 주사위의 면 수(최댓값) 정하게 하기

textfield에서 정수를 입력받고 0 이하 값은 에러 메시지를 보여준다.

@State private var maxNumber = 6
    @State private var isShowingErrorMessage = false
    var body: some View {
        VStack {
            Spacer()
            VStack(alignment: .leading) {
                TextField("Max number of the dice", value: $maxNumber, formatter: NumberFormatter())
                    .font(.largeTitle)
                    .keyboardType(.numberPad)
                if isShowingErrorMessage {
                    Text("Only numbers bigger than 0 is possible")
                        .foregroundStyle(.secondary)
                }
            }
            Spacer()
            Button(action: {
                if maxNumber < 1 {
                    isShowingErrorMessage = true
                } else {
                    isShowingErrorMessage = false
                }
            }, label: {
                Text("Let's Roll")
                    .font(.title)
            })
        }
        .padding()
    }

커밋 링크

2. 주사위의 총합을 보여주기

State인 totalNumber를 설정해서 나오는 주사위 숫자를 더해서 화면에 나타냈다.

	Text("Total: \(totalNumber) / Roll Count: \(rollCount)")
	// ...
    func rollDice() {
        newNumber = Int.random(in: 1...6)
        rollCount += 1
        totalNumber += newNumber!
    }

커밋 링크

3. UserDefaults에 주사위 값 저장하기

프로젝트에서 제안한 건 SwiftData나 JSON이지만, 현재 값과 총합 두 가지만 저장하고 사용하기에 UserDefaults를 간단히 활용하는 게 적절해서 UserDefaults를 사용했다.

    @State private var rollCount = UserDefaults.standard.integer(forKey: "RollCount")
    @State private var totalNumber = UserDefaults.standard.integer(forKey: "TotalNumber")
    // ... 
    func rollDice() {
        newNumber = Int.random(in: 1...6)
        rollCount += 1
        UserDefaults.standard.set(rollCount, forKey: "RollCount")
        totalNumber += newNumber!
        UserDefaults.standard.set(totalNumber, forKey: "TotalNumber")
    }

커밋 링크

4. 주사위를 굴릴 때 실행될 햅틱 설정하기

.sensoryFeedback()으로 버튼을 실행하면 햅틱이 실행되도록 설정했다.

            Button(action: {
                rollDice()
            }, label: {
                Text(newNumber == nil ? "?" : String(newNumber!))
                    .font(.largeTitle)
                    .padding(50)
                    .background(.white)
                    .clipShape(Circle())
                    .shadow(radius: 5)
            })
            .sensoryFeedback(.impact, trigger: newNumber)

커밋 링크

profile
계속 해보자

0개의 댓글

관련 채용 정보