【发布时间】:2014-11-08 01:10:22
【问题描述】:
我在 Objective-C 中使用 __DATE__ 和 __TIME__ 来获取我的应用程序的构建日期和时间。我找不到在 Swift 中获取这些信息的方法。有可能吗?
【问题讨论】:
-
在 Swift 中有很多东西不能说。没什么大不了的。使用用 Objective-C 编写的辅助类...
标签: swift build-time
我在 Objective-C 中使用 __DATE__ 和 __TIME__ 来获取我的应用程序的构建日期和时间。我找不到在 Swift 中获取这些信息的方法。有可能吗?
【问题讨论】:
标签: swift build-time
您可以在不恢复到 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__ 在更新时得到正确更新。
您可以使用#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())
【讨论】:
__LINE__ 或 __FILE__。显然,同样的方法只会返回 Objective-C 代码的行/文件。
#file、#line、#column、#function。
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()
}
【讨论】:
与以前的答案略有不同,而是检查可执行文件的创建日期。这似乎也适用于 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()
}
【讨论】:
这里所有较旧的答案都不好,因为它们没有提供稳定可靠的方法来获取实际构建日期。例如,在应用程序中获取文件的文件日期并不好,因为文件日期可能会更改而不会使应用程序的代码签名无效。
官方构建日期由 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 应用程序。
为您的应用添加一个新的Run Script 构建阶段并确保它被设置为在Compile Sources 阶段之前运行。
将此添加为该脚本中的代码:
#!/bin/bash
timestamp=$(date +%s)
echo "import Foundation;let appBuildDate: Date = Date(timeIntervalSince1970: $timestamp)" > ${PROJECT_DIR}/Path/To/Some/BuildTimestamp.swift
在项目中的某个路径创建文件BuildTimestamp.swift,然后确保上述脚本中的输出路径与该文件存在的位置相匹配,相对于项目的根文件夹。
您现在拥有一个全局appBuildDate,可以在您项目的任何地方使用。 (在使用变量之前构建项目一次,以便脚本在您指定的文件中创建它。)
可选:如果您希望在增量构建中更新日期,请务必取消选中您创建的运行脚本阶段中的“基于依赖关系分析”复选框。
这是自动的。
它不会受到用户更改 app bundle 中各种文件的修改/创建日期的影响(macOS 上的一个问题)。
它不需要来自 C 的旧 __TIME__ 和 __DATE__。
它已经是 Date 并且可以按原样使用。
【讨论】: