在Swift中获取构建日期和时间

qcuzuvrc  于 2023-08-02  发布在  Swift
关注(0)|答案(7)|浏览(150)

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

cfh9epnr

cfh9epnr1#

您可以获得构建日期和时间,而不需要返回到objective-C。构建应用程序时,放在包中的Info.plist文件始终是从项目中的那个文件创建的。因此该文件的创建日期与构建日期和时间相匹配。您始终可以读取应用包中的文件并获取其属性。所以你可以通过访问Swift的Info.plist文件属性来获取构建日期:

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()
 }

字符串
注意:这是我最初遇到这个问题时使用桥接头的帖子。从那时起,我发现了这个“更快”的解决方案,所以我想我应该分享它以供将来参考。
[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()
}

vyu0f0g1

vyu0f0g12#

您可以使用#line#column#function

  • 原始答案:*

在你的项目中创建一个新的Objective-C文件,当Xcode询问时,回答“yes”是否创建桥接头文件。
在这个新的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())

w1jd8yoj

w1jd8yoj3#

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()
}

字符串

ckx4rj1h

ckx4rj1h4#

一种防篡改、仅快速的方法:

1.向您的应用添加新的Run Script构建阶段,并确保将其设置为在Compile Sources阶段之前 * 运行。
1.在该脚本中添加以下代码:

#!/bin/bash

timestamp=$(date +%s)
echo "import Foundation;let appBuildDate: Date = Date(timeIntervalSince1970: $timestamp)" > ${PROJECT_DIR}/Path/To/Some/BuildTimestamp.swift

字符串
1.在项目中的某个路径创建文件BuildTimestamp.swift,然后确保上面脚本中的输出路径与该文件的存在位置相匹配,相对于项目的根文件夹。
1.现在,您拥有了一个全局appBuildDate,可以在项目中的任何地方使用。(在使用变量之前生成一次项目,以便脚本在指定的文件中创建该变量。)
1.可选:如果您希望在增量构建中更新日期,请确保取消选中您创建的RunScript阶段中的“basedondependencyanalysis”复选框。

优点:

1.是自动的
1.它不会受到用户更改应用程序包中各种文件的修改/创建日期的影响(macOS上的问题)。
1.它不需要来自C的旧的__TIME____DATE__
1.它已经是一个Date,可以按原样使用。

tyu7yeag

tyu7yeag5#

与以前的答案略有不同,检查可执行文件的创建日期。这似乎也适用于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()
}

字符串

smtd7mpg

smtd7mpg6#

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

  • 官方 * 构建日期由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;
}

字符串
这是您必须从项目的 Build Phases 运行的脚本,以便将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

fiei3ece

fiei3ece7#

依赖Info.plist的创建日期是行不通的。在某些情况下检索的结果可能是应用程序安装到您的计算机上的日期时间戳,这是实际发生在我身上的事情。
以下是我的两个想法:
1.请改用contentModificationDateKey。尽管如此,如果将此复制到FAT或NTFS卷,从而破坏时间戳信息,这可能是不可靠的。
1.找到一个方法来获取kseccodeinfotimestamp的CFDate值。这是不可篡改的。请参见以下示例:
(It'将返回nil,如果未被Apple Developer ID Application等签名。Ad-hoc签名也会让它抛出nil。)

// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.

import Foundation

let url = URL.init(fileURLWithPath: "/Users/shikisuen/Library/Input Methods/vChewing.app/")

func getCodeSignedDate(bundleURL: URL) -> Date? {
  var code: SecStaticCode?
  var information: CFDictionary?
  let status4Code = SecStaticCodeCreateWithPath(bundleURL as CFURL, SecCSFlags(rawValue: 0), &code)
  guard status4Code == 0, let code = code else { 
    NSLog("Error from getCodeSignedDate(): Failed from retrieving status4Code.")
    return nil
  }
  let status = SecCodeCopySigningInformation(code, SecCSFlags(rawValue: kSecCSSigningInformation), &information)
  guard status == noErr else { 
    NSLog("Error from getCodeSignedDate(): Failed from retrieving code signing intelligence.")
    return nil
  }
  guard let dictionary = information as? [String: NSObject] else { return nil }
  guard dictionary[kSecCodeInfoIdentifier as String] != nil else {
    NSLog("Error from getCodeSignedDate(): Target not signed.")
    return nil
  }
  guard let infoDate = dictionary[kSecCodeInfoTimestamp as String] as? Date else {
    NSLog("Error from getCodeSignedDate(): Target signing timestamp is missing.")
    return nil
  }
  return infoDate as Date
}

if let infoDate = getCodeSignedDate(bundleURL: url) {
  let dateFormatter = DateFormatter()
  dateFormatter.dateFormat = "yyyyMMdd.HHmm"
  dateFormatter.timeZone = .init(secondsFromGMT: +28800) ?? .current
  let strDate = dateFormatter.string(from: infoDate)
  print(strDate)
}

字符串

相关问题