生成Xcode templete,自动化注入工程配置
上一篇: 动态库注入及在非越狱手机使用
上一篇已经讲解了如何生成包含动态库的工程并自动注入APP安装到设备上。但是,每次新建项目,配置各种环境,稍显麻烦。
本篇主要讲解如何生成Xcode 模板,直接从模板中生成项目,效果图如下
主要包含两个template,一个是生成动态库的template,另外一个是生成app关联动态库的. NoahDevApp
创建的项目工程如下。
与手动生成的基本一致。
步骤如下
这里几个目录可以了解下
-
- Xcode iOS 项目template目录
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates
-
- Xcode自定义模板目录
~/Library/Developer/Xcode/Templates
-
- Xcode macOS项目template目录
/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates
我们之后生成的模板就放在自定义模板目录下
开始生成自定义动态库模板
1. 先看看系统模板里面怎么配置的
直接打开 Single View App.xctemplate
,发现主要是TemplateInfo.plist
这个文件
话不多说,直接复制整个 Single View App.xctemplate
到自定义目录
我们现在自定义模板目录新建一个目录,叫 Custom
,然后把 Single View App.xctemplate
放到他里面
试试Xcode新建项目,发现多了一个Custom段,底下还有Single View App
这么一个模板,这就是我们刚刚复制过来的。他与系统的Single View App
没什么区别,下面就来修改一下。
2. 修改自定义模板配置
打开TemplateInfo.plist
文件,改下 Identifiier
字段,否则系统的就被覆盖掉了
我们需要修改的字段截图如下
几个主要的字段解释:
- identifiier: 跟咱们工程的BundleIdentifier是一样的,用来标志应用的唯一性,所以不要用跟系统模板一样的,否则系统模板就找不到了。
- Ancestors: 这是父模板,系统已经存在的模板都是有继承关系的,跟类的继承类似。
- Nodes: 这是我们要引入的文件或者Group定义,比如当前项目生成的时候有AppDelegate类与ViewController类,这些可以在Nodes中引入
- Definitions: 与Nodes字段关联的字段,Nodes字段中定义的类或者Group,要在当前字段中指明path和Group,未定义Path的则不会被模板引入。未定义Group的则是默认项目所在的Group
- Targets: 与Xcode中的Target含义一致,里面可以做一些target的配置
- Project: 与Xocde中的Project含义一致,里面可以做一些工程相关的配置
- Components: 表示依赖关系,例如需要引入一个子项目
3. 生成一个动态库模板配置
我们的项目需要一个动态库的target,所以先弄一个动态库的模板,设定好需要修改的配置
1) 直接找到系统的动态库模板
2) 拷贝到自定义模板目录下
3) 生成library的初始文件,直接拿Cocoa___PACKAGENAMEASIDENTIFIER___类修改即可, 将类文件中的内容清空
4) 修改target配置,工程配置
Other_LDFLAGS: 即为Other Linker Flags字段
具体内容如下:
$(inherited) -weak_library /usr/lib/libc++.dylib -weak_library /usr/lib/libstdc++.dylib -weak_library /opt/NoahDev/Dylibs/libsubstrate.dylib -weak_library /opt/NoahDev/Dylibs/libcycript.dylib
5) 配置初始文件路径
动态库的配置采用另外一种方式,主要内容在红框中
6) 生成一个项目试试
TemplateInfo.plist的内容如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Kind</key>
<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
<key>Identifier</key>
<string>com.apple.dt.unit.osxLibrary.custom</string>
<key>Ancestors</key>
<array>
<string>com.apple.dt.unit.base</string>
<string>com.apple.dt.unit.iosBase</string>
</array>
<key>Concrete</key>
<true/>
<key>Description</key>
<string>This template builds a library configured for iOS tweak.</string>
<key>SortOrder</key>
<integer>10</integer>
<key>Targets</key>
<array>
<dict>
<key>Frameworks</key>
<array>
<string>Foundation</string>
<string>UIKit</string>
</array>
<key>TargetIdentifier</key>
<string>com.apple.dt.macosLibraryOrFrameworkTarget</string>
<key>SharedSettings</key>
<dict>
<key>ENABLE_BITCODE</key>
<string>NO</string>
<key>EXECUTABLE_PREFIX</key>
<string>lib</string>
<key>SKIP_INSTALL</key>
<string>NO</string>
</dict>
<key>BuildPhases</key>
<array>
<dict>
<key>Class</key>
<string>Headers</string>
</dict>
<dict>
<key>Class</key>
<string>Sources</string>
</dict>
<dict>
<key>Class</key>
<string>Frameworks</string>
</dict>
<dict>
<key>RunOnlyForDeploymentPostprocessing</key>
<false/>
<key>Class</key>
<string>ShellScript</string>
<key>ShellPath</key>
<string>/bin/sh</string>
<key>ShellScript</key>
<string>/opt/NoahDev/bin/dylib.sh</string>
</dict>
</array>
</dict>
</array>
<key>Options</key>
<array>
<dict>
<key>Identifier</key>
<string>libraryFramework</string>
<key>Name</key>
<string>Framework:</string>
<key>Description</key>
<string>What system framework the library will be based on</string>
<key>Type</key>
<string>popup</string>
<key>Values</key>
<array>
<string>iOSTweak</string>
</array>
<key>Default</key>
<string>iOSTweak</string>
<key>Units</key>
<dict>
<key>iOSTweak</key>
<array>
<dict>
<key>Nodes</key>
<array>
<string>___PACKAGENAMEASIDENTIFIER___.xm</string>
<string>___PACKAGENAMEASIDENTIFIER___.mm</string>
</array>
<key>Definitions</key>
<dict>
<key>___PACKAGENAMEASIDENTIFIER___.xm</key>
<dict>
<key>Path</key>
<string>iOSTweak___PACKAGENAMEASIDENTIFIER___.xm</string>
</dict>
<key>___PACKAGENAMEASIDENTIFIER___.mm</key>
<dict>
<key>Path</key>
<string>iOSTweak___PACKAGENAMEASIDENTIFIER___.mm</string>
</dict>
</dict>
</dict>
</array>
</dict>
</dict>
<dict>
<key>Identifier</key>
<string>libraryType</string>
<key>Name</key>
<string>Type:</string>
<key>Description</key>
<string>Which type of library to create</string>
<key>Type</key>
<string>popup</string>
<key>Default</key>
<string>Dynamic</string>
<key>Units</key>
<dict>
<key>Dynamic</key>
<array>
<dict>
<key>Targets</key>
<array>
<dict>
<key>ProductType</key>
<string>com.apple.product-type.library.dynamic</string>
<key>SharedSettings</key>
<dict>
<key>DYLIB_COMPATIBILITY_VERSION</key>
<string>1</string>
<key>DYLIB_CURRENT_VERSION</key>
<string>1</string>
</dict>
</dict>
</array>
</dict>
</array>
<key>Static</key>
<array>
<dict>
<key>Targets</key>
<array>
<dict>
<key>ProductType</key>
<string>com.apple.product-type.library.static</string>
</dict>
</array>
</dict>
</array>
</dict>
</dict>
</array>
<key>Project</key>
<dict>
<key>SharedSettings</key>
<dict>
<key>HEADER_SEARCH_PATHS</key>
<string>/opt/NoahDev/include/**</string>
<key>OTHER_LDFLAGS</key>
<string>$(inherited) -weak_library /usr/lib/libc++.dylib -weak_library /usr/lib/libstdc++.dylib -weak_library /opt/NoahDev/Dylibs/libsubstrate.dylib -weak_library /opt/NoahDev/Dylibs/libcycript.dylib</string>
<key>IPHONEOS_DEPLOYMENT_TARGET</key>
<string>8.0</string>
</dict>
</dict>
<key>Nodes</key>
<array/>
</dict>
</plist>
开始生成自定义App模板
1) 修改自定义模板下Single View App模板的 Identifier
2)修改Components依赖之前生成的动态库
3)修改target配置与工程配置
4)配置初始文件及Group
5)修改继承关系
6)修改storyboard使他支持 iOS8.0
7)创建APP,查看效果
TemplateInfo.plist 内容如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Platforms</key>
<array>
<string>com.apple.platform.iphoneos</string>
</array>
<key>Kind</key>
<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
<key>Identifier</key>
<string>com.apple.dt.unit.noahDevApp</string>
<key>Ancestors</key>
<array>
<string>com.apple.dt.unit.noahdev.cocoaTouchApplicationBase</string>
<string>com.apple.dt.unit.noahdev.sceneLifecycleApplication</string>
</array>
<key>Concrete</key>
<true/>
<key>Description</key>
<string>This template provides a starting point for an application that uses a single view. It provides a view controller to manage the view, and a storyboard or nib file that contains the view.</string>
<key>SortOrder</key>
<integer>1</integer>
<key>Nodes</key>
<array>
<string>ViewController.m</string>
<string>ViewController.h</string>
<string>SceneDelegate.m</string>
<string>SceneDelegate.h</string>
<string>AppDelegate.m</string>
<string>AppDelegate.h</string>
<string>TargetApp/TargetApp</string>
</array>
<key>Definitions</key>
<dict>
<key>ViewController.m</key>
<dict>
<key>Path</key>
<string>ViewController.m</string>
</dict>
<key>ViewController.h</key>
<dict>
<key>Path</key>
<string>ViewController.h</string>
</dict>
<key>SceneDelegate.m</key>
<dict>
<key>Path</key>
<string>SceneDelegate.m</string>
</dict>
<key>SceneDelegate.h</key>
<dict>
<key>Path</key>
<string>SceneDelegate.h</string>
</dict>
<key>AppDelegate.m</key>
<dict>
<key>Path</key>
<string>AppDelegate.m</string>
</dict>
<key>AppDelegate.h</key>
<dict>
<key>Path</key>
<string>AppDelegate.h</string>
</dict>
<key>TargetApp/TargetApp</key>
<dict>
<key>Group</key>
<array>
<string>TargetApp</string>
</array>
<key>Path</key>
<string>TargetApp/TargetApp</string>
</dict>
</dict>
<key>Targets</key>
<array>
<dict>
<key>SharedSettings</key>
<dict>
<key>ENABLE_BITCODE</key>
<string>NO</string>
</dict>
<key>BuildPhases</key>
<array>
<dict>
<key>RunOnlyForDeploymentPostprocessing</key>
<false/>
<key>Class</key>
<string>ShellScript</string>
<key>ShellPath</key>
<string>/bin/sh</string>
<key>ShellScript</key>
<string>/opt/NoahDev/bin/install.sh</string>
</dict>
</array>
<key>TargetIdentifier</key>
<string>com.noah.noahdev.TargetApp</string>
<key>ProductType</key>
<string>com.apple.product-type.application</string>
</dict>
</array>
<key>Project</key>
<dict>
<key>SharedSettings</key>
<dict>
<key>IPHONEOS_DEPLOYMENT_TARGET</key>
<string>8.0</string>
</dict>
</dict>
<key>Options</key>
<array>
<dict>
<key>Identifier</key>
<string>languageChoice</string>
<key>Units</key>
<dict>
<key>Objective-C</key>
<dict>
<key>Nodes</key>
<array>
<string>ViewController.h:comments</string>
<string>ViewController.h:imports:importCocoa</string>
<string>ViewController.h:interface(___FILEBASENAME___ : UIViewController)</string>
<string>ViewController.m:comments</string>
<string>ViewController.m:imports:importHeader:ViewController.h</string>
<string>ViewController.m:extension</string>
<string>ViewController.m:implementation:methods:viewDidLoad(- (void\)viewDidLoad)</string>
<string>ViewController.m:implementation:methods:viewDidLoad:super</string>
<string>Info.plist:UIMainStoryboardFile</string>
<string>Info.plist:UIApplicationSceneManifest:UISceneStoryboardFile</string>
<string>Base.lproj/Main.storyboard</string>
</array>
<key>Definitions</key>
<dict>
<key>Base.lproj/Main.storyboard</key>
<dict>
<key>Path</key>
<string>Main.storyboard</string>
<key>SortOrder</key>
<integer>98</integer>
</dict>
<key>Info.plist:UIMainStoryboardFile</key>
<string><key>UIMainStoryboardFile</key>
<string>Main</string>
</string>
</dict>
</dict>
<key>Swift</key>
<array>
<dict>
<key>RequiredOptions</key>
<dict>
<key>userInterface</key>
<string>Storyboard</string>
</dict>
<key>Nodes</key>
<array>
<string>ViewController.swift:comments</string>
<string>ViewController.swift:imports:importCocoa</string>
<string>ViewController.swift:implementation(___FILEBASENAME___: UIViewController)</string>
<string>ViewController.swift:implementation:methods:viewDidLoad(override func viewDidLoad(\))</string>
<string>ViewController.swift:implementation:methods:viewDidLoad:super</string>
<string>Info.plist:UIMainStoryboardFile</string>
<string>Info.plist:UIApplicationSceneManifest:UISceneStoryboardFile</string>
<string>Base.lproj/Main.storyboard</string>
</array>
<key>Definitions</key>
<dict>
<key>Base.lproj/Main.storyboard</key>
<dict>
<key>Path</key>
<string>Main.storyboard</string>
<key>SortOrder</key>
<integer>98</integer>
</dict>
<key>Info.plist:UIMainStoryboardFile</key>
<string><key>UIMainStoryboardFile</key>
<string>Main</string>
</string>
</dict>
</dict>
<dict>
<key>RequiredOptions</key>
<dict>
<key>userInterface</key>
<string>SwiftUI</string>
</dict>
<key>Nodes</key>
<array>
<string>ContentView.swift</string>
<string>Preview Content/Preview Assets.xcassets</string>
<string>SceneDelegate.swift:imports:importSwiftUI</string>
<string>SceneDelegate.swift:implementation:methods:sceneWillConnectToSession:body</string>
<string>SceneDelegate.swift:implementation:methods:sceneWillConnectToSession:body:windowScene</string>
</array>
<key>Definitions</key>
<dict>
<key>ContentView.swift</key>
<dict>
<key>SortOrder</key>
<integer>99</integer>
<key>Path</key>
<string>ContentView.swift</string>
</dict>
<key>Preview Content/Preview Assets.xcassets</key>
<dict>
<key>SortOrder</key>
<integer>100</integer>
<key>Path</key>
<string>Preview Assets.xcassets</string>
<key>Group</key>
<string>Preview Content</string>
</dict>
<key>SceneDelegate.swift:implementation:methods:sceneWillConnectToSession:body</key>
<string>
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
</string>
<key>SceneDelegate.swift:implementation:methods:sceneWillConnectToSession:body:windowScene</key>
<string>
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}</string>
<key>*:imports:importSwiftUI</key>
<string>import SwiftUI</string>
</dict>
<key>Targets</key>
<array>
<dict>
<key>SharedSettings</key>
<dict>
<key>ENABLE_PREVIEWS</key>
<string>YES</string>
<key>DEVELOPMENT_ASSET_PATHS</key>
<string>___PACKAGENAMEPREVIEWCONTENT:quoteIfNeeded___</string>
</dict>
</dict>
</array>
</dict>
<dict>
<key>RequiredOptions</key>
<dict>
<key>userInterface</key>
<string>SwiftUI</string>
<key>coreData</key>
<string>true</string>
</dict>
<key>Nodes</key>
<array>
<string>SceneDelegate.swift:implementation:methods:sceneWillConnectToSession:body</string>
</array>
<key>Definitions</key>
<dict>
<key>SceneDelegate.swift:implementation:methods:sceneWillConnectToSession:body</key>
<string>
// Get the managed object context from the shared persistent container.
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
// Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath.
// Add `@Environment(\.managedObjectContext)` in the views that will need the context.
let contentView = ContentView().environment(\.managedObjectContext, context)
</string>
</dict>
</dict>
</array>
</dict>
</dict>
<dict>
<key>Identifier</key>
<string>userInterface</string>
<key>Name</key>
<string>User Interface:</string>
<key>Description</key>
<string>The type of user interface.</string>
<key>Values</key>
<array>
<string>SwiftUI</string>
<string>Storyboard</string>
</array>
<key>RequiredOptionsForValues</key>
<dict>
<key>SwiftUI</key>
<dict>
<key>languageChoice</key>
<string>Swift</string>
</dict>
</dict>
<key>Default</key>
<string>SwiftUI</string>
<key>Type</key>
<string>popup</string>
</dict>
</array>
<key>Components</key>
<array>
<dict>
<key>Name</key>
<string>___PACKAGENAME___Dylib</string>
<key>Identifier</key>
<string>com.noah.noahdev.cocoaTouchLibrary</string>
<key>ProductBuildPhaseInjections</key>
<array>
<dict>
<key>TargetIdentifier</key>
<string>com.noah.noahdev.TargetApp</string>
</dict>
</array>
</dict>
</array>
</dict>
</plist>
我这里将脚本和需要依赖的动态库
libsubstrate.dyliib
放在了/opt/NoahDev/
目录下,还包含一些需要用到的头文件,工具应用等
为了方便使用,我直接写了一个Xcode工程,所有以上内容都在里面,编译该工程后自动生成对应的模板及文件夹, 下载地址