Images: dot syntax#206
Conversation
* master: key sorting whitespace - cleaned up duped pod build phases - Playground tweaks - misc style tweaks - changelog tweak - Fileprivate audit - fixed strings for strict alphabetical ordering - fixed strings test - First pass feedback - change swiftIdentifier method signature - Bump xcode version and doing quick lint to avoid travis pod repo issue : travis-ci/travis-ci#6473 - Fixed genumkit swift version - Playgrounds to swift 3 - Naming tweaks from feedback - Audit internal APIs for swift 3 compliance - Bump swift version and change log - Bump swift version and change log - Fixed font tests and made font parsing alphabetical for predictablity - fixed swift 3 index regression - fixed compiler errors - All tests passing, except font ordering issue - Swift 3 syntax to building - update pods to swit 3/latest - Ran Migrator to swift 3 # Conflicts: # GenumKit/Parsers/AssetsCatalogParser.swift # GenumKit/Stencil/Contexts.swift # SwiftGen.xcodeproj/project.pbxproj
| struct Entry { | ||
| var name: String | ||
| var value: String | ||
| var items: [Entry]? |
There was a problem hiding this comment.
If an Entry is either an image (with name and value) or a namespace (with name and sub-entries), shouldn't we use an enum with associated values for that instead?
Like either:
enum Entry {
case image(name: String, value: String)
indirect case namespace(name: String, entries: [Entry])
}Or
struct Entry {
var name: String
enum Content {
case image(value: String)
case namespace(entries: [Entry])
}
}There was a problem hiding this comment.
Haven't used enums with associated values yet (although I've read about them). I'll look into it.
There was a problem hiding this comment.
I think the first solution (a simple enum with associated values) would be easier to use than a struct with enum inside.
Using a struct with inner enum is only useful if all cases of the enum share multiple properties, like enough to want to avoid repeating each property in each enum case's associated values.
But here we only have one common property so I think it's not worth it and integrating name directly in each enum case would be easier to manipulate.
And that way you won't need dedicate inits, you can just create Entry.image(name: "foo", value: "parent/foo") and Entry.namespace(name: "parent", entries: children).
PS: Note that I'm not sure if the indirect keyword is really needed. I think so, because the case references the enum being defined, but it's worth trying without to be sure we can't avoid it.
|
|
||
| extension AssetsCatalogParser { | ||
| fileprivate func loadAssetCatalog(at path: String) -> [[String: AnyObject]]? { | ||
| fileprivate func loadAssetCatalog(at path: String) -> [[String: Any]]? { |
There was a problem hiding this comment.
It would be nice to add a doc-comment to this function to document the typical structure of the PLIST/JSON returned by actool, like the keys returned, how they look for image entries, for namespaced images, for namespaces themselves… Would help understand the rest of the code parsing it (especially parse(items😃)
(I know, I should have mentioned that in the other PR about actool migration ^^)
GenumKit/Stencil/Contexts.swift
Outdated
| ) | ||
| } | ||
|
|
||
| func justValues(entries: [Entry]) -> [String] { |
There was a problem hiding this comment.
Should probably be private (or fileprivate)?
GenumKit/Stencil/Contexts.swift
Outdated
| return result | ||
| } | ||
|
|
||
| func structure(entries: [Entry], currentLevel: Int = 0, maxLevel: Int = 5) -> [[String: Any]] { |
There was a problem hiding this comment.
Should probably be private (or fileprivate)?
GenumKit/Stencil/Contexts.swift
Outdated
| } | ||
| } | ||
|
|
||
| func flatten(entries: [Entry]) -> [[String: Any]] { |
There was a problem hiding this comment.
Should probably be private (or fileprivate)?
| } | ||
| } | ||
|
|
||
| func testFileWithDotSyntax() { |
There was a problem hiding this comment.
Seems like you don't have the same indentation settings as the ones defined in the project (2-spaces indentation)?
There was a problem hiding this comment.
I don't know, sometimes Xcode forgets them, or something else.
Personally I always use tabs so that's why it sometimes gets through, even though I try to be careful.
| if let providesNamespace = item[AssetCatalog.providesNamespace.rawValue] as? NSNumber, | ||
| providesNamespace.boolValue { | ||
| processCatalog(items: children, withPrefix: "\(prefix)\(filename)/") | ||
| providesNamespace.boolValue { |
There was a problem hiding this comment.
Doesn't Swift automatically bridge boolean NSNumber to Bool with as?? Especially since the AnyObject -> Any proposal got in?
I mean, we could probably do if let providesNamespace = item[AsstCatalog.providesNamespace.rawValue] as? Bool directly maybe? (At least it's worth a try)
There was a problem hiding this comment.
I think this was written before the swift3 merge. Don't know, but you're right, it should be bridged.
| var result = [Entry]() | ||
|
|
||
| for item in items { | ||
| guard let filename = item[AssetCatalog.filename.rawValue] as? String else { continue } |
There was a problem hiding this comment.
I totally forgot to see that in the other PR about actool migration, but if you're only using the enum AssetCatalog as a way to group constants together (and never use the cases other than via their rawValue to get the String constant), I'd suggest you to use an enum with no cases, but with static let constants namespaced in the enum instead:
private enum AssetCatalogKeys {
static let children = "children"
static let filename = "filename"
static let providesNamespace = "provides-namespace"
static let root = "com.apple.actool.catalog-contents"
}There was a problem hiding this comment.
So we'd be using the enum just as some sort of way to namespace constants?
There was a problem hiding this comment.
Yup. That's how I namespace all my constants in my projects now, and have been loving it!
Like I very often have stuff like this in my projects:
struct SomeModel {
enum JSONKeys {
static let firstname = "firstName"
static let lastname = "lastName"
static let dob = "dateOfBirth"
}
let firstname: String
let lastname: String
let dateOfBirth: String
init?(json: [String: Any]) {
guard
let fn = json[JSONKeys.firstname] as? String,
let ln = json[JSONKeys.lastname] as? String,
let dob = json[JSONKeys.dob] as? String
else { return nil }
self.firstname = fn
self.lastname = ln
self.dateOfBirth = dob
}
}
AliSoftware
left a comment
There was a problem hiding this comment.
Don't hesitate to rebase on top of master, instead of merging master into your branch.
Both have pros and cons, but merging master into your branch means that you can get quite a 🍝 git tree sometimes. And it's then harder to rebase stuff like a PR that has been opened on top of another (like 204 is on top of 201) and make the latest PR only contain the diff commits (instead it adds more 🍝 )
Sure, rebasing instead of merging also have cons, like rewriting history meaning that other people who already have pulled your commit won't have valid commits anymore, but on a feature branch used for a PR, and on which typically only one people commits onto, it seems less a problem than the git tree 🍝 brought by merging master. So sure it's a matter of preferences but I prefer clean trees personally 😉 🌲
There was a problem hiding this comment.
Doesn't PathKit's Path have a dedicated property to get the path string?
I mean, String(describing:) feels more intended for debug representation purposes to me, while something like path.path or path.string if it exists would feel more right (a bit like you'd use url.absoluteString)
There was a problem hiding this comment.
You'd think that, wouldn't you? There isn't any public one, only internal methods unfortunately.
There was a problem hiding this comment.
It does conform to CustomStringConvertible, but that's what I'm using.
There was a problem hiding this comment.
Not even path.description from some CustomStringConvertible conformance?
There was a problem hiding this comment.
Yeah, but what's better? String(describing: path) or path.description?
I'll create a PR in PathKit for something better, but I'll leave it up to you what we should use for now.
There was a problem hiding this comment.
I think path.description feels more like a property on Path while String(describing: path), although doing the same thing, feels more like a conversion or builder to me.
|
What we can do (and kylef from Commander does this), is when merging the pull request to do "squash and merge", that way you'll always have a clean tree. |
|
👍 |
AliSoftware
left a comment
There was a problem hiding this comment.
- Fix indentation
- Add CHANGELOG entry
| } | ||
|
|
||
| guard !found else { return false } | ||
| entries.append(Entry.image(name: name, value: name)) |
There was a problem hiding this comment.
indentation (from lines 17 to 26) seems wrong 😉
| <key>com.apple.actool.catalog-contents</key> | ||
| <array> | ||
| <dict> | ||
| <key>children</key> |
| static let Orange: AssetType = "Round/Orange" | ||
|
|
||
| struct Red { | ||
| static let Apple: AssetType = "Round/Apple" |
There was a problem hiding this comment.
Why isn't the value of this generated constant "Round/Red/Apple" instead of just "Round/Apple"?
Is it because the group in the asset catalogs doesn't have provides-namespace checked, so that's normal and intentional?
In that case I'm wondering if we should create the intermediate structure struct Red if it doesn't provide namespace. I'm thinking YES (and so in this case this output is 👍 ), as otherwise the developer wouldn't have created a group in its asset catalog… but just wanted to start the discussion in case anyone had a different opinion.
There was a problem hiding this comment.
This is intentional. Only namespaced folders modify the value (prefixing them with the folder's name).
I also think yes, because the structure in the catalog is there, and SwiftGen should provide an added value by offering this deeper structure. We could pass this extra structure information on to the template, although I'll have to think about how to best implement that.
|
BTW, I based the templates on the strings dot syntax. Any reason why we use nested |
|
I think it's just because I overlooked the PR implementing those templates at that time, would indeed seem better to use enums instead of structs here. |
|
Did this maybe depend on swift 3? |
|
Nah I don't think it did. enums as namespaces was already possible in Swift 2… |
|
Conflict to fix so we can merge 😉 |
|
Oh you want to do a minor release instead of patch? Sure, gimme a minute. |
|
Ah, didn't realize that this bumped to the minor version… why is that btw? |
|
New feature? (new template) |
|
I mean, before this PR, people using namespaces didn't have the dot namespace in the generated code, now they will… that means breaking, right? |
|
No the old workflow still works, it still generates a flat enum with all cases. |
|
Oh, sorry, you're right, I thought we already had a (non-working) dot-syntax template for Images, (I got confused with the one we already have for Strings). If that's a new template then yeah, minor it is 😉 In that case yeah, maybe we should do a patch version real quick first, before merging this and starting a new minor, right. |
|
Yeah, I mean, I still have to check this works from command line 😅 |
|
yup! |
|
So, I think I'll wait until 4.0.1 is released before I fix the conflict in this, otherwise I'll keep having to merge changes. |
|
Definitely! 🎉 |
Follow up of #199, implements the dot-syntax for asset catalogs (mentioned in #52).
For now only has the swift3 template, I want some input before I do the swift 2 version.