Skip to content
This repository has been archived by the owner on Sep 6, 2018. It is now read-only.

Switch to DOM-based XML parser #18

Merged
merged 8 commits into from
May 22, 2017
Merged

Switch to DOM-based XML parser #18

merged 8 commits into from
May 22, 2017

Conversation

djbe
Copy link
Member

@djbe djbe commented Mar 26, 2017

Tried SWXMLHash, but it doesn't support XPath queries, which removes one of the main advantages of DOM-based parsing, and is needed for finding segue connections in a storyboard.

We could use the built in (NS)XMLDocument parser, but it has a higher memory footprint, and antiquated syntax. Still, if we don't want the dependency, we can always switch to the built in Foundation parser.

Fixes #17.

@@ -28,90 +28,70 @@ public final class StoryboardParser {
let customModule: String?
}

enum XMLScene {
Copy link
Member Author

@djbe djbe Apr 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only bit I'm not too happy with. I first had an XML enum, with inside it separate enums for paths, tags and attributes, but then swiftlint started complaining about nested types. ¯\_(ツ)_/¯

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, not that big a deal imho, as long as we use …Path, …Tag & …Attribute suffixes, we don't have that many constants here to justify having structured constants at all cost, that's ok

@djbe djbe force-pushed the feature/new-xml-parser branch 2 times, most recently from c21d73d to 22fa6b3 Compare May 7, 2017 00:08
@djbe djbe added this to the 1.1.0 milestone May 7, 2017
@djbe djbe modified the milestones: 2.0.0, 1.1.0 May 8, 2017
@AliSoftware AliSoftware mentioned this pull request May 10, 2017
@djbe djbe force-pushed the feature/new-xml-parser branch 2 times, most recently from 4e67c50 to 9929356 Compare May 10, 2017 15:59
@djbe djbe requested a review from AliSoftware May 17, 2017 15:02
Copy link
Contributor

@AliSoftware AliSoftware left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly OK, just a couple of questions/remarks overall but otherwise LGTM

throw ColorsParserError.invalidFile(reason: reason)
for color in document.xpath(XML.colorPath) {
let value = color.stringValue
guard let name = color["name"], !name.isEmpty else { continue }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we throw an error if the name attribute is missing or empty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably should. My initial try for this was to keep functionality as it is, but we might as wel fix some stuff while we're at it.

scenes.insert(Scene(storyboardID: id,
tag: scene.tag ?? "",
customClass: customClass,
customModule: customModule))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we didn't consider that case in the old code either, but what if (for some reason or maybe a malformed Storyboard) we have a scene which have no storyboardIdentifier attribute, is it normal that we skip it? It shouldn't happen in normal cases I think, but given how Xcode generate some strange stuff with storyboards sometimes (see the issue with customModule in the other PR), maybe consider it? I don't know what would be best in that case though (ignoring it or generating a constant with an empty storyboardID?) or if that really matters or if I'm just nitpicking as often 😉

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scenes can have no storyboardIdentifier, in that case they're anonymous scenes. Usually such scenes are connected via segues to other scenes. Anonymous scenes cannot be directly instantiated from code, exactly because they don't have an identifier.

In all my projects, most of my scenes don't have an identifier exactly because they're all connected via segues. Only some of them have an identifier, and those correctly appear in the swiftgen generated file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right makes sense 👍

@@ -28,90 +28,70 @@ public final class StoryboardParser {
let customModule: String?
}

enum XMLScene {
static let path = "/document/scenes/scene/objects/*[@sceneMemberID=\"viewController\"]"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd maybe rather name that constant sceneXPath rather than just path?

Also, using @sceneMemberID is a new logic, isn't it? We didn't use the sceneMemberID to identify scenes in the previous SAX version of our parsing did we? Maybe that's all right but just want to make sure we're not over-restricting there in case there's some use case and strange storyboards out there which don't have that attribute on scenes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that it's new logic, before we were just taking the first item in the objects array. But that actually lead to issue #17, where sometimes viewController isn't the first item in the array. Instead we now search for the actual viewcontroller in that logic, thus the @sceneMemberID.

@@ -28,90 +28,70 @@ public final class StoryboardParser {
let customModule: String?
}

enum XMLScene {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, not that big a deal imho, as long as we use …Path, …Tag & …Attribute suffixes, we don't have that many constants here to justify having structured constants at all cost, that's ok

let value = color.stringValue
guard let name = color["name"], !name.isEmpty else { continue }
guard let name = color["name"], !name.isEmpty else {
throw ColorsParserError.invalidFile(reason: "Invalid structure, color must have a name.")
Copy link
Contributor

@AliSoftware AliSoftware May 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"… color \(value) must have a name …" to help identify in the error message which line on the XML we're taking about?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to add the xml line number? I don't think we have access to that.

Edit: Nvm, you want to display the parsed value, gotcha.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah just inserting the value in the error message 😉 (sorry it wasn't super clear ^^)

scenes.insert(Scene(storyboardID: id,
tag: scene.tag ?? "",
customClass: customClass,
customModule: customModule))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right makes sense 👍

@djbe djbe merged commit 763a82b into master May 22, 2017
@djbe djbe deleted the feature/new-xml-parser branch May 22, 2017 00:04
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants