Splitzy

After enjoying a nice dinner with friends, have you ever found yourself struggling to split the bill? You open the calculator to crunch the numbers, tell your friends how much each person owes, and when someone asks if you have Venmo, you pull up the app to display your QR code. Then, as they inquire about the total, you tend to go back to the calculator to double-check the figures, only to repeat the process when another friend requests the Venmo QR code again. 🫨

Introducing Splitzy, a simple app created with SwiftUI that allows users to upload screenshots of their transaction app account information, such as Venmo, Cash App, or PayPal, making it easier to split bills among multiple people.

A quick day and half project. I thought building this app would be a great way to kickstart my SwiftUI journey. Through this project, I realized that even simple apps can be incredibly useful with a little imagination and realistic use-case.

Search List.


import SwiftUI

struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme
    
    @State private var checkAmount = ""
    @State private var numberOfPeople = 2
    @State private var tipPercentage = 15
    @FocusState private var amountIsFocused: Bool
    
    @State private var venmoImage: Image?
    @State private var showImagePicker = false
    @State private var selectedImage: UIImage?
    
    let tipPercentages = [10, 15, 18, 20, 23, 25, 0]
    
    var currencySymbol: String {
        Locale.current.currencySymbol ?? "$"
    }
    var totalPerPerson: Double {
        guard let amount = Double(checkAmount), amount > 0 else { return 0.0 }
        
        let peopleCount = Double(numberOfPeople)
        let tipValue = amount / 100 * Double(tipPercentage)
        let grandTotal = amount + tipValue
        
        return grandTotal / peopleCount
    }
    
    var body: some View {
        NavigationStack {
            Form {
                Section {
                    HStack {
                        Text(currencySymbol)
                        TextField("Amount", text: $checkAmount)
                            .keyboardType(.decimalPad)
                            .focused($amountIsFocused)
                            .onChange(of: checkAmount) { newValue in
                                let filtered = newValue.filter { "0123456789.".contains($0) }
                                if filtered != newValue {
                                    checkAmount = filtered
                                }
                            }
                    }
                    
                    Picker("Number of people", selection: $numberOfPeople) {
                        ForEach(0..<100) {
                            Text("\($0) people")
                        }
                    }
                }

                Section("How much do you want to tip?") {
                    Picker("Tip percentage", selection: $tipPercentage) {
                        ForEach(tipPercentages, id: \.self) {
                            Text($0, format: .percent)
                        }
                    }
                    .pickerStyle(.segmented)
                    .tint(.orange)
                }
                
                VStack(spacing: 10) {
                    Text("Split per person:")
                        .font(.system(size: 30))
                        .font(.headline)
                        .multilineTextAlignment(.center)
                    
                    Text(totalPerPerson, format: .currency(code: Locale.current.currency?.identifier ?? "USD"))
                        .font(.system(size: 50))
                        .foregroundColor(.orange)
                        .multilineTextAlignment(.center)
                    
                    Text("Send Venmo to:")
                        .font(.headline)
                        .padding(.top, 30)
                    
                    venmoImageView()
                }
                
                .frame(maxWidth: .infinity)
                .padding(.bottom, 10)
                .listRowBackground(Color.clear)
            }

            .background(Color(.systemGroupedBackground))
            .padding(.top, 5)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Image(colorScheme == .dark ? "splitzyLogoWhite" : "splitzyLogo")
                        .resizable()
                        .scaledToFit()
                        .frame(height: 40)
                        .padding(.top, 70)
                         .offset(y: 5)
                }
                
                ToolbarItem(placement: .navigationBarTrailing) {
                    if amountIsFocused {
                        Button("Done") {
                            amountIsFocused = false
                        }
                    }
                }
            }
            
            .sheet(isPresented: $showImagePicker) {
                ImagePicker(image: $selectedImage)
                    .onDisappear {
                        if let selectedImage = selectedImage {
                            venmoImage = Image(uiImage: selectedImage)
                        }
                    }
            }
        }
    }
    
    @ViewBuilder
    private func venmoImageView() -> some View {
        if let venmoImage = venmoImage {
            venmoImage
                .resizable()
                .scaledToFill()
                .frame(width: 200, height: 200)
                .cornerRadius(30)
                .onTapGesture {
                    showImagePicker.toggle()
                }
        } else {
            Rectangle()
                .fill(Color.gray.opacity(0.3))
                .frame(width: 200, height: 200)
                .clipShape(Rectangle())
                .cornerRadius(30)
                .overlay(
                    Text("Add your\nVenmo QR\nImage:")
                        .foregroundColor(.white)
                        .multilineTextAlignment(.center)
                )
                .onTapGesture {
                    showImagePicker.toggle()
                }
        }
    }
}

#Preview {
    ContentView()
}