我有两个iOS项目,我想把它作为一个单独的项目,并根据条件一次运行一个项目。
例如:我有两个名为ProjectA和ProjectB的项目。我想将ProjectArojectBrojectCrojectC将有一个条件,这取决于我必须运行projectA或ProjectB代码的条件。
注意:运行项目的条件将在运行时应用,而不是在编译时应用。
我怀疑这真的可能吗?我需要如何处理这个问题的专业指导。
我建议您创建pod并调用您的项目,您可以根据场景/用例进行调用,这样您就可以在pod中保留任意数量的项目。
您可以参考此链接。
另一种方法是让你的两个项目走在同一条路上,创建动态框架,并将这些引用用于项目"C"。我指的是你为计划的新项目
任何一种方法都可以解决你的问题。
好的,根据阅读问题并提出一些问题。。。我真的不清楚为什么pods的答案得到了这么多选票。它不能解决问题。
假设我们有现有的项目A和项目B。ProjectC还不存在,但我们希望使ProjectC成为a和B的"组合",其中a或B将基于某种条件运行。一旦运行,该应用程序将一直运行该版本,直到重新启动。
两种基本方法是将所有代码和资产组合到ProjectC中,或者尝试制作您加载的A和B框架。但是,对于这两种情况,您都必须对代码库进行调整。你需要做的工作量也是项目复杂程度的副产品。
我参与过一个项目,我们成功地实现了您对大型应用程序的需求。我们通过iPhone和iPad项目,结合代码/资产,基本上制作了一个"通用应用程序"。我们在运行时"启动"了相应的版本。
在你付出这样的努力之前,你需要权衡一下后果。我会列出一些。
- 如果您的A/B依赖于bundle id,那么您将遇到问题。ProjectC将有自己的捆绑id。例如,是否有任何第三方API(如Facebook)在使用中,您希望仍然使用它们,并且看起来像ProjectA或ProjectB?如果是,但它们与捆绑id绑定,则会出现问题
- 如果您正在使用IAP,您的产品ID将需要有所不同。因此,很明显,如果这些产品ID是硬编码的,那么A和B中的代码将需要更改。如果它们是服务器驱动的,您需要确保服务器代码并实际提供正确的产品ID。如果期望ProjectA的所有者仍然拥有ProjectC的IAP,这是可能的。。。然而,成本来自于需要与您的规模相适应的服务器端逻辑来管理这一点
- 调试代码时需要付出多少努力
- 项目A和项目B仍在积极开发中吗?这样做可能会使这些项目在以后很难维护
- 为此将留出多少时间?这样做既乏味又耗时
您可能会更成功地将A和B的源/资产组合到C中。换句话说,您不会将A或B的项目文件添加到C中,为什么?因为你需要能够轻松识别所有冲突点,然后提供解决方案。冲突的一个简单例子是AppDelegate
,它是项目默认创建的。你会有三个(A、B、C)。
请记住,所有方法都充满了问题。如果你选择框架(不管它们是否是Pod),并且你决定你的资产进入框架捆绑包,你必须更改代码才能访问它们,因为它们不再在mainBundle
中。
好吧,一般的指导方针是什么?
- 选择一种方法(例如组合或框架)。我将讨论联合收割机
- 提前确定尽可能多的冲突。针对每种类型的冲突,确定您的策略。例如,要求解
AppDelegate
,您可以始终执行ProjectAAppDelegate
和ProjectBAppDelegate
- 检查
Info.plist
。这是其他冲突的一个很好的来源。C的Info.plist
将是两者的组合 - 想出一个如何处理冲突的策略。例如,我们有一个命名约定,当类名冲突时,我们会使用它
- 将您的ProjectA和ProjectC源代码/资产添加到Project C中。开始修复冲突
- 喝很多咖啡
你需要控制的另一件关键事情是基于决策的切入点。如果您可以在main.m
中调用UIApplicationMain
之前做出决定,您可以执行以下操作:
Class appDelegate;
if (runA) {
appDelegate = [ProjectAAppDelegate class];
} else {
appDelegate = [ProjectBAppDelegate class];
}
return UIApplicationMain(argc, argv, nil, NSStringFromClass(appDelegate));
如果这不起作用,那么您将不得不在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
中执行此操作。然后你可能想要让项目C的AppDelegate作为a和B的代理。例如:
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[self.projectAppDelegate applicationDidBecomeActive:application];
}
其中projectAppDelegate
设置为您需要的正确版本。
请注意,您还需要手动加载情节提要,以确保启动正确的情节提要。请记住,如果你在加载过程中花费太多时间做出决定(你说这是一个网络呼叫),你的应用程序可以启动。
如果它们是框架的话,你可能会找到一些漂亮的动态加载,但关键应该是可维护性/易于调试。
我将在这里停下来,主要是因为有太多不同的事情要做,我真的没有时间把它们都写出来。
Additional info based on follow up question
文件重复的可能性很高。我举了AppDelegate
的例子。enum
将不那么频繁,但会发生。还要记住,您关心的不是文件名,而是类或其他定义的数据类型。这些是对编译器/链接器很重要的冲突。
例如:
项目A:
typedef NS_ENUM(NSInteger, State) {}
项目B:
typedef NS_ENUM(NSInteger, State) {}
这就是我提前制定战略的意思。示例策略:
- 项目A是"赢家",因此只有项目B会有变化
- ProjectB是"赢家",因此只有ProjectA会有变化
- 项目A和项目B都将发生变化
所以对于1,结果将是
项目A:
typedef NS_ENUM(NSInteger, State) {}
项目B:
typedef NS_ENUM(NSInteger, PBState) {}
注意我听到的。为了让我的生活更轻松,我使用了一些预定义的前缀来指定它为ProjectB。在这种情况下,PB。
对于2:项目A:
typedef NS_ENUM(NSInteger, PAState) {}
项目B:
typedef NS_ENUM(NSInteger, State) {}
对于3:项目A:
typedef NS_ENUM(NSInteger, PAState) {}
项目B:
typedef NS_ENUM(NSInteger, PBState) {}
这将由你来制定规则,但要保持一致。您将需要一个如何解决所有数据类型更改的策略。例如,如果您要从State
->PBState
,那么您显然只想修改ProjectB。以下是在自己的项目中使用它会有所帮助的地方。但是,您可以使用Xcode的Search Scope
来帮助控制这种情况。
哦,还有一些其他的事情。
首先,投资一个脚本来查找ProjectA和ProjectB中的所有重复文件。基本上,您需要根据需要的扩展(例如.m、.h、.xib等)在ProjectA和ProjectB上执行find
。这将给你一份潜在候选人的名单,从中你可以制定规则。
由于我是做这部分项目的傻瓜,所以我基本上把这个列表保存在一个文本文件中。当我合并该文件时,我将其移动到文件中的另一个部分(用几行换行符分隔)。有不同的会计方法,这只是我选择的方法。
我也会确保你有一个好的diff工具,比如Araxis Merge(这就是我使用的)。
此外,经常拍摄快照以防万一。您可以使用git分支。我经常只是复制实际的目录,这样以后如果需要的话我可以对它们进行比较。