On this page:
2.1 Building feeds
feed-item
feed
2.2 Producing feed XML
express-xml
include-generator?
feed-language
feed-xslt-stylesheet
2.3 Podcasts
episode
podcast
2.4 Feed type predicates
feed-item?
feed?
episode?
podcast?
food?
8.17.0.6

2 Library Reference🔗ℹ

2.1 Building feeds🔗ℹ

Use feed-item and feed to create feeds for web content like blog posts, comments, or even notifications: any content with a timestamp and its own URL.

You have a choice of using RSS or Atom formats, or both. Twenty years ago, holy wars were fought over which format was superior and it was necessary to supply both in order to assure compatibility with the most clients. These days almost every client supports both, so you probably only need to supply one.

You should run all your feeds through the W3C Feed Validator. Please file an issue should you encounter any validation errors in feeds created with Splitflap.

procedure

(feed-item id    
  url    
  title    
  author    
  published    
  updated    
  content    
  [media])  feed-item?
  id : tag-uri?
  url : valid-url-string?
  title : string?
  author : person?
  published : moment?
  updated : moment?
  content : xexpr?
  media : (or/c enclosure? #f) = #f
Returns a #<feed-item> struct for inclusion in a feed. You can inspect its contents with express-xml.

The id argument must be a tag URI (obtain from mint-tag-uri or append-specific).

The title should not contain HTML markup; if it does, it will be escaped, and the raw markup may by shown in applications that use your feed.

The author argument must be a person struct.

The value of updated must be identical to or after published, taking time zone information into account, or an exception is raised. The values for these arguments can be most conveniently supplied by infer-moment, but any moment-producing method will work, such as constructing moments directly, parsing strings with parse-moment, etc.

If content is a tagged X-expression, it will be included as XML-appropriate escaped HTML; if it is a plain string, it will be included as CDATA.

You can optionally use the media argument to supply an enclosure, but if you are generating a feed for a podcast you should consider using episode and podcast instead.

procedure

(feed id site-url name entries)  feed?

  id : tag-uri?
  site-url : valid-url-string?
  name : string?
  entries : (listof feed-item?)
Returns a #<feed> struct. You can inspect its contents with express-xml.

If any of the tag URIs of the entries are tag=? with each other or with the feed id, an exception is raised identifying the first duplicate encountered.

2.2 Producing feed XML🔗ℹ

procedure

(express-xml data 
  dialect 
  [feed-url 
  #:as result-type]) 
  (or/c string? txexpr? document? element?)
  data : food?
  dialect : (or/c 'rss 'atom)
  feed-url : (or/c valid-url-string? #f) = #f
  result-type : (or/c 'xml-string 'xexpr 'xml) = 'xml-string
Returns the expression of data in one of three forms, depending on result-type: a string of XML, a tagged X-expression, or an XML object. In the latter case, the result is a document when data is a feed or a podcast, and an element otherwise.

The dialect argument is ignored when data is an episode or a podcast, since Apple’s Podcast feed requirements stipulate the use of RSS 2.0 for podcast feeds.

The feed-url argument must be supplied as a valid URL string when data is a feed or a podcast; this should be the URL where the feed itself will be located. It is not a required argument when data is any other type, and in those cases it will be ignored if supplied.

For complete feeds (e.g., when data is a feed or podcast), the entries/episodes will be sorted in reverse chronological order by their “updated” timestamps, and the most recent such timestamp is used as the value for feed-level “last updated” and/or “published” elements. If the feed contains no entries or episodes, these feed-level timestamps will use now/moment.

The output of complete feeds can be further affected by other parameters (view their documentation for more information):

Examples:
> (define item
    (feed-item (mint-tag-uri "rclib.example.com" "2012-06" "blog:example-post")
               "http://rclib.example.com"
               "Example"
               (person "Marion Paroo" "marion@rclib.example.com")
               (infer-moment "2013-04-13 08:45")
               (infer-moment "2013-04-14")
               '(article (p "Etc…"))))
> (display (express-xml item 'atom #f))

<entry>

  <title type="text">Example</title>

  <link rel="alternate" href="http://rclib.example.com" />

  <updated>2013-04-14T00:00:00Z</updated>

  <published>2013-04-13T08:45:00Z</published>

  <author>

    <name>Marion Paroo</name>

    <email>marion@rclib.example.com</email>

  </author>

  <id>tag:rclib.example.com,2012-06:blog:example-post</id>

  <content type="html">&lt;article&gt;&lt;p&gt;Etc…&lt;/p&gt;&lt;/article&gt;</content>

</entry>

> (express-xml item 'atom #:as 'xexpr)

'(entry

  (title ((type "text")) "Example")

  (link ((rel "alternate") (href "http://rclib.example.com")))

  (updated "2013-04-14T00:00:00Z")

  (published "2013-04-13T08:45:00Z")

  (author (name "Marion Paroo") (email "marion@rclib.example.com"))

  (id "tag:rclib.example.com,2012-06:blog:example-post")

  (content ((type "html")) "<article><p>Etc…</p></article>"))

parameter

(include-generator?)  boolean?

(include-generator? incl?)  void?
  incl? : boolean?
 = #t
When set to #t, express-xml will include a generator element in the feed generated for a feed or podcast, naming Racket vN.n [cs/3m] / splitflap vN.n as the generator of the feed.

parameter

(feed-language)  (or/c iso-639-language-code? #f)

(feed-language lang)  void?
  lang : (or/c iso-639-language-code? #f)
 = #f
A parameter that, when not set to #f, is used by express-xml as the language for a feed or podcast in place of system-language.

parameter

(feed-xslt-stylesheet)  (or/c url? non-empty-string? #f)

(feed-xslt-stylesheet path)  void?
  path : (or/c url? non-empty-string? #f)
 = #f
If this parameter is not #f, express-xml will use its value as the path to an XSLT stylesheet in the prolog of the XML document produced for a feed or podcast.

Generally, the XSLT file must be served from the same domain as the feed itself, otherwise the styling will not be applied.

Stylesheets can solve the problem of feeds looking and behaving in unfriendly ways when accessed directly in a web browser. See this Github issue for examples of people who have used XSLT stylesheets to add informative links and a welcoming layout to their feeds.

2.3 Podcasts🔗ℹ

Splitflap provides some special data types for podcast feeds: episode and podcast. These are patterned after Apple’s Podcast feed requirements since those serve as a kind of de facto standard for this application.

procedure

(episode id    
  url    
  title    
  author    
  published    
  updated    
  content    
  media    
  [#:duration duration    
  #:image-url image-url    
  #:explicit? explicit    
  #:episode-num ep-num    
  #:season-num s-num    
  #:type type    
  #:block? block])  episode?
  id : tag-uri?
  url : valid-url-string?
  title : string?
  author : person?
  published : moment?
  updated : moment?
  content : xexpr?
  media : enclosure?
  duration : (or/c exact-nonnegative-integer? #f) = #f
  image-url : (or/c valid-url-string? #f) = #f
  explicit : any/c = null
  ep-num : (or/c exact-nonnegative-integer? #f) = #f
  s-num : (or/c exact-nonnegative-integer? #f) = #f
  type : (or/c 'trailer 'full 'bonus #f) = #f
  block : any/c = #f
Returns an #<episode> struct, which is required for podcasts in the same way that feed-items are required for feeds. You can inspect its contents with express-xml.

The value of updated must be identical to or after published, taking time zone information into account, or an exception is raised. The values for these arguments can be most conveniently supplied by infer-moment, but any moment-producing method will work, such as constructing moments directly, parsing strings with parse-moment, etc.

If content is a tagged X-expression, it will be included as escaped HTML; if it is a plain string, it will be included as CDATA.

Below are further notes about particular elements supplied to episode. The colored passages indicate things which are required by Apple for inclusion in the Apple Podcasts directory but which are not validated by Splitflap. (See Apple’s Podcast feed requirements.)

  • The title should contain no markup, and should not contain episode or season number information (use #:episode-num and #:season-num for this instead).

  • The #:image-url is for episode-specific artwork. It must point to an image with a minimum size of 1400 ⨉ 1400 pixels and a maximum size of 3000 ⨉ 3000 pixels (the preferred size), in JPEG or PNG format, 72 dpi, with appropriate file extensions (.jpg, .png), and in the RGB colorspace. See Apple artwork requirements for other requirements.

  • The #:duration gives the episode’s duration in seconds, and is optional but recommended.

  • If explicit is an optional override of the mandatory feed-level parental advisory flag in podcast. If it is null (the default), the episode will not contain any parental advisory information. If it is #f, Apple Podcasts will mark the episode as “Clean”. If it is any other value, Apple Podcasts will mark the episode as “Explicit”.

  • The #:episode-num is optional, but Apple will require it if the podcast has a type of 'episodic.

  • You can optionally set #:type to 'full if this is a normal full-length episode; to 'trailer for short promos and previews; or to 'bonus for extra or cross-promotional content.

  • The #:block flag can be set to #t to prevent a particular episode from appearing in Apple podcasts. For example you might want to block a specific episode if you know that its content would otherwise cause the entire podcast to be removed from Apple Podcasts.

procedure

(podcast id    
  site-url    
  name    
  episodes    
  category    
  image-url    
  owner    
  #:explicit? explicit    
  [#:type type    
  #:block? block    
  #:complete? complete    
  #:new-feed-url new-url])  podcast?
  id : tag-uri?
  site-url : valid-url-string?
  name : string?
  episodes : (listof episode?)
  category : (or/c string? (list/c string? string?))
  image-url : valid-url-string?
  owner : person?
  explicit : any/c
  type : (or/c 'serial 'episodic #f) = #f
  block : any/c = #f
  complete : any/c = #f
  new-url : (or/c valid-url-string? #f) = #f
Returns a #<podcast> struct, which can be converted into a feed with express-xml.

If any of the tag URIs of the episodes are tag=? with each other or with the podcast feed id, an exception is raised identifying the first duplicate encountered.

Below are some notes about particular elements supplied to podcast. The colored passages indicate things which are required by Apple for inclusion in the Apple Podcasts directory but which are not validated by Splitflap. (See Apple’s Podcast feed requirements.)

  • The category can be either a simple category or a list containing a category and sub-category. The category names must be drawn from Apple’s podcast category list.

  • The image-url links to the show’s artwork. It must point to an image with a minimum size of 1400 ⨉ 1400 pixels and a maximum size of 3000 ⨉ 3000 pixels (the preferred size), in JPEG or PNG format, 72 dpi, with appropriate file extensions (.jpg, .png), and in the RGB colorspace. See Apple artwork requirements for other requirements.

  • The owner is for administrative communication about the podcast and is not displayed in Apple’s podcast listings.

  • The #:type can be one of two values.

    • Use 'episodic when episodes are not intended to be consumed in any particular order: in this case, Apple Podcasts will present newest episodes first. (If organized into seasons, the newest season will be presented first; otherwise, episodes will be grouped by year published, newest first.)

    • Use 'serial when episodes are intended to be consumed in sequential order: in this case, Apple Podcasts will present the oldest episodes first. (If organized into seasons, the newest season will be shown first.)

  • Setting #:block to #t will prevent the entire podcast from appearing in Apple Podcasts.

  • Setting #:complete? to #t indicates that a podcast is complete and you will not post any more episodes in the future.

  • If you change the URL where your feed is located, then (in the feed located at the original URL) set #:new-feed-url to the new feed’s URL. Apple Podcasts (and possibly other clients) will automatically update subscriptions to use the new feed. See Apple’s guidelines for moving your RSS feed.

2.4 Feed type predicates🔗ℹ

procedure

(feed-item? v)  boolean?

  v : any/c
Returns #t if v is a feed-item struct, #f otherwise.

procedure

(feed? v)  boolean?

  v : any/c
Returns #t if v is a feed struct, #f otherwise.

procedure

(episode? v)  boolean?

  v : any/c
Returns #t if v is an episode struct, #f otherwise.

procedure

(podcast? v)  boolean?

  v : any/c
Returns #t if v is a podcast struct, #f otherwise.

procedure

(food? v)  boolean?

  v : any/c
Returns #t when v is one of the struct types that implements the generic express-xml function: enclosure, feed-item, feed, episode, or podcast; #f otherwise