> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloneable.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# CloneableJSON documentation

## Overview

CloneableJSON extends JSON with data types such as images, locations, and coordinate mappings. Use it to extract, process, and save pole and midspan measurements.

CloneableJSON stores flexible data in `AnyCloneableData`. Components use this wrapper to exchange data.

## Reference

* `AnyCloneableData` is a type-erased container to hold complex data types
* `CloneableImage` stores UIImage data with file persistence capabilities
* `CloneableFileDataType` is a protocol for file-based data types that have a UUID identifier

## Pole measurement data structure

Pole measurements return this CloneableJSON structure:

```json theme={null}
{
  "high_accuracy": true,
  "pole_information": {
    "height": 35.5,
    "lean_angle": 2.1,
    "location": {
      "latitude": 40.7128,
      "longitude": -74.0060,
      "altitude": 10.5
    }
  },
  "attachments": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "type": "attachment",
      "classification": "Transformer",
      "inventoryClass": {
        "name": "Transformer",
        "subcategories": [
          {
            "name": "Size",
            "value": "25 kVA"
          },
          {
            "name": "Type",
            "value": "Single Phase"
          }
        ]
      },
      "owner": "Utility Company A",
      "images": [
        {
          "imageId": "550e8400-e29b-41d4-a716-446655440002",
          "point": {
            "x": 256.5,
            "y": 384.2
          }
        }
      ],
      "related_task_id": "task_123",
      "height": 28.5,
      "distanceFromPoleTop": 7.0,
      "relatedSpans": ["550e8400-e29b-41d4-a716-446655440010"],
      "relatedGuys": ["550e8400-e29b-41d4-a716-446655440020"],
      "heading": 180
    }
  ],
  "images": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440003",
      "type": "pole",
      "image": "[CloneableImage UUID reference]",
      "referenceLine": {
        "bottom": {
          "id": "550e8400-e29b-41d4-a716-446655440004",
          "pixelCoordinates": {
            "x": 320.0,
            "y": 800.0
          },
          "worldCoordinates": {
            "x": 0.0,
            "y": 0.0,
            "z": 0.0
          }
        },
        "top": {
          "id": "550e8400-e29b-41d4-a716-446655440005",
          "pixelCoordinates": {
            "x": 320.0,
            "y": 100.0
          },
          "worldCoordinates": {
            "x": 0.0,
            "y": 0.0,
            "z": 35.5
          }
        }
      },
      "scalingData": {
        "pixelArray": [
          {
            "id": "550e8400-e29b-41d4-a716-446655440006",
            "pixelCoordinates": {
              "x": 200.5,
              "y": 450.2
            },
            "worldCoordinates": {
              "x": -2.1,
              "y": 0.8,
              "z": 15.5
            }
          }
        ]
      },
      "cameraTilt": 5.2,
      "predictedCameraTilt": 4.8,
      "wasAutoCaptured": false,
      "wasCapturedInSafeZone": true,
      "stickDistance": 12.5,
      "stickHeight": 8.0
    }
  ]
}
```

## Understand CloneableJSON

CloneableJSON uses subscripts that return `CloneableJSONValue` enums. Accessor methods simplify data access.

### Access values

#### Safe optional access

```swift theme={null}
// Simple property access - returns nil if not found or wrong type
let height = result["pole_information"]["height"].numberValue
let classification = result["attachments"][0]["classification"].stringValue
let isHighAccuracy = result["high_accuracy"].boolValue

// With optional binding
if let height = result["pole_information"]["height"].numberValue {
    print("Pole height: \(height) feet")
}

// Your original example, now simplified:
print("Vertical measurement result: \(result["pole_information"]["height"].numberValue ?? 0)")
```

#### Throwing access

```swift theme={null}
// Throws error with details if wrong type or not found
do {
    let height = try result["pole_information"]["height"].getNumber()
    let classification = try result["attachments"][0]["classification"].getString()
    let attachments = try result["attachments"].getArray()
    
    print("Pole height: \(height) feet")
    print("Found \(attachments.count) attachments")
} catch let error as JSONError {
    switch error {
    case .wrongType(let expected, let actual):
        print("Type mismatch: expected \(expected) but found \(actual)")
    case .valueNotFound(let path):
        print("Value not found at path: \(path)")
    default:
        print("JSON Error: \(error.localizedDescription)")
    }
}
```

