【问题标题】:Get build date and time in Swift在 Swift 中获取构建日期和时间
【发布时间】:2014-11-08 01:10:22
【问题描述】:

我在 Objective-C 中使用 __DATE____TIME__ 来获取我的应用程序的构建日期和时间。我找不到在 Swift 中获取这些信息的方法。有可能吗?

【问题讨论】:

标签: swift build-time


【解决方案1】:

您可以在不恢复到 Objective-C 的情况下获取构建日期和时间。构建应用程序时,放置在包中的 Info.plist 文件始终是从项目中的文件创建的。因此,该文件的创建日期与构建日期和时间相匹配。您始终可以读取应用程序包中的文件并获取它们的属性。因此,您可以通过访问其 Info.plist 文件属性来获取 Swift 中的构建日期:

 var buildDate:NSDate 
 {
     if let infoPath = NSBundle.mainBundle().pathForResource("Info.plist", ofType: nil),
        let infoAttr = try? NSFileManager.defaultManager().attributesOfItemAtPath(infoPath),
        let infoDate = infoAttr["NSFileCreationDate"] as? NSDate
     { return infoDate }
     return NSDate()
 }

注意:当我最初遇到此问题时,这是让我使用桥接头的帖子。从那以后我发现了这个“Swiftier”解决方案,所以我想我会分享它以供将来参考。

[EDIT] 添加了 compileDate 变量以获取最新的编译日期,即使没有进行完整构建。这仅在开发过程中有意义,因为您将不得不进行完整构建才能在应用商店上发布应用程序,但它可能仍然有一些用处。它的工作方式相同,但使用包含实际代码的捆绑文件而不是 Info.plist 文件。

var compileDate:Date
{
    let bundleName = Bundle.main.infoDictionary!["CFBundleName"] as? String ?? "Info.plist"
    if let infoPath = Bundle.main.path(forResource: bundleName, ofType: nil),
       let infoAttr = try? FileManager.default.attributesOfItem(atPath: infoPath),
       let infoDate = infoAttr[FileAttributeKey.creationDate] as? Date
    { return infoDate }
    return Date()
}

【讨论】:

  • 这与__DATE__ 给出的结果不一致,因为当您更新应用程序时,info.plist 将保持不变,而__DATE__ 在更新时得到正确更新。
  • 我推断 OP 正在寻找构建日期和时间。我不确定“当您更新应用程序时”是什么意思。您要么重新构建它,这将始终更改 info.plist 的创建日期,要么您不重新构建它并且日期不会改变(如预期的那样)。
  • 是的,我的意思是重建。问题是,如果您通过 Xcode 进行简单的重建,它将像以前一样输出相同的时间。 Going Product > Clean 将强制更新 Info.plist。你能确认一下吗?
  • 我可以确认 (xCode 8.2.1),Product > Clean ... 然后 Build 将更新 Info.plist,即使您不更改任何文件。
  • 从 AppStore 下载应用时,这是否可靠?
【解决方案2】:

您可以使用#line#column#function


原答案:

在你的项目中创建一个新的 Objective-C 文件,当 Xcode 询问时,对创建桥接头说是。

在这个新的 Objective-C 文件中,添加以下 .h 文件:

NSString *compileDate();
NSString *compileTime();

并在.m 中实现这些功能:

NSString *compileDate() {
    return [NSString stringWithUTF8String:__DATE__];
}

NSString *compileTime() {
    return [NSString stringWithUTF8String:__TIME__];
}

现在转到桥接头并导入我们创建的.h

现在回到你的任何 Swift 文件:

println(compileDate() + ", " + compileTime())

【讨论】:

  • 太棒了。谢谢 nhgrif。
  • 请注意,不幸的是,我无法在 Swift 中准确使用 __LINE____FILE__。显然,同样的方法只会返回 Objective-C 代码的行/文件。
  • 其实是有办法的。查看 Apple 的这篇文章:developer.apple.com/swift/blog/?id=15
  • 我一直在寻找这个,但不知何故我无法创建正确的头文件(我对 Objective-C 不是很熟悉)。当我创建 Objective-C 文件时,会创建桥接头,但不会创建单独的头文件。如果我创建一个单独的头文件,上面的代码对此不起作用。也许答案可以更清楚一点,并提供完整的 .h、.m 和 bridging-header 文件的示例。这太棒了!谢谢!
  • @DavidPotter 注意:这些在 Swift 3 中已更改为 #file#line#column#function
