본문 바로가기

개발 프로젝트

[SwitfUI] 깨워줘요 앱 개발

728x90

"깨워줘요" 앱은 아직 출시하진 않았지만 연습용으로 만들어본 앱입니다.

012345

디자인부터 개발까지 다하느라 좀 공수가 들었지만 그래도 만들어놓고 나니 뿌듯하네요ㅎㅎ

우선 바탕화면은 제가 좋아하는 Glassmorphism 디자인으로 꾸몄습니다.

마치 유리 위에 도형들이 있는 것 같이 보여 글래스모피즘이라고 하는데요, 

ZStack {
    Circle()
        .frame(width: 300)
        .foregroundColor(Color.blue.opacity(0.3))
        .blur(radius: 10)
        .offset(x: -100, y : -150)
    Circle()
        .frame(width: 400)
        .foregroundColor(Color("lineColor").opacity(0.3))
        .blur(radius: 10)
        .offset(x: -50, y: 100)
    RoundedRectangle(cornerRadius: 30, style : .continuous)
        .frame(width: 500, height: 500)
        .foregroundStyle(LinearGradient(colors : [.purple, .mint], startPoint: .top, endPoint: .leading))
        .offset(x : 300)
        .opacity(0.5)
        .blur(radius : 30)
        .rotationEffect(.degrees(30))
}

 

이 도형들 뒤에 투명도를 준 박스를 올려주면 됩니다.

 

그리구 지하철 정보는 공공데이터포털에서 지하철 정보와 위, 경도 정보를 가져와서 JSON데이터로 변환하는 과정을 거쳤습니다.

[
    {
    "line":"01호선",
    "name":"녹양",
    "code":1908,
    "lat":37.75938,
    "lng":127.042292
    },
    {
    "line":"01호선",
    "name":"남영",
    "code":1002,
    "lat":37.541021,
    "lng":126.9713
    },
    {
    "line":"01호선",
    "name":"용산",
    "code":1003,
    "lat":37.529849,
    "lng":126.964561
    },
    {
    "line":"01호선",
    "name":"노량진",
    "code":1004,
    "lat":37.514219,
    "lng":126.942454
    }
  ...

이 JSON 데이터를 불러오는 코드는요

 

우선, 지하철 역 데이터를 저장할 struct를 하나 만들었습니다.

struct SubWayStations : Decodable,Identifiable,Hashable{
    let line : String
    let name : String
    let code : QuantumValue
    let lat : Double
    let lng : Double
    var id : Double { lat }
}

* 여기서 QuantumValue라는 타입은 String이나 Int 둘다 수용이 가능한 타입입니다. JSON데이터에 code 값은 어떤 역은 String으로 어떤 역은 Int 로 들어가있기 때문에 이렇게 처리를 했습니다.

enum QuantumValue : Decodable, Hashable{
    case int(Int), string(String)
    
    init(from decoder : Decoder) throws{
        if let int = try? decoder.singleValueContainer().decode(Int.self){
            self = .int(int)
            return
        }
        if let string = try? decoder.singleValueContainer().decode(String.self){
            self = .string(string)
            return
        }
        throw QuantumError.missingValue
    }
    enum QuantumError : Error {
        case missingValue
    }
}

이제 불러와야겠죠

.onAppear{
    @ObservedObject var datas = ReadData()
    self.SubwayStations = datas.subways
}
//stationJSON 데이터 읽기위한 클래스
class ReadData : ObservableObject{
    @Published var subways = [SubWayStations]()
    init(){
        loadData()
    }
    func loadData(){
        guard let url = Bundle.main.url(forResource: "StationJSON", withExtension: "json")
        else{
            print("Json file not found")
            return
        }
        let data = try? Data(contentsOf: url)
        let decoder = JSONDecoder()
        let subways = try? decoder.decode([SubWayStations].self, from : data!)
        self.subways = subways!
    }
}

 

이제 그럼 도착역까지 지정했으니, 

현재 위치를 Mapkit에서 받아와 도착지 위치와의 거리 계산하는 과정이 있어야겠지요.

.onAppear{
    calcDistance()
    Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { timer in
        if(isArrived == false && isAlarmOn == false){
            calcDistance()
        }
    }
}
func calcDistance(){
    if(selectedStation.get().count>0){
        let curLocation = CLLocationCoordinate2D(latitude: locationInfo.get().center.latitude, longitude: locationInfo.get().center.longitude)
        let desLocation = CLLocationCoordinate2D(latitude: selectedStation.get()[0].lat, longitude: selectedStation.get()[0].lng)

        distance = Double(curLocation.distance(to : desLocation)/1000)
        //계산된 거리를 활용하는 코드..
    }
}
extension CLLocationCoordinate2D {
    /// Returns the distance between two coordinates in meters.
    func distance(to: CLLocationCoordinate2D) -> CLLocationDistance {
        MKMapPoint(self).distance(to: MKMapPoint(to))
    }
    
}

5초에 한번씩 남은 거리를 계산하기 위해 타이머를 사용했고 거리계산에는 MKMapPoint의 distance 메소드를 사용했습니다.

 

또 목적지역에 지정한 범위 내에 도착했을 때는 알람을 울려줘야겠죠.

Text(!isArrived ? "\(selectedDistance) 전에 깨워드릴게요" : "지정하신 반경 내에 도착했어요")
    .font(.system(size:15))
    .foregroundColor(Color("lineColor"))
    .onReceive(timer) { _ in
        if(isAlarmOn == true){
            AudioServicesPlaySystemSound(SystemSoundID(1322))
    	}
}
    
struct SystemSounds: Identifiable {
    let id: Int
    let title: String
}

extension SystemSounds {
    static let all: [SystemSounds] = [.none, .alarm, .anticipate, .bloom, .calypso, .minuet, .suspense, .tweet]
    
    static let none = SystemSounds(id: 0, title: "None")
    static let alarm = SystemSounds(id: 1005, title: "Alarm")
    static let tweet = SystemSounds(id: 1016, title: "Tweet")
    static let anticipate = SystemSounds(id: 1320, title: "Anticipate")
    static let bloom = SystemSounds(id: 1321, title: "Bloom")
    static let calypso = SystemSounds(id: 1322, title: "Calypso")
    static let minuet = SystemSounds(id: 1327, title: "Minuet")
    static let suspense = SystemSounds(id: 1332, title: "Suspense")
}

이로써 어느정도 코드정리는 된 것 같네요

 

테스트를 해볼건데, 현재 저는 신길동에 있고 가장 가까운 역인 대방역에 도착했다고 가정을 해보겠습니다.