#### Direct enum pattern matching

```swift theme={null}
// Access returns CloneableJSONValue enum
let heightValue = result["pole_information"]["height"]

// Extract the actual value
switch heightValue {
case .value(let anyCloneableData):
    // Now you can use the getter methods on AnyCloneableData
    let numberValue = anyCloneableData.getNumberValue()
    let stringValue = anyCloneableData.getStringValue()
    let imageValue = anyCloneableData.getImageValue()  // For CloneableImage
case .object(let dict):
    // It's a nested object - dict is [String: CloneableJSONValue]
case .array(let array):
    // It's an array - array is [CloneableJSONValue]
case .undefined:
    // Key doesn't exist or value is null
}
```

### Accessor properties and methods

#### Optional Properties (return nil if wrong type):

* `.numberValue` → `Double?`
* `.stringValue` → `String?`
* `.boolValue` → `Bool?`
* `.dateValue` → `Date?`
* `.objectValue` → `[String: CloneableJSONValue]?`
* `.arrayValue` → `[CloneableJSONValue]?`
* `.isUndefined` → `Bool` (true if value is undefined)

#### Throwing Methods (throw JSONError if wrong type):

* `.getNumber()` → `Double`
* `.getString()` → `String`
* `.getBool()` → `Bool`
* `.getDate()` → `Date`
* `.getObject()` → `[String: CloneableJSONValue]`
* `.getArray()` → `[CloneableJSONValue]`

#### Helper Properties:

* `.typeDescription` → `String` (returns current type: "object", "array", "string", "number", etc.)

### JSONError Types

```swift theme={null}
public enum JSONError: Error, LocalizedError {
    case invalidJSON
    case unsupportedType
    case typeMismatch
    case invalidSchema
    case invalidRootStructure
    case valueNotFound(path: String)
    case wrongType(expected: String, actual: String)
}
```

## Work with attachments

### Extract attachment data

```swift theme={null}
func extractAttachments(from result: CloneableJSON) -> [AttachmentData] {
    var attachments: [AttachmentData] = []
    
    // Get attachments array using convenient accessor
    guard let attachmentsArray = result["attachments"].arrayValue else {
        print("No attachments found")
        return attachments
    }
    
    for attachmentValue in attachmentsArray {
        var attachment = AttachmentData()
        
        // Extract basic properties using optional accessors
        attachment.id = attachmentValue["id"].stringValue ?? UUID().uuidString
        attachment.type = attachmentValue["type"].stringValue ?? "unknown"
        attachment.classification = attachmentValue["classification"].stringValue
        attachment.height = attachmentValue["height"].numberValue
        attachment.distanceFromTop = attachmentValue["distanceFromPoleTop"].numberValue
        attachment.owner = attachmentValue["owner"].stringValue
        attachment.relatedTaskId = attachmentValue["related_task_id"].stringValue
        attachment.heading = attachmentValue["heading"].numberValue
        
        // Extract inventory classification with subcategories
        if attachmentValue["inventoryClass"].objectValue != nil {
            attachment.inventoryData = extractInventoryClassification(attachmentValue["inventoryClass"])
        }
        
        // Extract related spans (array of UUIDs)
        if let relatedSpans = attachmentValue["relatedSpans"].arrayValue {
            attachment.relatedSpanIds = relatedSpans.compactMap { $0.stringValue }
        }
        
        // Extract related guys (array of UUIDs)
        if let relatedGuys = attachmentValue["relatedGuys"].arrayValue {
            attachment.relatedGuyIds = relatedGuys.compactMap { $0.stringValue }
        }
        
        // Extract image measurements (pixel points on images)
        if let imagesArray = attachmentValue["images"].arrayValue {
            attachment.imageReferences = extractAttachmentImageReferences(imagesArray)
        }
        
        attachments.append(attachment)
    }
    
    return attachments
}

struct AttachmentData {
    var id: String = ""
    var type: String = ""                    // "attachment", "guy", "wire"
    var classification: String?              // Equipment name
    var height: Double?                      // Height above ground in feet
    var distanceFromTop: Double?             // Distance from pole top in feet
    var owner: String?                       // Utility company
    var relatedTaskId: String?               // Related verification task
    var heading: Double?                     // Compass direction in degrees
    var inventoryData: InventoryClassificationData?
    var relatedSpanIds: [String] = []        // Related power line span UUIDs
    var relatedGuyIds: [String] = []         // Related guy wire UUIDs
    var imageReferences: [AttachmentImageReference] = []
}

// Helper function to extract attachment image references
func extractAttachmentImageReferences(_ imagesArray: [CloneableJSONValue]) -> [AttachmentImageReference] {
    var references: [AttachmentImageReference] = []
    
    for imageValue in imagesArray {
        var ref = AttachmentImageReference()
        
        // Extract image ID
        ref.imageId = imageValue["imageId"].stringValue ?? ""
        
        // Extract pixel point where attachment was marked
        if let x = imageValue["point"]["x"].numberValue,
           let y = imageValue["point"]["y"].numberValue {
            ref.pixelPoint = CGPoint(x: x, y: y)
        }
        
        references.append(ref)
    }
    
    return references
}

struct AttachmentImageReference {
    var imageId: String = ""
    var pixelPoint: CGPoint = CGPoint.zero  // Where attachment was marked on image
}
```