【解决方案3】:

Swift 5 版本的 Alain T 的回答:

var buildDate: Date {
    if let infoPath = Bundle.main.path(forResource: "Info", ofType: "plist"),
        let infoAttr = try? FileManager.default.attributesOfItem(atPath: infoPath),
        let infoDate = infoAttr[.modificationDate] as? Date {
        return infoDate
    }
    return Date()
}

【讨论】:

【解决方案4】:

与以前的答案略有不同,而是检查可执行文件的创建日期。这似乎也适用于 macOS(使用 Catalyst 应用程序测试)。

/// Returns the build date of the app.
public static var buildDate: Date
{
    if let executablePath = Bundle.main.executablePath,
        let attributes = try? FileManager.default.attributesOfItem(atPath: executablePath),
        let date = attributes[.creationDate] as? Date
    {
        return date
    }
    return Date()
}

【讨论】:

    【解决方案5】:

    这里所有较​​旧的答案都不好,因为它们没有提供稳定可靠的方法来获取实际构建日期。例如,在应用程序中获取文件的文件日期并不好,因为文件日期可能会更改而不会使应用程序的代码签名无效。

    官方构建日期由 Xcode 添加到应用程序的 Info.plist 中 - 这是您应该使用的日期。

    例如,使用此代码(抱歉,它在 ObjC 中,但将其转录为 Swift 应该不会那么难):

    + (NSDate *)buildDate {
        static NSDate *result = nil;
        if (result == nil) { 
            NSDictionary *infoDictionary = NSBundle.mainBundle.infoDictionary;
            NSString *s = [infoDictionary valueForKey:@"BuildDateString"];
            NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];
            NSDate *d = [formatter dateFromString:s];
            result = d;
        }
        return result;
    }
    

    这是您必须从项目的构建阶段运行的脚本,以便将BuildDateString 添加到您的Info.plist

    #!/bin/sh
    infoplist="$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH"
    builddate=`date +%Y-%m-%dT%H:%M:%S%z`
    if [[ -n "$builddate" ]]; then
        # if BuildDateString doesn't exist, add it
        /usr/libexec/PlistBuddy -c "Add :BuildDateString string $builddate" "${infoplist}"
        # and if BuildDateString already existed, update it
        /usr/libexec/PlistBuddy -c "Set :BuildDateString $builddate" "${infoplist}"
    fi
    

    【讨论】:

    • 您如何将BuildDateString 添加到 plist 中? Xcode 不会自动为我这样做,至少不会为 macOS 应用程序。
    【解决方案6】:

    防篡改方法:

    1. 为您的应用添加一个新的Run Script 构建阶段并确保它被设置为在Compile Sources 阶段之前运行

    2. 将此添加为该脚本中的代码:

    #!/bin/bash
    
    timestamp=$(date +%s)
    echo "import Foundation;let appBuildDate: Date = Date(timeIntervalSince1970: $timestamp)" > ${PROJECT_DIR}/Path/To/Some/BuildTimestamp.swift
    
    1. 在项目中的某个路径创建文件BuildTimestamp.swift,然后确保上述脚本中的输出路径与该文件存在的位置相匹配,相对于项目的根文件夹。

    2. 您现在拥有一个全局appBuildDate,可以在您项目的任何地方使用。 (在使用变量之前构建项目一次,以便脚本在您指定的文件中创建它。)

    3. 可选:如果您希望在增量构建中更新日期,请务必取消选中您创建的运行脚本阶段中的“基于依赖关系分析”复选框。

    优点:

    1. 这是自动的。

    2. 它不会受到用户更改 app bundle 中各种文件的修改/创建日期的影响(macOS 上的一个问题)。

    3. 它不需要来自 C 的旧 __TIME____DATE__

    4. 它已经是 Date 并且可以按原样使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-07
      • 1970-01-01
      • 1970-01-01
      • 2018-03-04
      • 2012-03-03
      • 2015-03-22
      • 1970-01-01
      • 2017-09-06
      相关资源
      最近更新 更多