CloneableDataType Protocol

Inner representation of data types which are supported by the Cloneable platform and wrapped with the type erased AnyCloneableData

Overview

The CloneableDataType protocol is designed to define a standardized interface for data types that can be cloned, serialized, and deserialized. It includes a variety of methods for handling the data, checking equality, and working with external representations such as JSON and files.

Protocol Definition

swiftCopy codepublic protocol CloneableDataType {
    var bindingValue: Any { get set }
    func valueType() -> Any.Type
    func getRawValue() -> Any
    func getTypeAsString() -> String
    func getStringDescription() -> String
    func getJXValue(context: JXContext) -> JXValue?
    func isEqual(to other: CloneableDataType) -> Bool
    init(fileURL: URL, displayName: String) throws
    init(json: String) throws // throws CloneableDataError.invalidValue
    func getJSONValue() -> String
    func saveToFile(fileURL: URL) throws
    func getData() -> Data?
}

Initialization

Each supported data type can implement it's own initialization based on the implementation requirements.

For example a CloneableString is initialized as:

let cloneableString = CloneableString("Hello world")

Whereas CloneableLocation is a bit more complex

let cloneableLocation = CloneableLocation( 
    latitude: 37.7749,              // Required, sample latitude
    longitude: -122.4194,           // Required, sample longitude
    altitude: 30.0,                 // Optional, providing a sample altitude
)

Methods

  • valueType(): Returns the type of the underlying value.

  • getRawValue(): Retrieves the raw value.

  • getTypeAsString(): Provides the type of the value as a String.

  • getStringDescription(): Returns a string description of the value.

  • getJXValue(context:): Returns a JXValue representation of the value, based on the provided context.

  • isEqual(to:): Determines if the current instance is equal to another instance of CloneableDataType.

  • Initializers:

    • init(fileURL:displayName:): Initializes an instance from a file.

    • init(json:): Initializes an instance from a JSON string.

  • getJSONValue(): Serializes the value to a JSON string.

  • saveToFile(fileURL:): Saves the value to a file.

  • getData(): Returns a Data representation of the value, if supported.

Default Implementations with Extensions

The CloneableDataType protocol provides default implementations for some of its methods. This allows you to implement them, only when needed. Below are default implementations for three methods: init(fileURL:displayName:), saveToFile(fileURL:), and getData().

Extension for Default Implementations

swiftCopy codeextension CloneableDataType {
    /// Initializes an instance with a file URL and display name.
    /// By default, this initializer throws an error, indicating that the method needs an explicit implementation if required.
    public init(fileURL: URL, displayName: String) throws {
        throw CloneableDataError.implementationMissing
    }
    
    /// Saves the instance to a file at the specified URL.
    /// By default, this method throws an error, signifying an issue with saving to file.
    /// Implement this method in conforming types where file saving functionality is needed.
    public func saveToFile(fileURL: URL) throws {
        throw CloneableDataError.errorSavingToFile
    }
    
    /// Returns a Data representation of the instance.
    /// By default, this method returns nil, indicating no default Data representation is available.
    /// Override this method in conforming types that can be represented as Data.
    public func getData() -> Data? {
        return nil
    }
}

Description of Default Implementations

  1. Initialization from File (init(fileURL:displayName:)):

    • This initializer is a common requirement for types that need to be created from a file.

    • The default implementation throws CloneableDataError.implementationMissing, indicating that the conforming type must provide a specific implementation if this functionality is required.

  2. Save to File (saveToFile(fileURL:)):

    • This method is essential for persisting data to files.

    • The default implementation throws CloneableDataError.errorSavingToFile, signaling that an explicit implementation is necessary for types where file saving is needed.

  3. Data Representation (getData()):

    • Provides a way to get a Data representation of the conforming type.

    • The default implementation returns nil, suitable for types that do not have a straightforward Data representation. Types that can be represented as Data should override this method.

By using these default implementations, types conforming to CloneableDataType can avoid boilerplate code for common or optional functionality, focusing only on the specific implementations they require.

Example Implementation for String

Here's an example of how the CloneableDataType protocol could be implemented for a String:

CloneableString: CloneableDataType, Codable {
    private var value: String

    // MARK: - Initializers

    /// Initializes the CloneableString with a JSON string.
    public init(json: String) {
        self.value = json
    }

    /// Initializes the CloneableString with a string value.
    public init(_ value: String) {
        self.value = value
    }

    // MARK: - CloneableDataType Conformance

    /// Returns the string value as JSON.
    public func getJSONValue() -> String {
        return value
    }

    /// Converts the string to a JXValue based on the given context.
    public func getJXValue(context: JXContext) -> JXValue? {
        let stringValue = getRawValue() as! String
        return stringValue.toJX(in: context)
    }

    /// Dynamic binding value getter and setter.
    public var bindingValue: Any {
        get { return value }
        set { value = newValue as! String }
    }

    /// Returns the type of the underlying value, which is String.
    public func valueType() -> Any.Type {
        return String.self
    }

    /// Gets the raw value of the string.
    public func getRawValue() -> Any {
        return value
    }

    /// Returns a string representing the type.
    public func getTypeAsString() -> String {
        return "string"
    }

    /// Returns a description of the string.
    public func getStringDescription() -> String {
        return value
    }

    /// Compares two CloneableDataType objects for equality.
    public func isEqual(to other: CloneableDataType) -> Bool {
        if let otherString = other as? CloneableString {
            return self.value == otherString.value
        }
        return false
    }
}

Last updated