### Extract inventory classifications

```swift theme={null}
func extractInventoryClassification(_ inventoryClass: CloneableJSONValue) -> InventoryClassificationData {
    var classification = InventoryClassificationData()
    
    // Extract main classification name
    classification.name = inventoryClass["name"].stringValue ?? "Unknown"
    
    // Extract subcategories
    if let subcategoriesArray = inventoryClass["subcategories"].arrayValue {
        for subcategoryValue in subcategoriesArray {
            var subcategory = SubcategoryData(name: "", value: nil, required: false, options: [])
            
            subcategory.name = subcategoryValue["name"].stringValue ?? ""
            subcategory.value = subcategoryValue["value"].stringValue
            subcategory.required = subcategoryValue["required"].boolValue ?? false
            
            // Extract available options
            if let optionsArray = subcategoryValue["options"].arrayValue {
                subcategory.options = optionsArray.compactMap { $0.stringValue }
            }
            
            classification.subcategories.append(subcategory)
        }
    }
    
    return classification
}

struct InventoryClassificationData {
    var name: String = ""
    var subcategories: [SubcategoryData] = []
}

struct SubcategoryData {
    var name: String
    var value: String?
    var required: Bool
    var options: [String]
}
```

## Work with images

### Extract and save images

```swift theme={null}
import UIKit

func extractAndSaveImages(from result: CloneableJSON, to directory: URL) async {
    guard let imagesArray = result["images"].arrayValue else {
        print("No images found")
        return
    }
    
    for imageValue in imagesArray {
        let imageId = imageValue["id"].stringValue ?? "unknown"
        
        // Extract CloneableImage using pattern matching
        var cloneableImage: CloneableImage?
        if case .value(let imageData) = imageValue["image"] {
            cloneableImage = imageData.data as? CloneableImage
        }
        
        guard let image = cloneableImage else { 
            print("Could not extract CloneableImage for \(imageId)")
            continue 
        }
        
        // Get the UIImage from CloneableImage
        guard let uiImage = image.imageData else {
            print("CloneableImage has no imageData for \(imageId)")
            continue
        }
        
        // Save image with metadata
        await saveImageWithMetadata(
            image: uiImage,
            imageId: imageId,
            cloneableImage: image,
            metadata: imageValue,
            to: directory
        )
        
        // Extract and save scaling data
        await extractScalingData(imageValue: imageValue, imageId: imageId, to: directory)
    }
}

func saveImageWithMetadata(
    image: UIImage, 
    imageId: String, 
    cloneableImage: CloneableImage,
    metadata: CloneableJSONValue, 
    to directory: URL
) async {
    // Save the image file using CloneableImage's built-in method
    let imageURL = directory.appendingPathComponent("\(imageId).jpg")
    
    do {
        try cloneableImage.saveToFile(fileURL: imageURL)
        print("Saved image: \(imageURL.path)")
    } catch {
        print("Failed to save image \(imageId): \(error)")
    }
    
    // Save image metadata
    let metadataDict = extractImageMetadata(metadata)
    let metadataURL = directory.appendingPathComponent("\(imageId)_metadata.json")
    
    do {
        let jsonData = try JSONSerialization.data(withJSONObject: metadataDict, options: [.prettyPrinted])
        try jsonData.write(to: metadataURL)
        print("Saved image metadata: \(metadataURL.path)")
    } catch {
        print("Failed to save image metadata: \(error)")
    }
}

func extractImageMetadata(_ imageValue: CloneableJSONValue) -> [String: Any] {
    var metadata: [String: Any] = [:]
    
    // Extract basic properties using convenient accessors
    if let id = imageValue["id"].stringValue { metadata["id"] = id }
    if let type = imageValue["type"].stringValue { metadata["type"] = type }
    if let timestamp = imageValue["captureTimestamp"].stringValue { metadata["captureTimestamp"] = timestamp }
    if let orientation = imageValue["deviceOrientation"].stringValue { metadata["deviceOrientation"] = orientation }
    
    // Extract resolution
    if let width = imageValue["imageResolution"]["width"].numberValue,
       let height = imageValue["imageResolution"]["height"].numberValue {
        metadata["imageResolution"] = [
            "width": width,
            "height": height
        ]
    }
    
    // Extract AR camera tilt data
    if let tilt = imageValue["cameraTilt"].numberValue { metadata["cameraTilt"] = tilt }
    if let predictedTilt = imageValue["predictedCameraTilt"].numberValue { 
        metadata["predictedCameraTilt"] = predictedTilt 
    }
    
    // Extract capture method flags
    if let autoCaptured = imageValue["wasAutoCaptured"].boolValue { 
        metadata["wasAutoCaptured"] = autoCaptured 
    }
    if let safeZone = imageValue["wasCapturedInSafeZone"].boolValue { 
        metadata["wasCapturedInSafeZone"] = safeZone 
    }
    
    // Extract AR accuracy measurements
    if let distance = imageValue["stickDistance"].numberValue { metadata["stickDistance"] = distance }
    if let height = imageValue["stickHeight"].numberValue { metadata["stickHeight"] = height }
    
    return metadata
}
```

