mirror of
https://github.com/ivanvorobei/SwiftUI.git
synced 2026-05-30 03:10:54 +02:00
Added a project.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// AddCurrencyView.swift
|
||||
// Currency-SwiftUI
|
||||
//
|
||||
// Created by Alex Liu on 2019-06-20.
|
||||
// Copyright © 2018 Alex Liu <alexliubo@gmail.com> All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct AddCurrencyView : View {
|
||||
@EnvironmentObject var userData: UserData
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
ForEach(userData.allCurrencies) { currency in
|
||||
return HStack {
|
||||
Button(action: { self.select(currency) }) {
|
||||
Text("\(currency.code) - \(currency.name)")
|
||||
}
|
||||
Spacer()
|
||||
if self.isSelected(currency) {
|
||||
Image(systemName: "checkmark").foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.navigationBarItem(title: Text("Add Currency"))
|
||||
}
|
||||
|
||||
private func select(_ currency: Currency) {
|
||||
if userData.userCurrency.map({ $0.code }).contains(currency.code) {
|
||||
userData.userCurrency.removeAll{$0.code == currency.code}
|
||||
}
|
||||
else {
|
||||
userData.userCurrency.append(currency)
|
||||
}
|
||||
}
|
||||
|
||||
private func isSelected(_ currency: Currency) -> Bool {
|
||||
return userData.userCurrency.map({ $0.code }).contains(currency.code)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// Currency-SwiftUI
|
||||
//
|
||||
// Created by Alex Liu on 2019-06-27.
|
||||
// Copyright © 2018 Alex Liu <alexliubo@gmail.com> All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
// MARK: UISceneSession Lifecycle
|
||||
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||
// Called when a new scene session is being created.
|
||||
// Use this method to select a configuration to create the new scene with.
|
||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
|
||||
// Called when the user discards a scene session.
|
||||
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Currency-SwiftUI
|
||||
//
|
||||
// Created by Alex Liu on 2019-06-20.
|
||||
// Copyright © 2018 Alex Liu <alexliubo@gmail.com> All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct ConverterView : View {
|
||||
@EnvironmentObject var userData: UserData
|
||||
@State var baseAmount: String = "1.0"
|
||||
@State var isEditing: Bool = false
|
||||
@State var lastUpdated: String = ""
|
||||
|
||||
var body: some View {
|
||||
let inset = EdgeInsets(top: -8, leading: -20, bottom: -7, trailing: 5)
|
||||
let doubleValue: Double = Double(self.$baseAmount.value) ?? 1.0
|
||||
|
||||
return ZStack(alignment: .bottomTrailing) {
|
||||
VStack(alignment: .leading){
|
||||
Text("From:").bold().color(.gray)
|
||||
HStack{
|
||||
// Flag
|
||||
Text("\(userData.baseCurrency.flag)").padding(5)
|
||||
// Code and name
|
||||
VStack(alignment: .leading){
|
||||
Text(userData.baseCurrency.code).color(.white)
|
||||
Text(userData.baseCurrency.name).color(.white)
|
||||
}
|
||||
Spacer()
|
||||
// Amount and conversion
|
||||
TextField($baseAmount, placeholder: Text("1.0"), onCommit: {
|
||||
// TODO: update all currencies on the following list
|
||||
}).foregroundColor(.white)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 5)
|
||||
.fill(Color.clear)
|
||||
.border(Color(red: 0.7, green: 0.7, blue: 0.7), width: 1 / UIScreen.main.scale, cornerRadius: 5)
|
||||
.padding(inset)
|
||||
)
|
||||
}.background(Color.blue).cornerRadius(5)
|
||||
Text("To:").bold().color(.gray)
|
||||
List {
|
||||
// TODO: should filter out BaseCurrency from list
|
||||
ForEach(userData.userCurrency) { currency in
|
||||
CurrencyItemView(currency: currency, baseAmount: doubleValue, isEditing: self.$isEditing).tapAction {
|
||||
// Swap this and base
|
||||
self.userData.baseCurrency = currency
|
||||
}
|
||||
}
|
||||
}.onAppear(perform: loadCurrencies)
|
||||
.navigationBarItem(title: Text("Currenceis 💱"))
|
||||
.navigationBarItems(trailing: Button(action: { self.isEditing.toggle() }) {
|
||||
if !self.isEditing {
|
||||
Text("Edit")
|
||||
} else {
|
||||
Text("Done").bold()
|
||||
}
|
||||
})
|
||||
Text("Last updated: \(self.lastUpdated)").color(.gray).bold()
|
||||
}
|
||||
NavigationButton(destination: AddCurrencyView().environmentObject(self.userData)) {
|
||||
Text("💰")
|
||||
}.frame(width: 46, height: 46, alignment: .center)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 23)
|
||||
.fill(Color.blue)
|
||||
.border(Color(red: 0.7, green: 0.7, blue: 0.7), width: 1 / UIScreen.main.scale, cornerRadius: 23))
|
||||
.foregroundColor(.white).font(.largeTitle)
|
||||
}.padding()
|
||||
}
|
||||
|
||||
private func loadCurrencies() {
|
||||
// Check if last updated is the same date
|
||||
// if not the same pull from remote with base currency
|
||||
let url = URL(string: "https://api.exchangeratesapi.io/latest?base=USD")!
|
||||
|
||||
let task = URLSession.shared.dataTask(with: url, completionHandler: { data, _, _ in
|
||||
if let data = data {
|
||||
if let decoded: CurrencyList = self.decodeData(CurrencyList.self, data){
|
||||
//
|
||||
self.lastUpdated = decoded.date
|
||||
|
||||
// generate currency data
|
||||
var newCurrencies = [Currency]()
|
||||
for key in decoded.rates.keys {
|
||||
let newCurrency = Currency(name: supportedCurrencies[key]?[0] ?? "Unknown", rate: 1.0 / (decoded.rates[key] ?? 1.0), symbol: supportedCurrencies[key]?[1] ?? "", code: key)
|
||||
newCurrencies.append(newCurrency)
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.userData.allCurrencies = newCurrencies
|
||||
|
||||
if let base = self.userData.allCurrencies.filter({ $0.symbol == self.userData.baseCurrency.symbol }).first {
|
||||
self.userData.baseCurrency = base
|
||||
}
|
||||
|
||||
var tempNewUserCurrency = [Currency]()
|
||||
let userCurrencies = self.userData.userCurrency.map{ $0.code }
|
||||
for c in self.userData.allCurrencies {
|
||||
if userCurrencies.contains(c.code){
|
||||
tempNewUserCurrency.append(c)
|
||||
}
|
||||
}
|
||||
|
||||
self.userData.userCurrency = tempNewUserCurrency
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
task.resume()
|
||||
}
|
||||
}
|
||||
|
||||
extension ConverterView
|
||||
{
|
||||
private func decodeData<T>(_ decodeObject: T.Type, _ data: Data) -> T? where T: Codable
|
||||
{
|
||||
let decoder = JSONDecoder()
|
||||
do
|
||||
{
|
||||
return try decoder.decode(decodeObject.self, from: data)
|
||||
}
|
||||
catch let jsonErr
|
||||
{
|
||||
print("Error decoding Json ", jsonErr)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct ContentView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
ConverterView().environmentObject(UserData())
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// CurrencyData.swift
|
||||
// Currency-SwiftUI
|
||||
//
|
||||
// Created by Alex Liu on 2019-06-20.
|
||||
// Copyright © 2018 Alex Liu <alexliubo@gmail.com> All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct Currency: Equatable, Hashable, Codable, Identifiable{
|
||||
let id: UUID
|
||||
let name: String
|
||||
|
||||
// This is a rate to USD
|
||||
let rate: Double
|
||||
let symbol: String
|
||||
let code: String
|
||||
|
||||
init(name: String, rate: Double, symbol: String = "", code: String) {
|
||||
self.id = UUID()
|
||||
self.name = name
|
||||
self.rate = rate
|
||||
self.symbol = symbol
|
||||
self.code = code
|
||||
}
|
||||
|
||||
// Get unicode flag by currency symbol
|
||||
var flag: String {
|
||||
// Hard-code EU for now
|
||||
if self.symbol == "EU" { return "🇪🇺" }
|
||||
let base : UInt32 = 127397
|
||||
var s = ""
|
||||
for v in self.symbol.uppercased().unicodeScalars {
|
||||
s.unicodeScalars.append(UnicodeScalar(base + v.value)!)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// CurrencyItem.swift
|
||||
// Currency-SwiftUI
|
||||
//
|
||||
// Created by Alex Liu on 2019-06-20.
|
||||
// Copyright © 2018 Alex Liu <alexliubo@gmail.com> All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CurrencyItemView: View {
|
||||
@EnvironmentObject var userData: UserData
|
||||
let currency: Currency
|
||||
let baseAmount: Double
|
||||
@Binding var isEditing: Bool
|
||||
|
||||
var body: some View {
|
||||
let currency = self.currency
|
||||
let converstionRate = String(format: "%.4f", currency.rate / userData.baseCurrency.rate)
|
||||
let totalAmount = String(format: "%.3f", baseAmount * ( userData.baseCurrency.rate / currency.rate))
|
||||
|
||||
return HStack {
|
||||
if self.isEditing {
|
||||
// This is for delete mode
|
||||
HStack(alignment: .center){
|
||||
Image(systemName: "minus.circle")
|
||||
.foregroundColor(.red)
|
||||
.tapAction(count: 1) {
|
||||
self.delete()
|
||||
}
|
||||
|
||||
Text(currency.flag).font(.title)
|
||||
VStack(alignment: .leading){
|
||||
Text(currency.code)
|
||||
Text(currency.name).color(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// This is normal mode
|
||||
HStack{
|
||||
// Flag
|
||||
Text(currency.flag).font(.largeTitle)
|
||||
// Code and name
|
||||
VStack(alignment: .leading){
|
||||
Text(currency.code).font(.headline)
|
||||
Text(currency.name).font(.footnote).color(.gray)
|
||||
}
|
||||
Spacer()
|
||||
// Amount and conversion
|
||||
VStack(alignment: .trailing){
|
||||
Text("\(totalAmount)")
|
||||
// Would be 1 this currency = xxx base currency
|
||||
Text("1 \(currency.code) = \(converstionRate) \(userData.baseCurrency.code)").color(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: need to implement
|
||||
private func delete() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// CurrencyList.swift
|
||||
// Currency-SwiftUI
|
||||
//
|
||||
// Created by Alex Liu on 2019-06-21.
|
||||
// Copyright © 2018 Alex Liu <alexliubo@gmail.com> All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
struct CurrencyList: Codable{
|
||||
let base: String
|
||||
let date: String
|
||||
let rates: [String: Double]
|
||||
}
|
||||
|
||||
/// All supported currencies
|
||||
let supportedCurrencies: [String: [String]] = [
|
||||
"THB": ["Thai baht", "TH"],
|
||||
"PHP": ["Philippine peso", "PH"],
|
||||
"CZK": ["Czech koruna", "CZ"],
|
||||
"BRL": ["Brazilian real", "BR"],
|
||||
"CHF": ["Swiss franc", "CH"],
|
||||
"INR": ["Indian rupee", "IN"],
|
||||
"ISK": ["Icelandic króna", "IS"],
|
||||
"HRK": ["Croatian kuna", "HR"],
|
||||
"PLN": ["Polish złoty", "PL"],
|
||||
"NOK": ["Norwegian krone", "NO"],
|
||||
"USD": ["US Dollar", "US"],
|
||||
"CNY": ["Chinese Renminbi", "CN"],
|
||||
"RUB": ["Russian ruble", "RU"],
|
||||
"SEK": ["Swedish krona", "SE"],
|
||||
"MYR": ["Malaysian ringgit", "MY"],
|
||||
"SGD": ["Singapore dollar", "SG"],
|
||||
"ILS": ["Israeli new shekel", "IL"],
|
||||
"TRY": ["Turkish lira", "TR"],
|
||||
"BGN": ["Bulgarian lev", "BG"],
|
||||
"NZD": ["New Zealand dollar", "NZ"],
|
||||
"HKD": ["Hong Kong dollar", "HK"],
|
||||
"RON": ["Romanian leu", "RO"],
|
||||
"EUR": ["Euro", "EU"],
|
||||
"MXN": ["Mexican peso", "MX"],
|
||||
"CAD": ["Canadian Dollar", "CA"],
|
||||
"AUD": ["Australian dollar", "AU"],
|
||||
"GBP": ["Pound sterling", "GB"],
|
||||
"KRW": ["South Korean won", "KR"],
|
||||
"IDR": ["Indonesian rupiah", "ID"],
|
||||
"JPY": ["Japanese yen", "JP"],
|
||||
"DKK": ["Danish krone", "DK"],
|
||||
"ZAR": ["South African rand", "ZA"],
|
||||
"HUF": ["Hungarian forint", "HU"]
|
||||
]
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// SceneDelegate.swift
|
||||
// Currency-SwiftUI
|
||||
//
|
||||
// Created by Alex Liu on 2019-06-27.
|
||||
// Copyright © 2018 Alex Liu <alexliubo@gmail.com> All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||
|
||||
// Use a UIHostingController as window root view controller
|
||||
let window = UIWindow(frame: UIScreen.main.bounds)
|
||||
window.rootViewController = UIHostingController(rootView: NavigationView {
|
||||
ConverterView().environmentObject(UserData())
|
||||
})
|
||||
self.window = window
|
||||
window.makeKeyAndVisible()
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene) {
|
||||
// Called as the scene is being released by the system.
|
||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||
}
|
||||
|
||||
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||
// Called when the scene has moved from an inactive state to an active state.
|
||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||
}
|
||||
|
||||
func sceneWillResignActive(_ scene: UIScene) {
|
||||
// Called when the scene will move from an active state to an inactive state.
|
||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the background to the foreground.
|
||||
// Use this method to undo the changes made on entering the background.
|
||||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||
// Called as the scene transitions from the foreground to the background.
|
||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||
// to restore the scene back to its current state.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// UserData.swift
|
||||
// Currency-SwiftUI
|
||||
//
|
||||
// Created by Alex Liu on 2019-06-20.
|
||||
// Copyright © 2018 Alex Liu <alexliubo@gmail.com> All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
|
||||
private let defaultCurrencies: [Currency] = [
|
||||
Currency(name: "US dollar", rate: 1.0, symbol: "US", code: "USD"),
|
||||
Currency(name: "Canadian dollar", rate: 1.0, symbol: "CA", code: "CAD")
|
||||
]
|
||||
|
||||
@propertyDelegate
|
||||
struct UserDefaultValue<Value: Codable> {
|
||||
|
||||
let key: String
|
||||
let defaultValue: Value
|
||||
|
||||
var value: Value {
|
||||
get {
|
||||
let data = UserDefaults.standard.data(forKey: key)
|
||||
let value = data.flatMap { try? JSONDecoder().decode(Value.self, from: $0) }
|
||||
return value ?? defaultValue
|
||||
}
|
||||
set {
|
||||
let data = try? JSONEncoder().encode(newValue)
|
||||
UserDefaults.standard.set(data, forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class UserData: BindableObject {
|
||||
let didChange = PassthroughSubject<UserData, Never>()
|
||||
|
||||
@UserDefaultValue(key: "allCurrencies", defaultValue: defaultCurrencies)
|
||||
var allCurrencies: [Currency] {
|
||||
didSet {
|
||||
didChange.send(self)
|
||||
}
|
||||
}
|
||||
|
||||
@UserDefaultValue(key: "baseCurrency", defaultValue: defaultCurrencies[0])
|
||||
var baseCurrency: Currency {
|
||||
didSet {
|
||||
didChange.send(self)
|
||||
}
|
||||
}
|
||||
|
||||
@UserDefaultValue(key: "userCurrency", defaultValue: defaultCurrencies)
|
||||
var userCurrency: [Currency] {
|
||||
didSet {
|
||||
didChange.send(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user