@@ -16,63 +16,78 @@ import Foundation
16
16
/// - Returns: A function which takes a rendering context, and returns a string.
17
17
public func atomFeed< Context: AtomContext , M> ( title: String , author: String ? = nil , baseURL: URL , summary: ( ( Item < M > ) -> String ? ) ? = nil ) -> ( _ context: Context ) -> String where Context. M == M {
18
18
let RFC3339_DF = ISO8601DateFormatter ( )
19
-
19
+
20
20
return { context in
21
21
let feedPath = context. outputPath. string
22
-
23
- // Create the root element
24
- let rootElement = XMLElement ( name: " feed " )
25
- rootElement. setAttributesWith ( [ " xmlns " : " http://www.w3.org/2005/Atom " ] )
26
-
27
- // Create the XML document
28
- let XML = XMLDocument ( rootElement: rootElement)
29
-
30
- let idElement = XMLElement ( name: " id " )
31
- idElement. stringValue = baseURL. appendingPathComponent ( feedPath) . absoluteString
32
- rootElement. addChild ( idElement)
33
-
34
- rootElement. addChild ( XMLElement ( name: " title " , stringValue: title) )
35
-
22
+ let currentDate = RFC3339_DF . string ( from: Date ( ) )
23
+
24
+ // Build the feed header
25
+ var xml = """
26
+ <?xml version= " 1.0 " encoding= " UTF-8 " ?>
27
+ <feed xmlns= " http://www.w3.org/2005/Atom " >
28
+ <id> \( baseURL. appendingPathComponent ( feedPath) . absoluteString) </id>
29
+ <title> \( escapeXML ( title) ) </title>
30
+
31
+ """
32
+
33
+ // Add optional author
36
34
if let author = author {
37
- let authorElement = XMLElement ( name: " author " )
38
- authorElement. addChild ( XMLElement ( name: " name " , stringValue: author) )
39
- rootElement. addChild ( authorElement)
35
+ xml += """
36
+ <author>
37
+ <name> \( escapeXML ( author) ) </name>
38
+ </author>
39
+
40
+ """
40
41
}
41
-
42
- let linkElement = XMLElement ( name : " link " )
43
- linkElement . setAttributesWith ( [ " rel " : " self " , " href " : baseURL . absoluteString ] )
44
- rootElement . addChild ( linkElement )
45
-
46
- let updatedElement = XMLElement ( name : " updated " , stringValue : RFC3339_DF . string ( from : Date ( ) ) )
47
- rootElement . addChild ( updatedElement )
48
-
49
- // add entries to feed
42
+
43
+ // Add link and updated date
44
+ xml += """
45
+ <link rel= " self " href= " \( baseURL . absoluteString ) " />
46
+ <updated> \( currentDate ) </updated>
47
+
48
+ """
49
+
50
+ // Add entries
50
51
for item in context. items {
51
- // create entry element
52
- let entryElement = XMLElement ( name : " entry " )
53
-
54
- let idElement = XMLElement ( name : " id " )
55
- idElement . stringValue = baseURL . appendingPathComponent ( item . url ) . absoluteString
56
-
57
- entryElement . addChild ( idElement )
58
- entryElement . addChild ( XMLElement ( name : " title " , stringValue : item . title ) )
59
- entryElement . addChild ( XMLElement ( name : " updated " , stringValue : RFC3339_DF . string ( from : item . lastModified ) ) )
60
-
52
+ let itemURL = baseURL . appendingPathComponent ( item . url ) . absoluteString
53
+
54
+ xml += """
55
+ <entry>
56
+ <id> \( itemURL ) </id>
57
+ <title> \( escapeXML ( item . title ) ) </title>
58
+ <updated> \( RFC3339_DF . string ( from : item . lastModified ) ) </updated>
59
+
60
+ """
61
+
61
62
if let summary, let summaryString = summary ( item) {
62
- let summaryElement = XMLElement ( name : " summary " , stringValue : summaryString )
63
- let alternateElement = XMLElement ( name : " link " )
64
- alternateElement . setAttributesWith ( [ " rel " : " alternate " , " href " : baseURL . appendingPathComponent ( item . url ) . absoluteString ] )
65
- entryElement . addChild ( summaryElement )
66
- entryElement . addChild ( alternateElement )
63
+ xml += """
64
+ <summary> \( escapeXML ( summaryString ) ) </summary>
65
+ <link rel= " alternate " href= " \( itemURL ) " />
66
+
67
+ """
67
68
} else {
68
- let contentElement = XMLElement ( name: " content " , stringValue: item. body)
69
- contentElement. setAttributesWith ( [ " type " : " html " ] )
70
- entryElement. addChild ( contentElement)
69
+ xml += """
70
+ <content type= " html " > \( escapeXML ( item. body) ) </content>
71
+
72
+ """
71
73
}
72
-
73
- rootElement . addChild ( entryElement )
74
+
75
+ xml += " </entry> \n "
74
76
}
75
-
76
- return String ( data: XML . xmlData ( options: [ . nodePrettyPrint] ) , encoding: . utf8) ?? " "
77
+
78
+ // Close the feed
79
+ xml += " </feed> "
80
+
81
+ return xml
77
82
}
78
83
}
84
+
85
+ // Helper function to escape special XML characters
86
+ private func escapeXML( _ string: String ) -> String {
87
+ return string
88
+ . replacingOccurrences ( of: " & " , with: " & " )
89
+ . replacingOccurrences ( of: " < " , with: " < " )
90
+ . replacingOccurrences ( of: " > " , with: " > " )
91
+ . replacingOccurrences ( of: " \" " , with: " " " )
92
+ . replacingOccurrences ( of: " ' " , with: " ' " )
93
+ }
0 commit comments