Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Doctor Q

Administrator
Original poster
Staff member
Sep 19, 2002
39,844
7,681
Los Angeles
When I display an attributedString value using markdown, in a UITextView of my iOS app, the linebreaks are being ignored.

Swift:
do {
    let attributedString = try AttributedString(markdown: "**Heading**\n\nBody")
    uiTextView.attributedText = NSAttributedString(attributedString)
}

The display I get is

HeadingBody​

I've tried different linebreak syntax in the markdown text, \n\n, \r\n\r\n, etc., but I can't find a way to produce linebreaks in the display.

Is the problem with the syntax in my string constant? Is the problem occurring when the AttributedString is converted to an NSAttributedString? What's the solution?
 

chown33

Moderator
Staff member
Aug 9, 2009
10,766
8,466
A sea of green
I did a search and this tutorial came up in the top results:

"Kodeco is the new raywenderlich.com", so it's probably worth looking at their examples to see if you get the same results they show.

Which Xcode and iOS versions?


As a random suggestion, try putting some spaces in your string constant, e.g.:
"**Heading** \n \n Body"

Or don't start with a markdown marking, start with plain text.

Or try it with a single newline instead of 2.


The Kodeco tutorial discusses Runs as parts of an AttributedString. You could get that info, then dump it in a diagnostic format (e.g. how many runs, how long each is) and see if it's legit. If so, then the problem is more likely in the conversion to NSAttributedString. Some of this is shown in the tutorial, so see that for details.
 

Doctor Q

Administrator
Original poster
Staff member
Sep 19, 2002
39,844
7,681
Los Angeles
Thanks, chown33. I'm using the very latest Xcode and iOS. It just so happens that I already read the Kodeco page and already tried two or three linebreaks and/or using a pair of trailing spaces.

A single linebreak in markdown isn't supposed to create a paragraph break, but I had tried that too, to no avail.

I hadn't thought of starting with plain text or putting a space between the linebreaks. Result: The same problem.

I will look for clues based on the individual runs, in case that turns up a clue. But I suspect that the conversion to an NSAttributedString is eliding the linebreaks.

Unless I find a solution, I suspect that I'll have to parse the input into separate lines myself, pass each line through the markdown parser, and then append the results for the conversion to an NSAttributedString. It just seems that this shouldn't be necessary.
 

chown33

Moderator
Staff member
Aug 9, 2009
10,766
8,466
A sea of green
What happens if you do the simplest thing possible:
Plain text\nwith linebreaks\nand no Markdown\nat all.

If I understand what Runs in AttributedString represent, then I think this will be a single run. If so, then the diagnostic that describes runs should say so. If it then fails the conversion to NSAttributedString, then at least you know exactly where the problem lies.

If I misunderstand Runs, then maybe each "paragraph" is a run, which would at least simplify the iteration to convert to NSAttributedString.

In any case, I think a bug report would be in order, because this definitely seems broken.

EDIT
There are examples of init'ing an AttributedString with markdown in Apple's reference docs:

If you try that example and it fails to init, then you have a nice clean example to use in the bug report. You might also suggest that they improve their unit tests.


I thought of another test.

Two spaces at the end of a line, i.e. two spaces followed by a newline, are supposed to cause a line-break. See if that works.

Yes, two adjacent newlines should be a paragraph, but I wonder if the encoding for line-break is also broken.
 
Last edited:

Doctor Q

Administrator
Original poster
Staff member
Sep 19, 2002
39,844
7,681
Los Angeles
Linebreaks are ignored even with no markdown.

Apple's example works, but doesn't test linebreaks.

I conclude from the following experiment that the paragraph breaks are being correctly parsed. Notice that the first three runs are labeled paragraph id 1 and the next three runs are labeled paragraph id 2.

Swift:
if let attString = try? AttributedString(markdown: "See the *latest* news\n\nat *our* website.") {
    print("attString =", attString.description)
}

Output:
Code:
attString = See the  {
    NSPresentationIntent = [paragraph (id 1)]
}
latest {
    NSPresentationIntent = [paragraph (id 1)]
    NSInlinePresentationIntent = NSInlinePresentationIntent(rawValue: 1)
}
 news {
    NSPresentationIntent = [paragraph (id 1)]
}
at  {
    NSPresentationIntent = [paragraph (id 2)]
}
our {
    NSPresentationIntent = [paragraph (id 2)]
    NSInlinePresentationIntent = NSInlinePresentationIntent(rawValue: 1)
}
 website. {
    NSPresentationIntent = [paragraph (id 2)]
}


Here's the variation with two spaces to produce a single linebreak. The two spaces and \n produce NSInlinePresentationIntent(rawValue: 128), which I suspect is a value of type InlinePresentationIntent representing the "lineBreak" attribute. So again the parsing seems OK.

Swift:
if let attString = try? AttributedString(markdown: "See the *latest* news  \nat *our* website.") {
    print("attString =", attString.description)
}