### Use the AnyCloneableData extension

```swift theme={null}
// Using the AnyCloneableData extension method
func extractImageUsingExtension(from imageValue: CloneableJSONValue) -> UIImage? {
    if case .value(let anyCloneableData) = imageValue["image"] {
        return anyCloneableData.getImageValue()  // Returns UIImage?
    }
    return nil
}

// Example usage
if let uiImage = extractImageUsingExtension(from: imageValue) {
    // Work directly with UIImage
    let imageData = uiImage.jpegData(compressionQuality: 0.8)
    // ... save or process the image
}
```

### Work with scaling data

```swift theme={null}
func extractScalingData(imageValue: CloneableJSONValue, imageId: String, to directory: URL) async {
    var scalingData: [String: Any] = [
        "imageId": imageId,
        "timestamp": ISO8601DateFormatter().string(from: Date())
    ]
    
    // Extract pixel array scaling data
    if let pixelArray = imageValue["scalingData"]["pixelArray"].arrayValue {
        var scalingPoints: [[String: Any]] = []
        
        for pixelPoint in pixelArray {
            var scalingPoint: [String: Any] = [:]
            
            if let id = pixelPoint["id"].stringValue { scalingPoint["id"] = id }
            
            // Extract pixel coordinates
            if let x = pixelPoint["pixelCoordinates"]["x"].numberValue,
               let y = pixelPoint["pixelCoordinates"]["y"].numberValue {
                scalingPoint["pixelCoordinates"] = ["x": x, "y": y]
            }
            
            // Extract world coordinates
            if let x = pixelPoint["worldCoordinates"]["x"].numberValue,
               let y = pixelPoint["worldCoordinates"]["y"].numberValue,
               let z = pixelPoint["worldCoordinates"]["z"].numberValue {
                scalingPoint["worldCoordinates"] = ["x": x, "y": y, "z": z]
            }
            
            scalingPoints.append(scalingPoint)
        }
        
        scalingData["pixelArray"] = scalingPoints
    }
    
    // Extract reference line data
    if imageValue["referenceLine"].objectValue != nil {
        scalingData["referenceLine"] = extractReferenceLineData(imageValue["referenceLine"])
    }
    
    // Save scaling data to JSON file
    let scalingDataURL = directory.appendingPathComponent("\(imageId)_scaling.json")
    
    do {
        let jsonData = try JSONSerialization.data(withJSONObject: scalingData, options: [.prettyPrinted, .sortedKeys])
        try jsonData.write(to: scalingDataURL)
        print("Saved scaling data: \(scalingDataURL.path)")
    } catch {
        print("Failed to save scaling data for \(imageId): \(error)")
    }
}

func extractReferenceLineData(_ referenceLine: CloneableJSONValue) -> [String: Any] {
    var referenceLineData: [String: Any] = [:]
    
    // Extract bottom point
    if referenceLine["bottom"].objectValue != nil {
        referenceLineData["bottom"] = extractPointData(referenceLine["bottom"])
    }
    
    // Extract top point
    if referenceLine["top"].objectValue != nil {
        referenceLineData["top"] = extractPointData(referenceLine["top"])
    }
    
    return referenceLineData
}

func extractPointData(_ point: CloneableJSONValue) -> [String: Any] {
    var pointData: [String: Any] = [:]
    
    if let id = point["id"].stringValue { pointData["id"] = id }
    
    // Extract pixel coordinates
    if let x = point["pixelCoordinates"]["x"].numberValue,
       let y = point["pixelCoordinates"]["y"].numberValue {
        pointData["pixelCoordinates"] = ["x": x, "y": y]
    }
    
    // Extract world coordinates
    if let x = point["worldCoordinates"]["x"].numberValue,
       let y = point["worldCoordinates"]["y"].numberValue,
       let z = point["worldCoordinates"]["z"].numberValue {
        pointData["worldCoordinates"] = ["x": x, "y": y, "z": z]
    }
    
    return pointData
}
```

