4

我开发了一个名为 Swordy Quest 的 iOS 应用程序: https ://apps.apple.com/us/app/swordy-quest-an-rpg-adventure/id1446641513

它包含用于排行榜、成就、玩家对玩家 (PVP) 匹配和部落的 Game Center 集成。

我有一个在开发时使用的本地测试版本(带有测试 bundleID)。我也有我的游戏的生产版本,我用它来玩游戏和进步,就好像我是客户一样。但是,为了升级/实现上面的 Game Center 功能,我需要使用我的生产 bundleID 进行测试。然后,这会用我所有的测试数据覆盖我的“客户游戏”(破坏我的“自然”进度)。

所以我想知道,是否有可能拥有一个“干净”的应用程序生产版本,并且仍然有一个单独的测试版本,允许我测试 Game Center 的功能。或者有什么方法可以在 Xcode 中恢复以前的应用程序状态,这样我就可以在测试数据污染之前保存我的生产干净版本?我知道在 Mac 应用程序中您可以更改自定义工作目录,但我认为您不能在 iOS 中进行更改?

在进行 Game Center 升级之前,我已经考虑备份我的应用程序的生产版本,但看起来这可能是不可能的?有没有人想出一个聪明的方法来解决这个问题?

请注意,我在应用程序中存储了 CoreData 和 UserDefaults。

4

3 回答 3

2

自定义工作目录只是命令行工具项目。ChangeCurrentDirectoryPath 选项在此位置不再可用,如下面的 XCode 4.6.1 中的屏幕截图所示。听起来很疯狂,但您可以尝试降级到 Xcode 4 并实现它。

在此处输入图像描述


或者您将需要使用 Cocoa 的 NSBundle 类或 Core Foundation 的 CFBundle 函数来加载文件。因此,为您的 Swordy Quest 测试制作重复目标。它不会影响您的干净副本。

管理方案:

ss

最后单击小齿轮按钮创建一个干净的副本以避免触摸您的生产代码。

在此处输入图像描述

设置好密钥后,产品和测试在哪里

构建设置 > 打包(写入过滤器打包)

在此处输入图像描述

实现为您的逻辑函数下面的代码(例如在其中实现一个从 LoginPlayerVC 触发 GameHomeVC 的函数)

    var key: String?
    #if TARGET_PROD || TARGET_STORE
    key = @"prodKey";
    #else
    key = @"testKey";
于 2020-12-28T18:53:12.917 回答
0

Targets 就是为此而设计的。您设置预处理器宏值以使编译器根据目标/宏值编译特定代码。

在您的情况下,您根据选择的目标/宏组合更改客户游戏/测试数据文件的路径。

您还可以为每个目标设置不同的 bundleID。

一旦完成所有设置,您只需在目标和编译之间切换。整个事情应该无缝地工作。

备份您的项目,然后按照本教程进行操作,该教程详细介绍了如何执行此操作: https ://www.appcoda.com/using-xcode-targets/

如果以后上面的链接坏了,只需搜索“Xcode target tutorials”

于 2020-12-26T16:03:33.563 回答
0

作为先驱,我对 Game Center 并不熟悉,因此可能存在一些我没有考虑到的问题。所以,有了这个,我解决这个问题的本能是从启动参数开始的。这里有一篇关于如何做到这一点的精彩文章:https ://www.swiftbysundell.com/articles/launch-arguments-in-swift/ 。

现在您可以根据来自不同方案的启动参数开始改变行为,您可以开始研究如何分割您的测试/产品数据。

由于我不是 CoreData 专家,我不能 100% 有信心地说这是可能的(或容易),但我会研究如何根据启动参数设置单独的持久存储。使用本文-testGameCenter作为参考,在测试 Game Center 时,在为新TestGameCenter方案创建启动参数以创建内存数据存储后,您似乎可以大致执行以下操作

lazy var persistentContainer: NSPersistentContainer = {
  let container = NSPersistentContainer(name: "YourDataStore")

  if CommandLine.arguments.contains("-testGameCenter") {
    let description = NSPersistentStoreDescription()
    description.url = URL(fileURLWithPath: "/dev/null")
    container.persistentStoreDescriptions = [description]  
  }

  container.loadPersistentStores(completionHandler: { _, error in
    if let error = error as NSError? {
      fatalError("Failed to load stores: \(error), \(error.userInfo)")
    }
  })

  return container
}()

如果您能够解决上面的 CoreData 问题,那么是时候开始研究如何分割您的 UserDefaults 数据了。立即想到的这个粗略但简单的解决方案是在test从您的测试方案运行时为您的 UserDefault 键添加前缀。下面是一个示例,说明如何围绕 UserDefaults 构造一个包装器来管理它

struct UserDefaultsWrapper {
    let userDefaults: UserDefaults
    let keyPrefix: String

    init(userDefaults: UserDefaults, keyPrefix: String) {
        self.userDefaults = userDefaults
        self.keyPrefix = keyPrefix
    }

    func setValue(_ value: Any?, forKey key: String) {
        self.userDefaults.setValue(value, forKey: prefixedKey(forKey: key))
    }

    func value(forKey key: String) -> Any? {
        self.userDefaults.value(forKey: prefixedKey(forKey: key))
    }

    func prefixedKey(forKey key: String) -> String {
        return "\(keyPrefix)\(key)}"
    }
}

你可以像这样使用包装器

    let userDefaultsPrefix = CommandLine.arguments.contains("-testGameCenter") ? "testGameCenter_" : ""

    let userDefaultsWrapper = UserDefaultsWrapper(userDefaults: .standard, keyPrefix: userDefaultsPrefix)

为了获得更优雅的东西,您可以多看一下 UserDefaults,看看您是否可以应用类似于 CoreData 的解决方案,其中有两个完全独立的商店。快速浏览一下这个初始化器,也许你可以用你的包装器做一些像这样简单的事情

struct UserDefaultsWrapper {
    let userDefaults: UserDefaults

    init(userDefaults: UserDefaults) {
        self.userDefaults = userDefaults
    }

    func setValue(_ value: Any?, forKey key: String) {
        self.userDefaults.setValue(value, forKey: key)
    }

    func value(forKey key: String) -> Any? {
        self.userDefaults.value(forKey: key)
    }
}

你在哪里建造它

    let userDefaultsSuiteName: String? = CommandLine.arguments.contains("-testGameCenter") ? myTestingGameCenterSuiteName : nil

    let userDefaults = UserDefaults(suiteName: userDefaultsSuiteName)
    let userDefaultsWrapper = UserDefaultsWrapper(userDefaults: userDefaults)

最后,从您对另一个回复的评论来看,听起来您也关心全新安装场景。也就是说,我概述的方法对于在删除/安装中持久化数据没有帮助(至少我不认为)。但是,我认为您应该考虑的是是否有必要从您的生产捆绑包 ID 中测试这些删除/安装问题。您能否改为从测试包 id 手动测试这些问题和/或围绕涉及这些问题的组件编写单元测试?当你接近你的测试策略时,确保你在正确的层测试正确的东西是很重要的;在错误的层测试错误的东西会使每个测试层都更难执行

于 2020-12-30T21:12:26.757 回答