美文网首页
程序调试 (六) —— 使用Build Configuratio

程序调试 (六) —— 使用Build Configuratio

作者: 刀客传奇 | 来源:发表于2021-06-09 09:53 被阅读0次

    版本记录

    版本号 时间
    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<Entry>) -> 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,感兴趣的给个赞或者关注~~~

    相关文章

      网友评论

          本文标题:程序调试 (六) —— 使用Build Configuratio

          本文链接:https://www.haomeiwen.com/subject/ykdxeltx.html