Unit Converter - Walkthrough

Unit Converter ဆိုတာကတော့ ကျွန်တော်တို့တွေ unit တွေကို တခုက တခုပြောင်းတဲ့အခါ အလွယ်တကူ ပြောင်းလဲလို့ရအောင် လုပ်ထားတဲ့ app ပဲ ဖြစ်ပါတယ်။ ဒီ application မှာ အပူချိန်​ (temperature), အလျား​ (length), အချိန်​ (time) နဲ့ ထုထည် (volume) ဆိုပြီး unit system ၄မျိုးအတွက် လုပ်ထားတာပဲ ဖြစ်ပါတယ်။

ဒီ project ကတော့ ကျွန်တော် 100 Days of SwiftUI လေ့လာရင်း လိုက်လုပ်ထားတဲ့ project ပဲ ဖြစ်ပါတယ်။

100 Days of SwiftUI
I decided to learn iOS development a couple of months ago. Then, I started checking for online courses from apple’s official course and some courses from Udemy. After checking a few lessons, I don’t feel right. So, I decided to ask my friend who has been working as an iOS

ဒီ project မှာ ကျွန်တော် SwiftUI ကို အသုံးပြုထားတာဖြစ်ပြီး data တွေ အတွက်အချက်အတွက်ကို @State ကိုအသုံးပြုပြီး reactive ဖြစ်အောင် ပြုလုပ်ထားပါတယ်။

အရင်ဆုံးအနေနဲ့ အသုံးပြုမယ်ဆို conversion type ကို ရွေးချယ်ရမှာပဲဖြစ်ပါတယ်။ default အနေနဲ့ကတော့ Temperature ဆိုပြီး ထည့်ထားပါတယ်။ Conversion Type ကို ရွေးချယ်လိုက်တာနဲ့ သက်ဆိုင်ရာ field တွေလဲ အလိုအလျောက် ပြောင်းလဲသွားမှာ ဖြစ်ပါတယ်။ ဒါကိုတော့ SwiftUI မှာ switch ကို အသုံးပြုပြီး ရေးထားတာပဲ ဖြစ်ပါတယ်။

@State private var selectedConversion = "Temperature"

Conversion တခုချင်းဆီအတွက်လဲ __input, __fromUnit, __toUnit ဆိုပြီး သက်ဆိုင်ရာ value အတွေအတွက် သတ်မှတ်ထားပါတယ်။ original input value ရယ်၊ ကိုယ်အခု ရွေးချယ်ထားတဲ့ unit ရယ်၊ ပြောင်းလဲချင်တဲ့ unit အတွက်ရယ်ဆိုပြီးပဲ ဖြစ်ပါတယ်။ default type အနေနဲ့ကတော့ unit တွေထဲကမှ တခုကို ရွေးချယ်ပြီး သတ်မှတ်ပေးထားတာပဲ ဖြစ်ပါတယ်။

@State private var temperatureInput = 0.0
@State private var temperatureFromUnit: UnitTemperature = .celsius
@State private var temperatureToUnit: UnitTemperature = .celsius
@State private var lengthInput = 0.0
@State private var lengthFromUnit: UnitLength = .meters
@State private var lengthToUnit: UnitLength = .meters
@State private var timeInput = 0.0
@State private var timeFromUnit: UnitDuration = .minutes
@State private var timeToUnit: UnitDuration = .minutes
@State private var volumeInput = 0.0
@State private var volumeFromUnit: UnitVolume = .cubicMeters
@State private var volumeToUnit: UnitVolume = .cubicMeters

ဒီမှာ အသုံးပြုထားတဲ့ unit တွေက custom ရေးထားတာမဟုတ်ဘဲ swift foundation မှာ ပါပြီးသား unit တွေပဲဖြစ်ပါတယ်။​ ဒီအတွက် unit တခုကနေ တခုကို ပြောင်းတဲ့အခါ တွက်ချက်တာကို ကျွန်တော်တို့ကိုယ်တိုင် လုပ်စရာမလိုတော့ဘဲ swift ကို လုပ်ခိုင်းလို့ ရသွားတာပဲ ဖြစ်ပါတယ်။

