版本记录
版本号 | 时间 |
---|---|
V1.0 | 2021.06.09 星期三 |
前言
程序总会有bug,如果有好的调试技巧和方法,那么就是事半功倍,这个专题专门和大家分享下和调试相关的技巧。希望可以帮助到大家。感兴趣的可以看下面几篇文章。
1. 程序调试 (一) —— App Crash的调试和解决示例(一)
2. 程序调试 (二) —— Xcode Simulator的高级功能(一)
3. 程序调试 (三) —— Xcode Simulator的高级功能(二)
4. 程序调试 (四) —— Xcode内存管理(一)
5. 程序调试 (五) —— 使用Build Configurations 和 .xcconfig 构建你的App(一)
源码
1. Swift
首先看下工程组织结构:
下面看下源码。
1. Base.xcconfig
// https://help.apple.com/xcode/#/dev745c5c974
APP_NAME = Ninja Counter
BASE_BUNDLE_IDENTIFIER = com.raywenderlich.NinjaCounter
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER)
USER_DEFAULTS_SUITE_NAME = group.$(BASE_BUNDLE_IDENTIFIER)
ONLY_ACTIVE_ARCH[config=Debug][sdk=*][arch=*] = YES
2. Debug.xcconfig
#include "Base.xcconfig"
APP_NAME = $(inherited)Dev
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-NonProd
USER_DEFAULTS_RECORDS_KEY = HatchlingsRecords-Debug
3. Staging.xcconfig
#include "Base.xcconfig"
APP_NAME = $(inherited)QA
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-NonProd
USER_DEFAULTS_RECORDS_KEY = HatchlingsRecords-Staging`
4. Release.xcconfig
#include "Base.xcconfig"
APP_NAME = $(inherited)
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
USER_DEFAULTS_RECORDS_KEY = HatchlingsRecords
5. WidgetBase.xcconfig
#include "Base.xcconfig"
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).widget
6. WidgetDebug.xcconfig
#include "WidgetBase.xcconfig"
#include "Debug.xcconfig"
7. WidgetStaging.xcconfig
#include "WidgetBase.xcconfig"
#include "Staging.xcconfig"
8. WidgetRelease.xcconfig
#include "WidgetBase.xcconfig"
#include "Release.xcconfig"
9. NinjaCounterApp.swift
import SwiftUI
@main
struct NinjaCounterApp: App {
var body: some Scene {
WindowGroup {
ContentView(hatchlings: Hatchling.generatePreviewHatchlings())
}
}
}
10. ContentView.swift
import SwiftUI
struct ContentView: View {
@State var hatchlings: [Hatchling] = []
@State private var tag: String = ""
var body: some View {
NavigationView {
VStack {
List(hatchlings, id: \.id) { ninja in
HStack {
Text(ninja.tag)
Spacer()
Text("Hatch time")
.font(.footnote)
.foregroundColor(.green)
Text(formatDate(ninja.date))
}
}
.listStyle(InsetGroupedListStyle())
.navigationBarTitle("Ninja Counter")
.navigationBarItems(trailing:
Button("Clear") {
hatchlings = []
UserDefaultsHelper.clearRecords()
})
VStack {
Divider()
HStack {
Text("Tag:")
.padding(.leading)
.foregroundColor(Color("rw-dark"))
TextField("Leonardo", text: $tag)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.trailing)
}
.padding(.top)
Button("+ Hatchling") {
recordHatchling()
}
.padding(.bottom)
.font(.largeTitle)
}
}
}
.onAppear(perform: loadProducts)
.accentColor(Color("rw-green"))
}
func loadProducts() {
hatchlings = UserDefaultsHelper.getRecords()
}
func recordHatchling() {
var hatchling = Hatchling(tag: tag, date: Date())
if hatchling.tag.isEmpty {
let newTag = String(hatchling.id.uuidString.suffix(6))
hatchling.tag = newTag
}
hatchlings.append(hatchling)
UserDefaultsHelper.persistRecords(hatchlings)
}
func formatDate(_ date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd HH:mm:SS"
return dateFormatter.string(from: date)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(hatchlings: Hatchling.generatePreviewHatchlings())
}
}
11. Hatchling.swift
import Foundation
struct Hatchling: Codable {
var id = UUID()
var tag: String
var date: Date
static func generatePreviewHatchlings() -> [Hatchling] {
let leo = Hatchling(tag: "Leonardo", date: Date())
let don = Hatchling(tag: "Donatello", date: Date())
return [leo, don]
}
}
12. UserDefaultsHelper.swift
import Foundation
enum UserDefaultsHelper {
static private let defaults =
UserDefaults(suiteName: Config.stringValue(forKey: "USER_DEFAULTS_SUITE_NAME")) ?? .standard
static private let recordsKey = Config.stringValue(forKey: "USER_DEFAULTS_RECORDS_KEY")
static func getRecords() -> [Hatchling] {
guard
let objects = defaults.value(forKey: recordsKey) as? Data,
let hatchlings = try? JSONDecoder().decode([Hatchling].self, from: objects)
else {
return []
}
return hatchlings
}
static func persistRecords(_ array: [Hatchling]) {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(array) {
defaults.set(encoded, forKey: recordsKey)
}
}
static func clearRecords() {
defaults.removeObject(forKey: recordsKey)
}
static func getRecordsCount() -> Int {
return getRecords().count
}
}
13. Config.swift
import Foundation
enum Config {
static func stringValue(forKey key: String) -> String {
guard let value = Bundle.main.object(forInfoDictionaryKey: key) as? String else {
fatalError("Invalid value or undefined key")
}
return value
}
}
14. Widget.swift
import WidgetKit
import SwiftUI
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), count: 0, latest: "--")
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
let entry = SimpleEntry(
date: Date(),
count: UserDefaultsHelper.getRecordsCount(),
latest: UserDefaultsHelper.getRecords().last?.tag ?? "--")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
if let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate) {
let entry = SimpleEntry(
date: entryDate,
count: UserDefaultsHelper.getRecordsCount(),
latest: UserDefaultsHelper.getRecords().last?.tag ?? "--")
entries.append(entry)
}
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
var date: Date
let count: Int
let latest: String
}
struct WidgetEntryView: View {
var entry: Provider.Entry
var body: some View {
VStack {
Text("Hatchlings")
.bold()
Text("\(entry.count)")
Divider()
Text("Latest Recorded")
.bold()
Text("\(entry.latest)")
}
}
}
@main
struct CounterWidget: Widget {
let kind: String = "widget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
WidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
struct Widget_Previews: PreviewProvider {
static var previews: some View {
WidgetEntryView(entry: SimpleEntry(date: Date(), count: 4, latest: "Raphael"))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
后记
本篇主要讲述了使用
Build Configurations
和.xcconfig
构建你的App
,感兴趣的给个赞或者关注~~~