## Work with CloneableImage directly

### Advanced image operations

```swift theme={null}
// Download image from backend if needed
func downloadImageIfNeeded(_ cloneableImage: CloneableImage) async -> CloneableImage? {
    // Check if image data is already loaded
    if cloneableImage.imageData != nil {
        return cloneableImage
    }
    
    // Download from backend
    do {
        let downloadedImage = try await cloneableImage.downloadImage(
            maxRetries: 3,
            onProgress: { progress in
                print("Download progress: \(Int(progress * 100))%")
            }
        )
        return downloadedImage
    } catch {
        print("Failed to download image: \(error)")
        return nil
    }
}

// Save to temporary file
func saveImageTemporarily(_ cloneableImage: CloneableImage) throws -> URL {
    return try cloneableImage.saveToTemporaryFile(fileExtension: "jpg")
}

// Create CloneableImage from UIImage
func createCloneableImage(from uiImage: UIImage, persistTemporary: Bool = false) -> CloneableImage {
    return CloneableImage(uiImage, persistTemporary: persistTemporary)
}
```

## Export a JSON string

CloneableJSON provides a built-in method to convert to standard JSON string:

```swift theme={null}
// Convert CloneableJSON to pretty-printed JSON string
let jsonString = cloneableJSON.getJSONValue()

// Example usage with pole measurement
let poleResult: CloneableJSON = // ... your measurement result
let jsonOutput = poleResult.getJSONValue()

// Use the JSON string
print(jsonOutput)                    // Print to console
saveToFile(jsonOutput)              // Save to file
sendToAPI(jsonOutput)               // Send to web API

// Save to file example
func saveMeasurementResult(_ result: CloneableJSON, to url: URL) throws {
    let jsonString = result.getJSONValue()
    try jsonString.write(to: url, atomically: true, encoding: .utf8)
}
```