Output:
Code:
attString = See the  {
    NSPresentationIntent = [paragraph (id 1)]
}
latest {
    NSInlinePresentationIntent = NSInlinePresentationIntent(rawValue: 1)
    NSPresentationIntent = [paragraph (id 1)]
}
 news {
    NSPresentationIntent = [paragraph (id 1)]
}

 {
    NSInlinePresentationIntent = NSInlinePresentationIntent(rawValue: 128)
    NSPresentationIntent = [paragraph (id 1)]
}
at  {
    NSPresentationIntent = [paragraph (id 1)]
}
our {
    NSPresentationIntent = [paragraph (id 1)]
    NSInlinePresentationIntent = NSInlinePresentationIntent(rawValue: 1)
}
 website. {
    NSPresentationIntent = [paragraph (id 1)]
}


Therefore, I think the main suspects are the conversion to NSAttributedString or the assignment to the UITextView. My next experiment might determine which.
 
Last edited:

Doctor Q

Administrator
Original poster
Staff member
Sep 19, 2002
39,844
7,681
Los Angeles
I think this experiment shows that the problem is with UITextView's support for linebreaks, not with AttributedString or NSAttributedString. This time I'm using NSAttributedString's markdown parser instead of the one for AttributedString.

The description has paragraph id 1 and paragraph id 2, so the linebreak is represented, but the resulting display is still missing the linebreak.

Swift:
do {
    let nsAttributedString = try NSAttributedString(markdown: "See the **latest** news\n\nat **our** website.")
    print("nsAttributedString description =", nsAttributedString.description)
    uiTextView.attributedText = nsAttributedString
} catch {
    uiTextView.text = "error"
}

Output:
Code:
nsAttributedString description = See the {
    NSPresentationIntent = "<NSPresentationIntent 0x6000034dc900>: Paragraph (id 1)";
}latest{
    NSInlinePresentationIntent = 2;
    NSPresentationIntent = "<NSPresentationIntent 0x6000034dc900>: Paragraph (id 1)";
} news{
    NSPresentationIntent = "<NSPresentationIntent 0x6000034dc900>: Paragraph (id 1)";
}at {
    NSPresentationIntent = "<NSPresentationIntent 0x6000034dc9c0>: Paragraph (id 2)";
}our{
    NSInlinePresentationIntent = 2;
    NSPresentationIntent = "<NSPresentationIntent 0x6000034dc9c0>: Paragraph (id 2)";
} website.{
    NSPresentationIntent = "<NSPresentationIntent 0x6000034dc9c0>: Paragraph (id 2)";
}

Display:

See the latest newsat our website.​
 

Doctor Q

Administrator
Original poster
Staff member
Sep 19, 2002
39,844
7,681
Los Angeles
Just for fun, I changed the UITextView to a UILabel.

The problem is the same, but the display is slightly different:

See the latest newsat our website.​
 

chown33

Moderator
Staff member
Aug 9, 2009
10,766
8,466
A sea of green
It definitely seems like a bug to me.

As I recall, the latest Xcode is still a beta, so maybe wait for non-beta and see if it's fixed. Unless by "latest Xcode" you meant the latest non-beta release.

If you have an older Xcode installed, maybe try it there as a test.

Either way, you've got plenty of examples for a bug report.
 

Doctor Q

Administrator
Original poster
Staff member
Sep 19, 2002
39,844
7,681
Los Angeles
I tested an ugly workaround: breaking the markdown text into paragraphs myself, handling the markdown in each paragraph separately, and then displaying the results as a vertical stack view. It works, but it's crude, and the inter-paragraph spacing isn't linked to the font height.

Then I found the real solution!

The iOS markdown paragraph handling may be broken, but there's an .inlineOnlyPreservingWhitespace option to keep the original linebreaks and blank lines:

Swift:
let attributedString = try AttributedString(
    markdown: myMarkDownText,
    options: AttributedString.MarkdownParsingOptions(
        allowsExtendedAttributes: false,
        interpretedSyntax: .inlineOnlyPreservingWhitespace,
        failurePolicy: .returnPartiallyParsedIfPossible,
        languageCode: nil
    )
)

textVIew.attributedText = NSAttributedString(attributedString)

I've tried this and it works. I could have saved a lot of time if paragraphs had been handled properly in the first place, but at least I can now move on.

Thanks for your suggestions.
 

chown33

Moderator
Staff member
Aug 9, 2009
10,766
8,466
A sea of green
I searched on the term inlineOnlyPreservingWhitespace and saw some places where it was mentioned.

Although it solves the problem, needing it in order to get correct parsing seems exactly backwards to me. You should have to ask for whitespace to NOT be preserved, rather than needing to ask FOR it to be preserved. After all, blank lines have significance in Markdown, so the default behavior should be to parse the Markdown exactly as given.

At the very least, Apple's reference docs and examples should have a much more prominent discussion of how to get Markdown to be parsed correctly.
 

Doctor Q

Administrator
Original poster
Staff member
Sep 19, 2002
39,844
7,681
Los Angeles
Apple should also support the rest of the usual markdown syntax: for headings, bullet lists, numbered lists, block quotes, etc.

Why stop at bold and italics?
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.