Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Transform a Feedbin JSON backup file into a directory of Jekyll-formatted posts
import Foundation
Alls you gotta do is (in Main.swift):
1. Initialize an Importer with the file URL to the JSON file (origin) and a URL to the directory that will contain all the Jekyll-formatted posts (destination).
2. Call the `run()` method on the Importer.
struct Post: Codable {
static let filenameFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
return formatter
static let frontmatterFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd HH:mm:ss"
return formatter
static let disallowedCharacters = NSCharacterSet
.union(CharacterSet(charactersIn: "-"))
let title: String
let url: String
let content: String
let published: Date
var fileName: String {
let date = Post.filenameFormatter.string(from: published)
let slug = title
.replacingOccurrences(of: " ", with: "-")
.components(separatedBy: Post.disallowedCharacters)
.joined(separator: "")
.trimmingCharacters(in: CharacterSet(charactersIn: "-"))
return "\(date)-\(slug).html"
var fileContent: String {
let dateStamp = Post.frontmatterFormatter.string(from: published)
return """
layout: post
title: "\(title)"
date: \(dateStamp)
navtab: blog
public final class Importer {
let origin: URL
let destination: URL
let formatter: ISO8601DateFormatter
public init(origin: URL, destination: URL) {
print("Will read data from \(origin)")
self.origin = origin
self.destination = destination
self.formatter = ISO8601DateFormatter()
// init satisfied
formatter.formatOptions = formatter.formatOptions.union(.withFractionalSeconds)
public func run() throws {
let data = try Data(contentsOf: origin)
try decode(data)
private func decode(_ data: Data) throws {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(formatter.parseDate)
let posts = try decoder.decode([Post].self, from: data)
posts.forEach { post in
let fileUrl = destination.appendingPathComponent(post.fileName, isDirectory: false)
let fileContent = .utf8)!
try! fileContent.write(to: fileUrl, options: .atomicWrite)
extension ISO8601DateFormatter {
enum DateError: Error {
case invalidDateFormat
func parseDate(using decoder: Decoder) throws -> Date {
let string = try decoder.singleValueContainer().decode(String.self)
guard let date = date(from: string) else {
throw DateError.invalidDateFormat
return date
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.