var temperatureResult: String {
    let convertedMeasurement = Measurement(value: temperatureInput, unit: temperatureFromUnit) .converted(to: temperatureToUnit)
    let formatter = MeasurementFormatter()
    formatter.unitOptions = .providedUnit
    return formatter.string(from: convertedMeasurement)
}
var lengthResult: String {
    let convertedMeasurement = Measurement(value: lengthInput, unit: lengthFromUnit) .converted(to: lengthToUnit)
    let formatter = MeasurementFormatter()
    formatter.unitOptions = .providedUnit
    return formatter.string(from: convertedMeasurement)
}
var timeResult: String {
    let convertedMeasurement = Measurement(value: timeInput, unit: timeFromUnit) .converted(to: timeToUnit)
    let formatter = MeasurementFormatter()
    formatter.unitOptions = .providedUnit
    return formatter.string(from: convertedMeasurement)
}
var volumeResult: String {
    let convertedMeasurement = Measurement(value: volumeInput, unit: volumeFromUnit) .converted(to: volumeToUnit)
    let formatter = MeasurementFormatter()
    formatter.unitOptions = .providedUnit
    return formatter.string(from: convertedMeasurement)
}

UI ကိုတော့ text field, picker နဲ့ text components တွေသုံးပြီးတော့ တည်ဆောက်ထားပါတယ်။ ဒီမှာ picker ကို တခုနဲ့တခု မတူဘဲ ပုံစံအမျိုးမျိုးနဲ့ အသုံးပြုထားပါတယ်။

.segmented

.wheel

.inline

SwiftUI နဲ့ တည်ဆောက်ပုံ အသေးစိတ်ကို ဒီမှာ ကြည့်နိုင်ပါတယ်။

//
//  ContentView.swift
//  Unit Converter
//
//  Created by Arkar Min Tun on 14/08/2023.
//

import SwiftUI

struct ContentView: View {
    @State private var selectedConversion = "Temperature"
    @FocusState private var amountIsFocused: Bool
    
    let conversions = ["Temperature", "Length", "Time", "Volume"]
    
    
    @State private var temperatureInput = 0.0
    @State private var temperatureFromUnit: UnitTemperature = .celsius
    @State private var temperatureToUnit: UnitTemperature = .celsius
    
    let temperatureConversions: [UnitTemperature] = [.celsius, .kelvin,.fahrenheit]
    
    var temperatureResult: String {
        let convertedMeasurement = Measurement(value: temperatureInput, unit: temperatureFromUnit) .converted(to: temperatureToUnit)
        let formatter = MeasurementFormatter()
        formatter.unitOptions = .providedUnit
        return formatter.string(from: convertedMeasurement)
    }
    
    
    @State private var lengthInput = 0.0
    @State private var lengthFromUnit: UnitLength = .meters
    @State private var lengthToUnit: UnitLength = .meters
    
    let lengthConversions: [UnitLength] = [.centimeters, .decimeters, .fathoms, .feet, .inches, .kilometers, .meters, .miles, .millimeters, .nanometers, .nauticalMiles, .yards]
    
    var lengthResult: String {
        let convertedMeasurement = Measurement(value: lengthInput, unit: lengthFromUnit) .converted(to: lengthToUnit)
        let formatter = MeasurementFormatter()
        formatter.unitOptions = .providedUnit
        return formatter.string(from: convertedMeasurement)
    }
    
    @State private var timeInput = 0.0
    @State private var timeFromUnit: UnitDuration = .minutes
    @State private var timeToUnit: UnitDuration = .minutes
    
    let timeConversions: [UnitDuration] = [.hours, .microseconds, .milliseconds, .minutes, .nanoseconds, .picoseconds, .seconds]
    
    var timeResult: String {
        let convertedMeasurement = Measurement(value: timeInput, unit: timeFromUnit) .converted(to: timeToUnit)
        let formatter = MeasurementFormatter()
        formatter.unitOptions = .providedUnit
        return formatter.string(from: convertedMeasurement)
    }
    
    @State private var volumeInput = 0.0
    @State private var volumeFromUnit: UnitVolume = .cubicMeters
    @State private var volumeToUnit: UnitVolume = .cubicMeters
    
    let volumeConversions: [UnitVolume] = [.acreFeet, .bushels, .centiliters, .cubicCentimeters, .cubicDecimeters, .cubicFeet, .cubicInches, .cubicKilometers, .cubicMeters, .cubicMiles, .cubicMillimeters, .cubicYards, .cups, .deciliters, .fluidOunces, .gallons, .kiloliters, .liters, .megaliters, .metricCups, .milliliters, .pints, .quarts, .tablespoons, .teaspoons]
    
