开发工具代码生成【免费下载链接】SwiftGenThe Swift code generator for your assets, storyboards, Localizable.strings, … — Get rid of all String-based APIs!项目地址https://gitcode.com/gh_mirrors/sw/SwiftGen点击查看免费下载本文是 SwiftGen 内置 Core Data 模板coredata/swift5.stencil的完整技术指南。它面向需要在 Swift 5 项目中消除手写NSManagedObject子类、字符串化 KVC 属性与手写NSFetchRequest的开发者讲解该模板的适用条件、配置方式、三个自定义参数extraImports、generateObjcName、publicAccess、基于 Data Model UserInfo 的RawType/unwrapOptional/nonOptionalInit高级定制以及生成代码的逐段语义。读完本文你将能够用一份swiftgen.yml配置把.xcdatamodeld一键转换为类型安全、可直接编译的 Core Data 模型代码。模板基本信息项目内容模板文件coredata/swift5.stencil仓库路径Sources/SwiftGenCLI/templates/coredata/swift5.stencil目标语言Swift 5作者Grant J. Butler配置示例coredata: inputs: path/to/model.xcdatamodeld; outputs: templateName: swift5; output: CoreData.swift模板对应的解析器是 SwiftGenKit 的 Core Data 解析器入口实现位于 CoreDataParser.swift。它接受.xcdatamodeld模型包或.xcdatamodel单版本模型文件当输入是.xcdatamodeld时解析器会先读取包内的.xccurrentversion文件通过_XCCurrentVersionName键定位当前激活的模型版本再解析其contentsXML仅支持 Xcode 4.0 格式的模型文件。什么时候使用这个模板需要为 Swift 5 项目生成 Core Data 模型代码时使用本模板。它是coredata/swift4.stencil的 Swift 5 版本二者生成思路一致但 Swift 5 版本在取请求fetch requestAPI 上更贴近 Swift 5 时代的新写法下文“生成代码解析”会详细对比。一个重要的前提该模板只会为 Codegen 属性设置为Manual/None的实体生成代码。这是为了避免与 Xcode 冲突——如果实体选择的是 Class Definition 或 Category/ExtensionXcode 会自动生成代码SwiftGen 再生成一遍就会重复。这一行为在源码中有明确对应解析器读取实体 XML 的codeGenerationType属性只有它不等于class或category时才标记shouldGenerateCode true见 Entity.swift。模板则据此输出// Note: AutoClassGen has codegen enabled for Xcode, skipping code generation.该输出可参考完整生成样例 defaults.swift。也就是说在 Xcode 的 Data Model 编辑器中把希望由 SwiftGen 接管代码生成的实体 Codegen 选为 Manual/None其余实体保留 Xcode 自动生成即可。在配置文件中启用模板模板通过 SwiftGen 的配置文件Documentation/ConfigFile.md引用。最简配置如下coredata: inputs: path/to/model.xcdatamodeld outputs: templateName: swift5 output: CoreData.swiftinputs指向.xcdatamodeld或.xcdatamodel文件解析器的默认过滤器只接收这两种扩展名见 Documentation/Parsers/coredata.md。outputs.templateName指定使用内置模板swift5等价写法是使用templatePath指向自定义 stencil 文件二者只能选其一。outputs.output是生成文件的输出路径。outputs.params用于向模板传入自定义参数见下一节。自定义参数Template Parameters在调用swiftgen时可通过params覆盖以下参数。这三个参数并非强制项未设置时模板按默认行为生成。参数名默认值说明extraImports无额外导入的模块列表。当实体属性使用了外部模块中的类型如Transformables模块里的自定义类型时用它补充import语句。generateObjcName无设置后生成的类型会带上objc(...)注解注解内容为类名便于 Objective-C 运行时识别。publicAccess无设置后生成的类型标记为public否则声明为internal。配置文件中的写法示例coredata: inputs: path/to/model.xcdatamodeld outputs: templateName: swift5 output: CoreData.swift params: publicAccess: true generateObjcName: true extraImports: - ExtraModule - Transformables参数在模板中的实现查看 swift5.stencil 源码可以确认每个参数的实际作用点extraImports模板在默认import CoreData/import Foundation之后循环输出额外 import第 8-15 行。它同时兼容数组与单个字符串两种传法。测试样例 defaults-extraImports.swift 展示了传入extraImportsExtraModule, Transformables后生成的import ExtraModule与import Transformables。generateObjcName在类声明前插入objc({{ entityClassName }})第 35-37 行。样例见 defaults-generateObjcName.swift。publicAccess模板先把访问级别存入accessModifier变量第 19 行类、属性、方法、关系扩展均使用该变量一处设置全局生效。NSManaged属性与关系的方法签名在 Xcode 中需要 ObjC 可见因此模板对关系扩展统一使用public修饰见下文“生成代码解析”。这些参数都有对应的模板测试覆盖例如 Tests/TemplatesTests/CoreDataTests.swift 分别用parameters: [generateObjcName]、parameters: [publicAccess]、parameters: [extraImportsExtraModule, extraImportsTransformables]生成了后缀为-generateObjcName、-publicAccess、-extraImports的对照样例。UserInfo Keys在 Data Model 中声明的高级定制除了 CLI 参数模板还会读取你在 Data Model 编辑器里为实体、属性、关系、fetched property 配置的UserInfo即模型 XML 中userInfo下的键值对解析器将其原样暴露给模板见 CoreDataParserContext.swift 中的userInfo字段。作用范围UserInfo Key说明AttributeRawType将该属性关联到一个RawRepresentable类型例如enum Foo: String或struct Foo: OptionSet。该类型需由你的代码声明SwiftGen 不会生成它或来自外部模块此时配合extraImports导入。该类型的RawValue必须与 Data Model 中属性的存储类型一致。AttributeunwrapOptional与RawType配合使用。设置后生成代码不再把该属性当作可选类型而是在类型转换失败时抛出fatalError。AttributenonOptionalInit与RawType配合使用。适用于RawRepresentable类型拥有非可选init(rawValue:)的情形例如OptionSet让生成代码直接调用该初始化器而不做失败处理。模板如何消费这三个 Key在 swift5.stencil 的RawType分支中存在RawType的属性会被生成为计算属性读写走willAccessValue/didAccessValue与primitiveValue/setPrimitiveValue的 KVC 通道并把存储值先转成RawType.RawValue再构造RawType。设置unwrapOptional后getter 在 RawValue 转换失败或RawType(rawValue:)返回 nil 时执行fatalError(Could not convert value for key ...)未设置时转换失败返回nil。设置nonOptionalInit或未设置unwrapOptional时直接return RawType(rawValue: value)当unwrapOptional为 true 且未设置nonOptionalInit时才走guard let result ... else fatalError的双重校验路径。在完整生成样例 defaults.swift 中可以同时看到三种形态IntegerEnum使用了RawTypeunwrapOptional第 129-150 行含双重fatalError、IntegerOptionSet使用了RawTypenonOptionalInit第 151-169 行直接返回IntegerOptionSet(rawValue: value)、StringEnum仅使用RawType第 238-256 行可选类型失败返回 nil。这三个用例恰好验证了上表的三种组合行为。生成代码解析下面是模板生成的核心代码节选节选自完整样例 defaults.swiftinternal class MainEntity: NSManagedObject { internal class var entityName: String { return MainEntity } internal class func entity(in managedObjectContext: NSManagedObjectContext) - NSEntityDescription? { return NSEntityDescription.entity(forEntityName: entityName, in: managedObjectContext) } nonobjc internal class func makeFetchRequest() - NSFetchRequestMainEntity { return NSFetchRequestMainEntity(entityName: entityName) } NSManaged internal var attributedString: NSAttributedString? NSManaged internal var binaryData: Data? NSManaged internal var boolean: Bool NSManaged internal var date: Date? NSManaged internal var float: Float NSManaged internal var int64: Int64 internal var integerEnum: IntegerEnum { get { let key integerEnum willAccessValue(forKey: key) defer { didAccessValue(forKey: key) } guard let value primitiveValue(forKey: key) as? IntegerEnum.RawValue, let result IntegerEnum(rawValue: value) else { fatalError(Could not convert value for key \(key) to type IntegerEnum) } return result } set { let key integerEnum willChangeValue(forKey: key) defer { didChangeValue(forKey: key) } setPrimitiveValue(newValue.rawValue, forKey: key) } } NSManaged internal var manyToMany: SetSecondaryEntity } // MARK: Relationship ManyToMany extension MainEntity { objc(addManyToManyObject:) NSManaged public func addToManyToMany(_ value: SecondaryEntity) objc(removeManyToManyObject:) NSManaged public func removeFromManyToMany(_ value: SecondaryEntity) objc(addManyToMany:) NSManaged public func addToManyToMany(_ values: SetSecondaryEntity) objc(removeManyToMany:) NSManaged public func removeFromManyToMany(_ values: SetSecondaryEntity) }各段代码的作用与生成逻辑entityName与entity(in:)每个实体类都带有类属性entityName返回 Data Model 中的实体名注意与类名可能不同和entity(in:)便捷方法。若实体存在父实体superEntity这两者会带上override修饰若实体是抽象实体或拥有子实体则不会加final模板第 38 行final仅当实体非抽象且没有子实体时出现。ChildEntity与CheckedNewEntity的继承版本可见 defaults.swift。取请求工厂方法Swift 5 版本同时生成两个方法——带available(*, deprecated, renamed: makeFetchRequest)的旧fetchRequest()与推荐使用的makeFetchRequest()。前者是NSManagedObject中已有方法的重载容易产生歧义因此模板将其标记为废弃模板第 48-55 行这与swift4版本只生成makeFetchRequest()的做法不同是 Swift 5 模板的主要差异之一。普通属性使用NSManaged声明。类型由 Data Model 的属性类型、usesScalarValueType与customClassName共同决定布尔/整数/浮点类属性在勾选标量类型后生成Bool/Int16/Int32/Int64/Float/Double否则为NSNumberBinary生成Data、Date生成Date、URI生成URL、UUID生成UUID、Transformable使用customClassName否则回退AnyObject映射逻辑见 Attribute.swift。可选标量属性即使 Data Model 中布尔/整数/浮点属性勾选了标量类型且为可选模板也不会直接写成Bool?加NSManaged而是生成带primitiveValue(forKey:)的 KVC 计算属性以保证可选语义正确模板第 90-108 行样例中的optionalBoolean、optionalInt16、optionalDouble等即此形态。派生属性Derived只生成 getter不生成 setter非可选派生属性在取不到值时触发fatalError模板第 109-123 行样例中的now、derivedCount。关系Relationship对多关系生成Set目标实体可选关系带?有序对多关系生成NSOrderedSet对一关系生成目标实体类型。对于所有对多关系模板额外生成一个extension提供带objcselector 的增删方法addTo.../removeFrom...、批量版本、有序关系还有insertInto.../removeFrom...(at:)与replace...系列这些方法统一标记为public因为 Core Data 的 KVC 机制要求 ObjC 可见模板第 141-181 行。Fetched Property 与 Fetch Request 模板实体的 fetched property 生成NSManaged var xxx: [目标实体]Data Model 中定义的 fetch request 模板会生成对应的类方法方法名由模板名首字母大写而来如fetchObjectFetchRequest并按谓词中的$变量解析出替换变量substitution variables作为方法参数解析逻辑见 CoreDataParserContext.swift。方法内部通过managedObjectModel.fetchRequestFromTemplate(withName:substitutionVariables:)执行并根据resultTypeObject / Object ID / Dictionary把返回类型分别映射为[实体类]、[NSManagedObjectID]、[[String: Any]]。完整形态见 defaults.swift。完整的生成结果可直接查看 Sources/TestUtils/Fixtures/Generated/CoreData/swift5/defaults.swift它同时展示了抽象实体、继承、跳过的实体codegen 开启、非法类型名跳过We.Cannot.Handle.ImpossibleType、各类标量/可选/派生属性、有序与无序关系、fetched property 与三种 resultType 的 fetch request。使用示例生成完成后业务代码可以这样使用与模板文档中的示例一致// Fetch all the instances of MainEntity let request MainEntity.makeFetchRequest() let mainItems try myContext.execute(request) // Type-safe relationships: relatedItem will be a SecondaryEntity? in this case let relatedItem myMainItem.manyToMany.firstmakeFetchRequest()返回强类型的NSFetchRequestMainEntity不再需要字符串形式的实体名。关系属性是强类型集合manyToMany是SetSecondaryEntity.first直接得到SecondaryEntity?消除了Any/NSObject转换。相关资源模板源码Sources/SwiftGenCLI/templates/coredata/swift5.stencil模板文档swift4 版本用于对比差异Documentation/templates/coredata/swift4.md解析器实现Sources/SwiftGenKit/Parsers/CoreData/CoreDataParser.swift 及 Entity.swift、Attribute.swiftStencil 上下文结构自定义模板时参考Documentation/SwiftGenKit Contexts/coredata.md对应实现 CoreDataParserContext.swift解析器说明输入过滤、选项Documentation/Parsers/coredata.md完整生成样例与参数变体Sources/TestUtils/Fixtures/Generated/CoreData/swift5/下的defaults.swift、defaults-extraImports.swift、defaults-generateObjcName.swift、defaults-publicAccess.swift、defaults-importCoreLocation.swift等模板测试Tests/TemplatesTests/CoreDataTests.swift赞分享开发工具代码生成【免费下载链接】SwiftGenThe Swift code generator for your assets, storyboards, Localizable.strings, … — Get rid of all String-based APIs!项目地址https://gitcode.com/gh_mirrors/sw/SwiftGen点击查看免费下载相关推荐SwiftGen与Core Data类型安全数据模型生成实践SwiftGen与Core Data类型安全数据模型生成实践 你是否还在为Core Data开发中的字符串API错误而烦恼是否经历过因实体名称拼写错误导致的开发工具代码生成SwiftGen colors/literals-swift5 模板实战指南用 colorLiteral 生成 Swift 5 类型安全颜色常量SwiftGen colors/literals swift5 模板实战指南用 colorLiteral 生成 Swift 5 类型安全颜色常量 本指南以 S开发工具代码生成Tania事件溯源架构解析理解农场数据管理的底层原理Tania事件溯源架构解析理解农场数据管理的底层原理 你是否曾想过一个农场管理软件如何确保每棵作物的生长记录、每次灌溉的细节、每个任务的变更历史都永不丢失后端前端智慧农业上一篇Nitro HTTP/2支持提升Web性能的新一代协议下一篇C STL容器比较终极指南掌握相等性与有序比较的10个关键技巧创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考