Base64 Encoding & Decoding in Swift

Swift's Foundation framework provides Base64 support through the Data type. Data.base64EncodedString() and Data(base64Encoded:) cover the majority of use cases with minimal code.

Basic String Encoding

import Foundation

// Encode string to Base64
let original = "Hello, world!"
if let data = original.data(using: .utf8) {
    let encoded = data.base64EncodedString()
    print(encoded) // "SGVsbG8sIHdvcmxkIQ=="
}

// Decode Base64 to string
let encoded = "SGVsbG8sIHdvcmxkIQ=="
if let data = Data(base64Encoded: encoded),
   let decoded = String(data: data, encoding: .utf8) {
    print(decoded) // "Hello, world!"
}

Encoding Options

import Foundation

let data = "Hello, world!".data(using: .utf8)!

// Default: no line breaks
let compact = data.base64EncodedString()

// With line breaks every 64 characters (for MIME/PEM)
let formatted = data.base64EncodedString(
    options: .lineLength64Characters
)

// With line breaks every 76 characters
let mime = data.base64EncodedString(
    options: .lineLength76Characters
)

URL-Safe Base64

Foundation doesn't have a built-in URL-safe encoder, but it's easy to add:

import Foundation

extension Data {
    func base64URLEncodedString() -> String {
        return base64EncodedString()
            .replacingOccurrences(of: "+", with: "-")
            .replacingOccurrences(of: "/", with: "_")
            .trimmingCharacters(in: CharacterSet(charactersIn: "="))
    }
}

extension String {
    static func fromBase64URL(_ base64url: String) -> String? {
        var base64 = base64url
            .replacingOccurrences(of: "-", with: "+")
            .replacingOccurrences(of: "_", with: "/")
        // Restore padding
        let remainder = base64.count % 4
        if remainder > 0 {
            base64 += String(repeating: "=", count: 4 - remainder)
        }
        guard let data = Data(base64Encoded: base64) else { return nil }
        return String(data: data, encoding: .utf8)
    }
}

// Usage
let encoded = "Hello 🌍".data(using: .utf8)!.base64URLEncodedString()
let decoded = String.fromBase64URL(encoded)

Encoding Images (UIImage / NSImage)

import Foundation
#if canImport(UIKit)
import UIKit

func imageToBase64(_ image: UIImage) -> String? {
    guard let data = image.pngData() else { return nil }
    return data.base64EncodedString()
}

func imageToDataURI(_ image: UIImage) -> String? {
    guard let data = image.pngData() else { return nil }
    return "data:image/png;base64," + data.base64EncodedString()
}

func base64ToImage(_ base64: String) -> UIImage? {
    guard let data = Data(base64Encoded: base64) else { return nil }
    return UIImage(data: data)
}
#endif

Encoding Files

import Foundation

// Encode file at URL to Base64
func encodeFile(at url: URL) throws -> String {
    let data = try Data(contentsOf: url)
    return data.base64EncodedString()
}

// Decode Base64 and write to file
func decodeToFile(_ base64: String, outputURL: URL) throws {
    guard let data = Data(base64Encoded: base64) else {
        throw NSError(domain: "Base64Error", code: 1,
                      userInfo: [NSLocalizedDescriptionKey: "Invalid Base64"])
    }
    try data.write(to: outputURL)
}

// Example
let fileURL = Bundle.main.url(forResource: "document", withExtension: "pdf")!
let encoded = try? encodeFile(at: fileURL)

Handling Malformed Input

import Foundation

func safeBase64Decode(_ input: String) -> Data? {
    // Remove whitespace that may have been added
    let cleaned = input.components(separatedBy: .whitespacesAndNewlines).joined()

    // Restore padding if stripped
    var padded = cleaned
    let remainder = padded.count % 4
    if remainder > 0 {
        padded += String(repeating: "=", count: 4 - remainder)
    }

    return Data(base64Encoded: padded)
}

Verify your Swift Base64 output

Paste the encoded string from your Swift app into base64.dev to decode and inspect it.

Open base64.dev →