    var volumeResult: String {
        let convertedMeasurement = Measurement(value: volumeInput, unit: volumeFromUnit) .converted(to: volumeToUnit)
        let formatter = MeasurementFormatter()
        formatter.unitOptions = .providedUnit
        return formatter.string(from: convertedMeasurement)
    }
    
    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker("Conversion Type", selection: $selectedConversion) {
                        ForEach(conversions, id: \.self) {
                            Text($0)
                        }
                    }
                }
                
                switch selectedConversion {
                    case "Temperature":
                        Section {
                            HStack {
                                TextField(
                                    "Temperature",
                                    value: $temperatureInput,
                                    format: .number
                                )
                                .keyboardType(.decimalPad)
                                .focused($amountIsFocused)
                                
                                Picker("", selection: $temperatureFromUnit) {
                                    ForEach(temperatureConversions, id: \.self) {
                                        Text($0.symbol)
                                    }
                                }
                            }
                        } header: {
                            Text("Temperature Conversion")
                        }
                        
                        Section {
                            Picker("Unit To", selection: $temperatureToUnit) {
                                ForEach(temperatureConversions, id: \.self) {
                                    Text($0.symbol)
                                }
                            }
                            .pickerStyle(.segmented)
                        } header: {
                            Text("Convert To")
                        }
                        
                        Section {
                            Text(temperatureResult)
                        } header: {
                            Text("Result")
                        }
                        
                    case "Length":
                        Section {
                            HStack {
                                TextField(
                                    "Length",
                                    value: $lengthInput,
                                    format: .number
                                )
                                .keyboardType(.decimalPad)
                                .focused($amountIsFocused)
                                
                                Picker("", selection: $lengthFromUnit) {
                                    ForEach(lengthConversions, id: \.self) {
                                        Text($0.symbol)
                                    }
                                }
                            }
                        } header: {
                            Text("Length Conversion")
                        }
                        
                        Section {
                            Picker("Convert To", selection: $lengthToUnit) {
                                ForEach(lengthConversions, id: \.self) {
                                    Text($0.symbol)
                                }
                            }
                            .pickerStyle(.wheel)
                        }
                        
                        Section {
                            Text(lengthResult)
                        } header: {
                            Text("Result")
                        }
                        
                    case "Time":
                        Section {
                            HStack {
                                TextField(
                                    "Time",
                                    value: $timeInput,
                                    format: .number
                                )
                                .keyboardType(.decimalPad)
                                .focused($amountIsFocused)
                                
                                Picker("", selection: $timeFromUnit) {
                                    ForEach(timeConversions, id: \.self) {
                                        Text($0.symbol)
                                    }
                                }
                            }
                        } header: {
                            Text("Time Conversion")
                        }
                        
                        Section {
                            Picker("Convert To", selection: $timeToUnit) {
                                ForEach(timeConversions, id: \.self) {
                                    Text($0.symbol)
                                }
                            }
                            .pickerStyle(.inline)
                        }
                        
                        Section {
                            Text(timeResult)
                        } header: {
                            Text("Result")
                        }
                        
                    case "Volume":
                        Section {
                            HStack {
                                TextField(
                                    "Volume",
                                    value: $volumeInput,
                                    format: .number
                                )
                                .keyboardType(.decimalPad)
                                .focused($amountIsFocused)
                                
                                Picker("", selection: $volumeFromUnit) {
                                    ForEach(volumeConversions, id: \.self) {
                                        Text($0.symbol)
                                    }
                                }
                            }
                        } header: {
                            Text("Volume Conversion")
                        }
                        
                        Section {
                            Picker("Convert To", selection: $volumeToUnit) {
                                ForEach(volumeConversions, id: \.self) {
                                    Text($0.symbol)
                                }
                            }
                            .pickerStyle(.navigationLink)
                        }
                        
                        Section {
                            Text(volumeResult)
                        } header: {
                            Text("Result")
                        }
                        
                    default:
                        Section {
                            Text("HELLO")
                        } header: {
                            Text("Others")
                        }
                }
            }
            .navigationTitle("Unit Converter")
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    Spacer()
                    Button("Done") {
                        amountIsFocused = false
                    }
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}