import Foundation class BooruPostXMLParser: NSObject, XMLParserDelegate { private var posts: [BooruPost] = [] private var currentPost: BooruPost? private var parser: XMLParser init(data: Data) { parser = XMLParser(data: data) super.init() parser.delegate = self } func parse() -> [BooruPost] { parser.parse() return posts } func parser(_: XMLParser, didStartElement elementName: String, namespaceURI _: String?, qualifiedName _: String?, attributes attributeDict: [String: String] = [:]) { if elementName == "post" { guard let id = attributeDict["id"], let heightStr = attributeDict["height"], let height = Int(heightStr), let score = attributeDict["score"], let fileUrl = attributeDict["file_url"], let parentId = attributeDict["parent_id"], let sampleUrl = attributeDict["sample_url"], let sampleWidthStr = attributeDict["sample_width"], let sampleWidth = Int(sampleWidthStr), let sampleHeightStr = attributeDict["sample_height"], let sampleHeight = Int(sampleHeightStr), let previewUrl = attributeDict["preview_url"], let rating = attributeDict["rating"], let tags = attributeDict["tags"], let widthStr = attributeDict["width"], let width = Int(widthStr), let change = attributeDict["change"], let md5 = attributeDict["md5"], let creatorId = attributeDict["creator_id"], let hasChildrenStr = attributeDict["has_children"], let createdAt = attributeDict["created_at"], let status = attributeDict["status"], let source = attributeDict["source"], let previewWidthStr = attributeDict["preview_width"], let previewWidth = Int(previewWidthStr), let previewHeightStr = attributeDict["preview_height"], let previewHeight = Int(previewHeightStr) else { return } let hasNotesStr = attributeDict["has_notes"] ?? "false" let hasCommentsStr = attributeDict["has_comments"] ?? "false" currentPost = BooruPost( id: id, height: height, score: score, fileURL: URL(string: fileUrl)!, parentID: parentId, sampleURL: URL(string: sampleUrl)!, sampleWidth: sampleWidth, sampleHeight: sampleHeight, previewURL: URL(string: previewUrl)!, rating: rating, tags: tags.components(separatedBy: " ").filter { !$0.isEmpty }, width: width, change: change, md5: md5, creatorID: creatorId, hasChildren: hasChildrenStr == "true", createdAt: parseCreatedAt(createdAt)!, status: status, source: source, hasNotes: hasNotesStr == "true", hasComments: hasCommentsStr == "true", previewWidth: previewWidth, previewHeight: previewHeight ) } } func parser(_: XMLParser, didEndElement elementName: String, namespaceURI _: String?, qualifiedName _: String?) { if elementName == "post", let post = currentPost { posts.append(post) currentPost = nil } } #if DEBUG func parser(_: XMLParser, parseErrorOccurred parseError: any Error) { print("parser: \(parseError)") } #endif func parseCreatedAt(_ input: String) -> Date? { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "EEE MMM dd HH:mm:ss Z yyyy" dateFormatter.locale = Locale(identifier: "en_US_POSIX") if let date = dateFormatter.date(from: input) { return date } if let timestamp = Double(input) { return Date(timeIntervalSince1970: timestamp) } return nil } }