Coding01

Coding 点滴

0%

学习 iOS Widgets 开发之 exchange 汇率转换工具

学习 iOS 小组件开发,我找了一个不错的开源代码Littleor/iWidget入手,模仿编写汇率换算工具。

汇率接口使用:Foreign exchange rates API with currency conversion

具体网络请求代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//
// ExchangeData.swift
// iWidget
//
// Created by 叶梅树 on 2020/11/3.
//

import Foundation

struct ExchangeRate {
let from: Double
let to: Double
let fromType: String
let toType: String
}

struct ExchangeLoader {
static func fetch(from: String, to: String, completion: @escaping (Result<ExchangeRate, Error>) -> Void) {
let ExchangeURL = URL(string: "https://api.exchangeratesapi.io/latest?base=\(from)&symbols=\(from),\(to)")!
let task = URLSession.shared.dataTask(with: ExchangeURL) { (data, response, error) in
guard error == nil else {
completion(.failure(error!))
return
}
let exchangeRate = getExchangeInfo(fromType: from, toType: to, fromData: data!)
completion(.success(exchangeRate))
}
task.resume()
}

static func getExchangeInfo(fromType: String, toType: String, fromData data: Foundation.Data) -> ExchangeRate {
let json = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
let rates = json["rates"] as! [String: Any]
let from = rates[fromType] as! Double
let to = rates[toType] as! Double
return ExchangeRate(from: from, to: to, fromType: fromType, toType: toType)
}
}

创建 View:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//
// ExchangeView.swift
// iWidget
//
// Created by 叶梅树 on 2020/11/3.
//

import SwiftUI

struct ExchangeView: View {
let data: ExchangeRate

var redColor = Color(UIColor(displayP3Red: 1, green: 15/255, blue: 83/255, alpha: 1))

@Environment(\.widgetFamily) var family

var body: some View {
HStack(spacing: 20) {
// 1. Dislpay data of currency
VStack(alignment: .leading) {
Text(data.fromType).bold().font(.system(size: 12)).foregroundColor(redColor)
Text(String(format: "%.2f", data.from))
.bold()
.font(.system(size: 50))
.foregroundColor(Color.black)
.shadow(color: .gray, radius: 15, x: 7, y: 7)
.minimumScaleFactor(0.5)
Text(data.toType).bold().font(.system(size: 12)).foregroundColor(redColor)
Text(String(format: "%.2f", data.to))
.bold()
.font(.system(size: 50))
.foregroundColor(Color.black)
.shadow(color: .gray, radius: 15, x: 7, y: 7)
.minimumScaleFactor(0.5)
}

// 2. Make responsive widget, by setting up sepearate view for systemMedium family widget
if family == .systemMedium {
VStack(alignment: .center) {
Text("Last Updated")
.bold()
.font(.system(size: 12))
.foregroundColor(redColor)
.shadow(color: .gray, radius: 15, x: 7, y: 7)
.minimumScaleFactor(0.5)
Text("Today")
.bold()
.font(.system(size: 40))
.foregroundColor(Color.black)
.shadow(color: .gray, radius: 15, x: 7, y: 7)
.minimumScaleFactor(0.5)
}
}
}.padding(.all, 10)
}
}

创建 Widget:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//
// ExchangeWidget.swift
// iWidget
//
// Created by 叶梅树 on 2020/11/3.
//

import WidgetKit
import SwiftUI
import Intents

struct ExchangeProvider: IntentTimelineProvider {
func placeholder(in context: Context) -> ExchangeEntry {
return ExchangeEntry(date: Date(),data: ExchangeRate())
}
func getSnapshot(for configuration: ExchangeIntent, in context: Context, completion: @escaping (Entry) -> Void) {
let entry = ExchangeEntry(date: Date(),data: ExchangeRate())
completion(entry)
}
func getTimeline(for configuration: ExchangeIntent, in context: Context, completion: @escaping (Timeline<ExchangeEntry>) -> Void) {
let currentDate = Date()
let refreshDate = Calendar.current.date(byAdding: .minute, value: 60, to: currentDate)!
//逃逸闭包传入匿名函数 当调用completion时调用该匿名函数刷新Widget
ExchangeLoader.fetch(from: "USD", to: "GBP") { result in
let data: ExchangeRate
if case .success(let exchangeData) = result {
data = exchangeData
} else {
data = ExchangeRate()
}
let entry = ExchangeEntry(date: currentDate,data: data)
let timeline = Timeline(entries: [entry], policy: .after(refreshDate))
completion(timeline)
}
}
}

struct ExchangeEntry: TimelineEntry {
public let date: Date
public let data: ExchangeRate
}

struct ExchangePlaceholderView : View {
//这里是PlaceholderView - 提醒用户选择部件功能
var body: some View {
ExchangeView(data: ExchangeRate())
}
}

struct ExchangeEntryView : View {
//这里是Widget的类型判断
@Environment(\.widgetFamily) var family: WidgetFamily
var entry: ExchangeProvider.Entry

@ViewBuilder
var body: some View {
ExchangeView(data: entry.data)
}
}

struct ExchangeWidget: Widget {
private let kind: String = "ExchangeWidget"
public var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ExchangeIntent.self, provider: ExchangeProvider()){entry in
ExchangeEntryView(entry: entry)
}
.configurationDisplayName("兑换")
.description("获取你关心的最新信息")
.supportedFamilies([.systemMedium,.systemLarge])
}
}

其中,Intent 看截图:

执行看看效果:

「systemMedium」:

IMG_3904

「systemLarge」:

IMG_3905

实际效果可以看看:

IMG_3907

IMG_3909

和今天的实际比较结果一致:

Welcome to my other publishing channels