-
Notifications
You must be signed in to change notification settings - Fork 2
8.gyro
- Go to gyro's GitHub to read the README
- Install it with
gem install gyro. - Note the available built-in templates in the README. Try the CLI:
$ gyro --help
$ gyro --list swift3- Download one of the
xcdatamodelused by gyro's unit tests (in the repo's specs/fixtures/xcdatamodel folder) as a starting point. We'll start with an existing model first to understand the command line and the output.- I suggest you the
global.xcdatamodelone which is the more complete. - Download the repo's zip or clone it and copy the
spec/fixtures/xcdatamodel/global.xcdatamodelintoCodeGenDemo/Resourcesfor example:cp -R ~/Downloads/gyro-master/spec/fixtures/xcdatamodel/global.xcdatamodel CodeGenDemo/Resources - Open the
xcdatamodelin Xcode to look at its content and see the entities we designed there
- I suggest you the
- Try to use
gyroon thisxcdatamodelwith one of the provided template.
💡 You should create the output directory manually first, in the Finder or withmkdir).
$ mkdir CodeGenDemo/CodeGen/Models
$ gyro --template swift3 --model CodeGenDemo/Resources/global.xcdatamodel --output CodeGenDemo/CodeGen/Models- Open the generated files in Xcode or your Text Editor to see what was generated. Especially
Animal.swiftfeatures a lot of different use cases we can encounter in a Realm.Object subclass: Attributes, Relationships (List<Pedigree>), optionalString(via theString?type), optionalInt32(via theRealmOptional<Int32>type), …
-
Now that we've seen how that works, delete
global.xcdatamodeland all the generated files inCodeGenDemo/CodeGen/Models, then go to Xcode and create our own new, empty Data Model file (let's call itModel.xcdatamodeld) inCodeGenDemo/Resources -
Select the
Model.xcdatamodeldfile you just added in Xcode from the Project Navigator on the left, then go on the Utilities pane on the right, and in the "File Inspector" pane (Cmd-Alt-1), be sure to uncheck theModel.xcdatamodeldfrom theCodeGenDemotarget, as we'll use that xcdatamodel only for visual model edition but we don't want it to be included in the app and final.ipaas we're not using it for CoreData- In fact you could totally remove the
Model.xcdatamodeldfrom your Xcode project (just keep it in the Finder but not in the project) as it will only be used as an input forgyro. I still like to keep it in the Xcode project though (as long as it's not in any target) for easy access and edition, but it's really up to you.
- In fact you could totally remove the
-
Add a new entities called
Person,AddressandPhoneand add the proper attributes and relationships to it (to match the ones we already use in ourCodeGenDemo/Sources/Models)- Especially we'll have a 1-to-1 relationship from
PersontoAddress, and a 1-to-many relationship fromPersontoPhone - For the
modelproperty of thePhone, being of typeenum PhoneModel: Stringin our case, let's just use anStringin thexcdatamodel - Don't forget to uncheck the "Optional" box for all the attributes, as in our current demo app none of our model have any optional property (we only use non-optional
StringandPhoneModel, so…)
- Especially we'll have a 1-to-1 relationship from
- Run
gyroon our new data model
💡 Be sure to point to the
Model.xcdatamodelfile which is inside theModel.xcdatamodeld.gyroexpect you to point to anxcdatamodel, not axcdatamodeldbundle.
$ gyro --template swift3 --model CodeGenDemo/Resources/Model.xcdatamodeld/Model.xcdatamodel --output CodeGenDemo/CodeGen/Models-
Look at the generated code. We're missing something in
Phone.swift:modelis aString, but we want it to be exposed as an enum. Let's fix that -
Go back to gyro's README. You can see it's explained that we can customize our data model a bit more using "User Info" keys in our
xcdatamodel. Follow the link in the README to go to the documentation for those keys in USER-INFO.md, then jump to the "Handling enum" section. You'll see that we'll have to add 2 "User Info" keys to themodelattribute of ourPhoneentity, one to give the name of the enum, the other to give it the values. -
Select the
modelattribute of yourPhoneentity -
Go to the "Core Data Inspector" pane on the right, and in the "User Info" section, add a new
enumNamekey with valuePhoneModel -
You'll also have to add an
enumValueskey as well, and as a value, paste all the phone models as a comma-separated list.- I suggest you copy-paste the content between the curly braces of our
CodeGenDemo/Sources/PhoneModel.swiftto have all thecase …, paste it in your favorite text editor, then remove thecasekeyword & the spaces, and replace CRLF with commas, so you have that one-line comma-separated list of models ready.
- I suggest you copy-paste the content between the curly braces of our
-
Run
gyroagain to re-generate the model files with those changes- look at the new code for the generated
CodeGenDemo/CodeGen/Models/Phone.swift. See themodelEnumgenerated variable? - observe that
gyroalso now generated aCodeGenDemo/CodeGen/Models/PhoneModel.swiftfile with ourenumand all itscases!
- look at the new code for the generated
Now we'll be able to use our new Realm models instead of the structs we manually wrote before.
- Remove the files in
CodeGenDemo/Sources/Modelfrom your Xcode project (you can even delete them from the disk) - Install Realm in your Project (for this classroom we'll follow the "Dynamic Framework" installation method)
- Add the files generated by
gyroto your Xcode project inCodeGenDemo/CodeGen/Modelgroup - Build. You'll obviously see a lot of errors, because now we're using classes instead of structs for our model types, the
Reftype (which allowed us to wrap a struct into a reference type as a cheap workaround) doesn't exist anymore, etc…
We'll fix those issues one at a time to make the code compile:
- Replace
Ref<Person>with justPerson
// PersonListViewController.swift
- private var dataSource: [Ref<Person>] = [] {
+ private var dataSource: [Person] = [] {
// PersonRecordViewController.swift
- var personRef: Ref<Person>!
- var person: Person { return personRef.object }
+ var person: Person!- Similarly, replace every usage of
personRef.objectwithpersonnow that it's directly a class - Replace the usage of the
phone.modelproperty (which is now aString) with thephone.modelEnumproperty. (Note: In future versions ofgyroit's likely that we'll do the opposite logic for enum, where the enum computed property would be namedmodeland the backing property for Realm would be namedmodelStringor similar, but that feature is not there yet) - Notice that since we removed our manually-written models and that they are now code-generated, they are no longer annotated. We'll also need to fix that:
-
AutoJSONDeserializableis not applied anymore toPersonand others, so we can't callPerson(JSONObject: json)anymore. As this type is code-generated and we can't modify manually the generated file otherwise it would be rewritten on next build, there's no easy way to add the// sourcery: AutoJSONDeserializableannotation to fix that here. This could show the limitation of using annotations like that over the other solution of using a Phantom Protocol. - However, in our case, we could use
gyroto generate the JSON parsing instead, using the JSON Mapping UserInfo keys if necessary and using one of thedecodableorobject-mappertemplate to generate the JSON parsing code from that. But is that even necessary? - Actually probably not anymore anyway, as all our JSON keys are exactly named the same as our model properties without any customisation needed, so we can actually directly use the
Person(value: json)constructor forRealm.Objectto pass the dictionary of properties/values in our case! ➡️ So in the end, just replacePerson(JSONObject: json)withPerson(value: json)and our demo project will work! - But for a more contrived example and cases where your JSON keys are named differently from your
Realm.Object's property names, you could totally usegyroto generate the JSON parsing code using Anviking'sDecodableorObjectMapper. This step is left as an exercise for the user 😜
-
- Also
AutoCasesandAutoStringPropertiesannotations/conformances are not there anymore (because now our code-generated model happen not to conform to those phantom protocols at the declaration site), we need to re-annotate the types by making them to conform to those phantom protocols again.- To do that, we could totally modify the gyro templates to add conformances to those phantom protocols in the template, but is that really worth it in that specific case?
- As Swift allows us to conform to a protocol using an
extension, it will be way simpler to do it that way! - ➡️ go to
SourceryProtocols.swiftand add the following lines at the end of the file:
extension PhoneModel: AutoCases {}
extension Person: AutoStringProperties {}
extension Address: AutoStringProperties {}- Build and run the project, all our compiler errors should be fixed now and the project be working again!
💡 As those "fix compiler error" steps are less detailed as the previous steps in this walkthru, don't hesitate to look at the diff made by commit "Step 8.4" to understand exactly what to do to fix the build. Or you can even jump directly to that "Step 8.4" commit to have the fixes already applied and a buildable project again.
The rest is left as an exercice of the user:
- you can now use your
Realm.Object, create aRealm, modify your model objects and save them to yourRealm… whatever you'd do with objects in a project using Realm - you can also stop reading the
data.jsonfile at the beginning ofPersonListViewController(or at least only if yourRealm()is empty to populate some initial values), and instead read your dataSource fromRealm, so that modified data are persistent across launches - etc… pick whatever you can do now that you have Realm in this project with auto-generated models, it's up to you (and if you don't know Realm yet, it's time to switch to a tutorial about Realm now 😉 )