Marky Mark is a parser written in Swift that converts markdown into native views.

Related tags

Text Marky-Mark
Overview

Marky Mark

build status codecov Marky Mark is a parser written in Swift that converts markdown into native views. The way it looks it highly customizable and the supported markdown syntax is easy to extend.

Screenshot Screenshot Screenshot

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

  • iOS 8.0+
  • Xcode 8.0+

Installation

CocoaPods 1.0.0+ is required to build MarkyMark

To integrate MarkyMark into your Xcode project using CocoaPods, specify it in your Podfile:

pod "markymark"

Alternatively, add MarkyMark to your project using Swift Package Manager using:

https://github.com/M2Mobi/Marky-Mark

Simple usage

View with default styling

let markDownView = MarkDownTextView()
markDownView.text = "# Header\nParagraph"

View with modified styling

Markymark has many styling options, please check the examples in the styling section of this readme. A simple example:

let markDownView = MarkDownTextView()
markDownView.styling.headingStyling.textColorsForLevels = [
	.orange, //H1 (i.e. # Title)
	.black,  //H2, ... (i.e. ## Subtitle, ### Sub subtitle)
]

markDownView.styling.linkStyling.textColor = .blue
markDownView.styling.paragraphStyling.baseFont = .systemFont(ofSize: 14)
markDownView.text = "# Header\nParagraph"

Supported tags in the Default Flavor

Note: Different tags can be supported by either extending the ContentfulFlavor (default) or by implementing a class that comforms to Flavor and implement the required Rule's

Headings
# H1
## H2
### H3
#### H4
##### H5
###### H6

Lists
- item
	- item
* item
	* item
+ item
	+ item
a. item
b. item
1. item
2. item

Emphasis
*Em*
_Em_
**Strong**
__Strong__
~~Strike through~~

Images
![Alternative text](image.png)

Links
[Link text](https://www.example.net)

Code 
`code`
\```code```

Customizing default style

Default Styling instance

var styling = DefaultStyling()

Paragraphs (regular text)

Markdown example: Some text

styling.paragraphStyling.baseFont = .systemFont(ofSize: 14)
styling.paragraphStyling.textColor = .black
styling.paragraphStyling.contentInsets = UIEdgeInsets(top:0, left: 0, bottom: 5, right: 0)  
styling.paragraphStyling.lineHeight = 4
styling.paragraphStyling.isBold = false
styling.paragraphStyling.isItalic = false
styling.paragraphStyling.textAlignment = .left

Headings

Markdown example: # Title or ## Subtitle etc.

styling.headingStyling.fontsForLevels = [
	UIFont.boldSystemFontOfSize(24), //H1
	UIFont.systemFontOfSize(18),     //H2
	UIFont.systemFontOfSize(16)      //H3, ... (last item will be next levels as well)
]

styling.headingStyling.colorsForLevels = [
	.red, //H1
	.black, //H2, ... (last item will be next levels as well)
]

// Margins
styling.headingStyling.contentInsetsForLevels = [
	UIEdgeInsets(top: 5, left: 0, bottom: 15, right: 10), // H1
	UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 10) //H2, ... (last item will be next levels as well)
]

styling.headingStyling.isBold = false
styling.headingStyling.isItalic = false
styling.headingStyling.isUnderlined = false
styling.headingStyling.textAlignment = .left

linkStyling