The `getJSONValue()` method automatically:

* Converts `CloneableImage` objects to UUID strings (via `getJSONValue()` method)
* Converts `CloneableFileDataType` objects to UUID strings
* Converts `CloneableNumber` to standard JSON numbers
* Returns pretty-printed, sorted JSON string
* Preserves complete nested data structure
* Handles undefined values as `null`

## Schema support

CloneableJSON supports optional schema validation:

```swift theme={null}
// Check if JSON has a schema
if let schema = cloneableJSON.schema {
    print("Schema ID: \(schema.id)")
    print("Schema name: \(schema.name)")
    
    // Validate against schema
    if cloneableJSON.validate() {
        print("JSON validates against schema")
    } else {
        print("JSON validation failed")
    }
}

// Access schema properties
if let heightSchema = cloneableJSON[schemaProperty: "height"] {
    print("Height type: \(heightSchema.type)")
    print("Height description: \(heightSchema.description)")
}
```

## Get all file data values

```swift theme={null}
// Get all file data values from the JSON (includes images)
let fileDataValues = cloneableJSON.getAllFileDataValues()

for fileData in fileDataValues {
    if let cloneableImage = fileData.data as? CloneableImage {
        print("Found image: \(cloneableImage.id)")
        if let uiImage = cloneableImage.imageData {
            print("  Image size: \(uiImage.size)")
        } else {
            print("  Image not loaded, needs download")
        }
    } else if let fileDataType = fileData.data as? CloneableFileDataType {
        print("Found file: \(fileDataType.id)")
    }
}
```

## Error handling best practices

When using the throwing accessor methods, you can handle specific error cases:

```swift theme={null}
do {
    // Chain multiple accesses with clear error context
    let height = try result["pole_information"]["height"].getNumber()
    let attachments = try result["attachments"].getArray()
    
    for (index, attachment) in attachments.enumerated() {
        do {
            let classification = try attachment["classification"].getString()
            print("Attachment \(index): \(classification)")
        } catch {
            print("Attachment \(index) has no classification")
        }
    }
} catch let error as JSONError {
    switch error {
    case .wrongType(let expected, let actual):
        print("Type mismatch: expected \(expected) but found \(actual)")
    case .valueNotFound(let path):
        print("Value not found at path: \(path)")
    default:
        print("JSON Error: \(error.localizedDescription)")
    }
} catch {
    print("Unexpected error: \(error)")
}
```

## Best practices

1. **Use optional accessors for exploratory code** - When you're not sure if a value exists or what type it is
2. **Use throwing accessors for production code** - When you expect certain data to be present and want clear error messages
3. **Check `.isUndefined` before accessing nested values** - To avoid unnecessary operations
4. **Use `.typeDescription` for debugging** - To understand what type of value you're dealing with
5. **Prefer the convenience accessors** - They're cleaner and more Swift-like than manual enum matching
6. **Handle CloneableImage download scenarios** - Images may need to be downloaded from backend
7. **Use CloneableImage's built-in file operations** - For saving and loading images efficiently
8. **Import UIKit when working with images** - CloneableImage uses UIImage which requires UIKit

## Quick reference

### Your Original Problem - Now Solved!

```swift theme={null}
// Before (complex):
if case .value(let cloneableData) = result["pole_information"]["height"],
   let numberValue = cloneableData.getNumberValue() {
    print("Vertical measurement result: \(numberValue)")
}

// After (simple):
print("Vertical measurement result: \(result["pole_information"]["height"].numberValue ?? 0)")

// Or with error handling:
do {
    let height = try result["pole_information"]["height"].getNumber()
    print("Vertical measurement result: \(height)")
} catch {
    print("Could not get height: \(error)")
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.