Markdown Example [Google](http://www.google.com)

styling.linkStyling.textColor = .black
styling.linkStyling.baseFont = nil // Default: nil. Setting baseFont to nil will inherit font from paragraphStyling

styling.linkStyling.isBold = false
styling.linkStyling.isItalic = false
styling.linkStyling.isUnderlined = true

List styling

Markdown Example:

- List item 1
- List item 2
- Nested List item
// By default a font will be used with the bullet character `•`. Use the follow properties to configure it's size and color:
styling.listStyling.bulletFont = .systemFont(ofSize: 14)
styling.listStyling.bulletColor = .black

// Bullets can also be images for more complex styling. When setting images, bullet font and color won't be used anymore
// Array of images used as bullet for each level of nested list items
styling.listStyling.bulletImages = [
	UIImage(named: "circle"),
	UIImage(named: "emptyCircle"),
	UIImage(named: "line"),
	UIImage(named: "square")
]

// Size of the images
styling.listStyling.bulletViewSize = CGSize(width: 16, height: 16)

styling.listStyling.baseFont = .systemFont(ofSize: 14)
styling.listStyling.contentInsets = UIEdgeInsets(top: 0, left:  0, bottom: 10, right: 10)

//Amount of space underneath each list item
styling.listStyling.bottomListItemSpacing = 5

// Number of pixels to indent for each nested list level
styling.listStyling.listIdentSpace = 15

styling.listStyling.textColor = .black

Styling is also possible for:

styling.paragraphStyling
styling.italicStyling
styling.boldStyling
styling.strikeThroughStyling
styling.imageStyling
styling.linkStyling
styling.horizontalLineStyling
styling.codeBlockStyling
styling.inlineCodeBlockStyling
styling.quoteStyling

Please check the DefaultStyling class for more information

Advanced usage

Advanced usage is only needed for very specific cases. Making subsets of styling, making different styling combinations, supporting different Markdown rules (syntax) or modifying certain views after that have been generated.

Custom styling objects

struct CustomMarkyMarkStyling: Styling {
	var headerStyling = CustomHeaderStyling()
	var paragraphStyling = ParagraphStyling()
	var linkStyling = ListStyling()

	var itemStylingRules: [ItemStyling] {
		return [headerStyling, paragraphStyling, linkStyling]  
	}
}

You can implement CustomHeaderStyling by checking how other Styling objects have been implemented, like ``HeaderStyling. Make sure your CustomHeaderStyling` comforms to all styling rules you'd like your custom styling to support. i.e. comform to `TextColorStylingRule` to support textStyle of your custom styling.

Each styling rule can be applied to a markDownItem by comforming to ItemStyling and implement the required method like this:

public func isApplicableOn(_ markDownItem: MarkDownItem) -> Bool {
	return markDownItem is HeaderMarkDownItem
}

This will let the mechanism know it should apply your styling to a HeaderMarkDownItem

You can inject your new styling object by passing it to the constructor of the MarkdownTextView

MarkDownTextView(styling: CustomMarkyMarkStyling())

Adding your own rules

Adding a new rule requires three new classes of based on the following protocol:

  • Rule that can recoginizes the desired markdown syntax
  • MarkDownItem for your new element that will be created by your new rule
  • LayoutBlockBuilder that can convert your MarkDownItem to layout

Add the rule to MarkyMark

markyMark.addRule(MyCustomRule())

Or when using the MarkdownTextView:

markdownTextView.add(rule: MyCustomRule())

Add the block builder to your layout converter

converter.addLayoutBlockBuilder(MyCustomLayoutBlockBuilder())

Or when using the MarkdownTextView use either of these options (depending on the configuration view or attributedString):

markdownTextView.addViewLayoutBlockBuilder(MyCustomLayoutBlockBuilder())
markdownTextView.addAttributedStringLayoutBlockBuilder(MyCustomLayoutBlockBuilder())

If needed you can also add a custom styling class to the default styling

styling.addStyling(MyCustomStyling())

Converter hook

The converter has a callback method which is called every time a MarkDownItem is converted to layout.

converter.didConvertElement = {
	markDownItem, view in
	// Do something with markDownItem and / or view here
}

When using the MarkdownTextView

markDownTextView.onDidConvertMarkDownItemToView = {
    markDownItem, view in

}

Link behavior

By default Markymark opens URL's using UIApplication.shared.delegate.open(_:open:options). links will only be openened when this method is implemented. Markymark allows changing this behavior by passing a custom URLOpener, an object that conforms to the URLOpener protocol.

let markDownView = MarkDownTextView()
markDownTextView?.urlOpener = MyCustomerURLOpener()

Using Markymark in Extensions

Markymark also supports usage the a Today extension. By default tapping url's is not working, since Extensions don't have access to UIApplication.shared, in order to support links you can pass a different url opener to a MarkyDownTextView. See the Example project for a working example:

markDownTextView?.urlOpener = ExtensionContextURLOpener(extensionContext: self.extensionContext)

Author

M2mobi, [email protected]

License

MarkyMark is available under the MIT license. See the LICENSE file for more info.

Comments
  • Feature/#98 add spm support

    Feature/#98 add spm support

    Like mentioned in PR #97 I've created a simplified version of the same.

    I've tested it and it works. In order for the project to build I've also had to change the way the default URL handler works. This will cause the user to implement url handling in the app delegate in some cases and is therefore a breaking change and new version will therefore be 10.0.0.

    I'll await results from Travis update it's stack if needed.

    If all is well at some point. I can do two things:

    1. Merge and create a SPM only release tagged 10.0.0 (since there's no new functionality for cocoapods users, I don't think this should be a problem)
    2. Merge and create a SPM only release tagged 10.0.0.prerelease

    @Basca what's would be your preference in how to handle the release?

    opened by lvnkmn 10
  • Make markymark safe to use in extensions

    Make markymark safe to use in extensions

    Currently, when trying to use markymark via cocoapods, and linking it into a framework, we get the compiler error 'shared' is unavailable: Use view controller based solutions where appropriate instead.. This is because UIApplication cannot be used from extensions or dynamically linked frameworks. A possible solution to make markymark extension safe is outlined here: https://gist.github.com/natecook1000/151d8de423eb77fc87bf#gistcomment-1656503

    opened by mhoeller-clue 6
  • Awesome library! Any chance of SVG support

    Awesome library! Any chance of SVG support

    Firstly, I just wanted to say I really think this library is awesome! I love that i don't have to deal with all the asynchronous loading that web views require and that I can throw either NSAttributedString or UIView into places like UITableViewCell—something that web views make less feasible. I also like how you've added functionality for easy extension.

    My question is about SVG support for inline images...while I realize I can make my own LayoutBlockBuilder to extend functionality, the issue is that if I want to associate my SVGAttributedStringBlockBuilder with an ImageMarkDownItem, I can't grab the file or altText from the markdown item as they missing accessibility identifiers and therefore default to internal.

    Additionally, the niceties you have implemented in your own TextAttachment class are also internal and therefore my SVG block builder can't take advantage of that either without copy/pasting the class.

    Can, at minimum, the accessibility identifiers be made public? Additionally, I'm curious if you have any thoughts about adding an SVG sub-spec that depends on another framework like SVGKit (enables extraction of UIImage for injecting for injecting to NSTextAttachment), or SwiftSVG (seems to have active maintenance, but no simple UIImage extraction).

    opened by loudmouth 5
  • Marky-Mark is not detecting url automatically. How to enable that??

    Marky-Mark is not detecting url automatically. How to enable that??

    Marky-Mark information

    • Marky-Mark version: _____
    • MarkDownConfiguration: _____ (View, AttributedString)

    Describe the bug

    A clear and concise description of what the bug is.

    Used markdown text

    What markdown text did you use that caused this issue? Try to isolate the part causing the issue.

    Screenshots

    If applicable, add screenshots to help explain your problem.

    opened by nselvapandian 4
  • add MinimumHeightStylingRule so Paragraphs containing only an empty l…

    add MinimumHeightStylingRule so Paragraphs containing only an empty l…

    …ine dont end up with zero height when using MarkdownConfiguration.view

    A text paragraph like "Lorem ipsum\n\nDolor sit amet." will be parsed into 3 single paragraphs, of which the middle one will contain an empty line. This will be rendered to a zero-height view. Not an issue per se, but in combination with a ParagraphStyling with no top/bottom contentInsets this will appear as if the new line was simply cut away.

    By setting the new fully optional minHeight property, the container for each paragraph will be constrained to show with at least the given height to show even with empty lines.

    opened by krs1w 4
  • MarkDownToAttributedStringConverter Swift 2

    MarkDownToAttributedStringConverter Swift 2

    pod install could not find the MarkDownToAttributedStringConverterConfiguration in Swift 2.2. I don't want to use Swift 2.3 or 3. This project is dependent on Swift 2.2 at the moment. We are using Version 0.1.3. Can you assist? Thanks

    opened by viewDidAppear 4
  • ref #92 - Expose a post configuration closure for UITextViews created…

    ref #92 - Expose a post configuration closure for UITextViews created…

    … by MarkDownAsAttributedStringViewConfiguration so that UITextViews can be further customized if needed.

    see https://github.com/M2Mobi/Marky-Mark/issues/92

    opened by lvnkmn 3
  • Ability to customize UITextView that's created in attributedString configuration of MarkdownTextView

    Ability to customize UITextView that's created in attributedString configuration of MarkdownTextView

    Hi there!

    It would be really nice if UITextViews created by MarkDownAsAttributedStringViewConfiguration could be further customized just like views created in MarkDownAsViewViewConfiguration can be customized though the public var onDidConvertMarkDownItemToView:((_ markDownItem: MarkDownItem, _ view: UIView) -> Void)? closure.

    this way scrolling, contentInsets etc. can be changed as desired.

    opened by lvnkmn 3
  • Handle tap on elements

    Handle tap on elements

    Hi,

    I'm currently using Marky-Mark and I love it, it saves me a lot of time.

    I need to handle touches on markdown elements (in my case, I need to open a new view controller on click on some images). What would be the simplest way to do that?

    I was thinking using a custom LayoutBlockBuilder with a delegate and a rule which overrides the default "image" rule. But I somehow need to pass my delegate to the custom LayoutBlockBuilder. Or maybe it could be a native feature for every items?

    Thanks a lot !

    opened by borgeser 3
  • Debug regex for ImageRule

    Debug regex for ImageRule

    The regex for ImageRule was broken. It would take any ! as the start of an inline image even if there was no image present in the markdown text if there was a regular link later in the same string.

    i.e. this was breaking and being rendered as an image: "Congratulations! Here is some text, head over to the Example site"

    I've fixed the regex. You can test with the following string here

    proper regex: !\[([^]]*)\]\(([^]]+)\) proper regex with extra escapes for swift strings: !\\[([^]]*)\\]\\(([^]]+)\\) test string: "Congratulations! Here is some text, head over to the Example site Alt text or contact support.

    Alt text Alt text a "

    opened by loudmouth 3
  • [iOS ]Unable to add <br/> tag functionality on MarkDowbTextView(). <br/> as it is display on UI.

    [iOS ]Unable to add
    tag functionality on MarkDowbTextView().
    as it is display on UI.

    Please provide your solution. Is it supported or need to find another way ?

    Marky-Mark information

    • Marky-Mark version: _____
    • MarkDownConfiguration: _____ (View, AttributedString)

    Describe the bug

    A clear and concise description of what the bug is.

    Used markdown text

    What markdown text did you use that caused this issue? Try to isolate the part causing the issue.

    Screenshots

    If applicable, add screenshots to help explain your problem.

    opened by zspradipwalghude 2
  • Setting MarkDownTextView.onDidConvertMarkDownItemToView does not work

    Setting MarkDownTextView.onDidConvertMarkDownItemToView does not work

    Marky-Mark information

    • Marky-Mark version: 10.1.4
    • MarkDownConfiguration: view (View, AttributedString)

    Describe the bug

    I have set up a MarkDownTextView via xib, and am attempting to set onDidConvertMarkDownItemToView via the didSet of the outlet as so:

        @IBOutlet weak var markDownTextView: MarkDownTextView! {
            didSet {
                self.markDownTextView.onDidConvertMarkDownItemToView = { item, view in
                   // doesn't get called
                }
            }
        }
    

    Unfortunately onDidConvertMarkDownItemToView never gets called. Am I doing something wrong? I have made a PR to fix this: https://github.com/M2mobi/Marky-Mark/pull/118

    opened by majsty 0
  • Add inline rules

    Add inline rules

    Allow adding inline rules and inline layout builders to MarkdownTextView

    • Made sure all layout builders are open classes
    • Allow overriding allowsChildMarkDownItems of rules
    • Refactor MarkdownTextView to have either a MarkdownViewTextView or MarkdownAttributedStringTextView and removed the existing solution
    opened by jvanzummeren 0
  • MarkyMark does not support `<url>` style links

    MarkyMark does not support `` style links

    Marky-Mark information

    • Marky-Mark version: N/A this is based on the Readme
    • MarkDownConfiguration: N/A (View, AttributedString)

    Describe the bug

    The Contentful flavor does not support <https://contentful.com> while Contentful as a system does support it.

    Markdown spec reference: https://spec.commonmark.org/0.30/#example-20

    Used markdown text

    <https://contentful.com>

    Screenshots

    NA

    opened by SMillerDev 0
Releases(10.1.4)
Owner
M2mobi
M2mobi
A Pure Swift implementation of the markdown mark-up language

SmarkDown A pure Swift markdown implementation consistent with Gruber's 1.0.1 version. It is released under the BSD license so please feel free to use

Swift Studies 67 Jan 24, 2022
A simple and customizable Markdown Parser for Swift

MarkdownKit MarkdownKit is a customizable and extensible Markdown parser for iOS and macOS. It supports many of the standard Markdown elements through

Bruno Oliveira 687 Dec 18, 2022
Leverages Apple's Swift-based Markdown parser to output NSAttributedString.

Markdownosaur ?? Markdownosaur uses Apple's excellent and relatively new Swift Markdown library to analyze a Markdown source, and then takes that anal

Christian Selig 232 Dec 20, 2022
Markdown parser for iOS

Marky Mark Marky Mark is a parser written in Swift that converts markdown into native views. The way it looks it highly customizable and the supported

M2mobi 254 Jun 11, 2021
Markdown parser for iOS

Marky Mark Marky Mark is a parser written in Swift that converts markdown into native views. The way it looks it highly customizable and the supported

M2mobi 262 Nov 23, 2021
LDOMarkdownParser - Parse (some) markdown attributes into an NSAttributedString

LDOMarkdownParser Description Convert markdown text styling into an NSAttributed

Lurado 1 Feb 4, 2022
Swift markdown library

Markdown ![Swift version](https://img.shields.io/badge/Swift-2.1 | 2.2-blue.svg) ![GitHub license](https://img.shields.io/badge/license-LGPL v3-green.

Crossroad Labs 79 Oct 9, 2022
`resultBuilder` support for `swift-markdown`

SwiftMarkdownBuilder resultBuilder support for swift-markdown. The default way to build Markdown in swift-markdown is to use varargs initializers, e.g

DocZ 9 May 31, 2022
Blazing fast Markdown / CommonMark rendering in Swift, built upon cmark.

Down Blazing fast Markdown (CommonMark) rendering in Swift, built upon cmark v0.29.0. Is your app using it? Let us know! If you're looking for iwasrob

John Nguyen 2k Dec 19, 2022
MarkdownView is a WKWebView based UI element, and internally use bootstrap, highlight.js, markdown-it.

MarkdownView is a WKWebView based UI element, and internally use bootstrap, highlight.js, markdown-it.

Keita Oouchi 1.8k Dec 21, 2022
Notepad - A fully themeable iOS markdown editor with live syntax highlighting.

Notepad is just like any other UITextView, but you need to use the convenience initializer in order to use the themes. To create a new theme, copy one of the existing themes and edit the JSON.

Rudd Fawcett 802 Dec 31, 2022
Markdown in SwiftUI, and some other interesting components.

RoomTime RoomTime is a bundle of tools developed in my app RoomTime Lite. ( ?? RoomTime Lite is still in development) Features TextArea AutoWrap Markd

Chen SiWei 56 Dec 20, 2022
AttributedString Markdown initializer with custom styling

AttributedString Markdown initializer with custom styling AttributedString in iOS 15 and macOS 12 comes with a Markdown initializer. But: There is no

Frank Rausch 41 Dec 19, 2022
An Objective-C framework for converting Markdown to HTML.

MMMarkdown MMMarkdown is an Objective-C framework for converting Markdown to HTML. It is compatible with OS X 10.7+, iOS 8.0+, tvOS, and watchOS. Unli

Matt Diephouse 1.2k Dec 14, 2022
Markdown syntax highlighter for iOS

Marklight Markdown syntax highlighter for iOS and macOS. Description Marklight is a drop in component to easily add realtime Markdown syntax highlight

Matteo Gavagnin 539 Dec 29, 2022
Rich Markdown editing control for iOS

MarkdownTextView Rich Markdown Editing for iOS MarkdownTextView is an iOS framework for adding rich Markdown editing capabilities. Support for Markdow

Indragie Karunaratne 676 Dec 7, 2022
Markdown parsing and rendering for iOS and OS X

CocoaMarkdown Markdown parsing and rendering for iOS and macOS CocoaMarkdown is a cross-platform framework for parsing and rendering Markdown, built o

Indragie Karunaratne 1.2k Dec 12, 2022
A Github action for creating generic run report using Markdown

create-report A Github action for creating generic run report (using Markdown!) - uses: michaelhenry/[email protected] with: report-title: "

Michael Henry 4 Apr 19, 2022
Generate help centers for your iOS apps, with Markdown

Generate help centers for your iOS apps, with Markdown! All you need to do is wr

Peter Salz 6 Jan 15, 2022