# :hammer_and_wrench: Pref Types

Unity’s built-in `PlayerPrefs` and `EditorPrefs` are limited to just three data types: `int`, `float`, and `string`. That works for simple use cases — but as your project grows, that limitation quickly becomes painful.

EazyPrefs expands on this with a wide range of **built-in types**, giving you the ability to store data like `Vector3`, `Color`, `DateTime`, enums, even complex objects — all without needing to manually serialize or deserialize anything.

On top of that, EazyPrefs is fully **extensible**. You can define your own pref types, with custom serialization, validation, and helper methods, tailored to your project’s needs.

---

## :toolbox: Supported Built-in Types

Out of the box, EazyPrefs supports the following pref types:

- `int`, `uint`, `float`, `double`, `short`, `ushort`, `long`, `ulong`,
- `bool`, `string`, `char`, `byte`
- `Enum<T>`
- `DateTime`, `TimeSpan`
- `Vector2`, `Vector2Int`, `Vector3`, `Vector3Int`, `Vector4`, `Quaternion`
- `Color`
- `JSONObj<T>` — for storing complex classes and collections

!!!success
You can use Arrays, Lists, Dictionaries or any other collections you want, using the `JSONObjPref<T>` pref type.
!!!

You can use these with either **PlayerPrefs** or **EditorPrefs**, and they work with both static and instance-based access.

---

## :recycle: How It Works

Internally, all EazyPrefs types extend a common `Pref<T>` base class. However, Most advanced types use the `StringBasedPref<T>` class — which adds support for **encryption**, **compression**, and is based on **string serialization**.

For structured data (e.g. classes, dictionaries), EazyPrefs uses `JSONObjPref<T>`, which wraps around `StringBasedPref<T>` and uses **JSON.NET** for serialization.

---

## :sparkles: Creating Your Own Pref Type

EazyPrefs supports custom pref types of any class. Assume we have a class `UserData` we want to turn into a pref type
```csharp
[Serializable]
public class UserData
{
    public string name;
    public int coins;
    public Difficulty selectedDifficulty;
}

public enum Difficulty
{
    Easy,
    Medium,
    Hard
}
```

!!!warning
Make sure to always use the `[Serializable]` attribute on classes you use with EazyPrefs.
!!!

There are two recommended ways to implement a custom pref type:

---

### Option 1: Extend `JSONObjPref<T>`

Use this if you want a **custom strongly-typed wrapper** around a class or struct, and you're fine using built-in JSON serialization.

```csharp
[PrefTypeName("UserData")]
[PrefTypeSerializationAlias("UserData")]
public class UserDataPref : JSONObjPref<UserData>
{
    public UserDataPref(IPrefStorageSource source) : base(source) { }

    // Optional: Add custom methods, logic, or defaults here
}
```

This avoids repeating new `JSONObjPref<UserData>(...)` everywhere and keeps your codebase cleaner.

### Option 2: Extend `StringBasedPref<T>`

Use this if you want **full control over serialization**, for performance, optimization, or compatibility with a non-JSON format.

```csharp
[PrefTypeName("UserData")]
[PrefTypeSerializationAlias("UserData")]
public class UserDataPref : StringBasedPref<UserData>
{
    public UserDataPref(IPrefStorageSource source) : base(source) { }

    protected override string ConvertValueToString(UserData value)
    {
        // Store as a delimited string: name|coins|difficulty
        return $"{value.name}|{value.coins}|{(int)value.selectedDifficulty}";
    }

    protected override UserData ConvertStringToValue(string valueStr)
    {
        var parts = valueStr.Split('|');
        return new UserData
        {
            name = parts[0],
            coins = int.Parse(parts[1]),
            selectedDifficulty = (Difficulty)int.Parse(parts[2])
        };
    }
}
```

!!!
This is the preferred approach if you want to optimize how data is stored and retrieved — for example, using delimited strings instead of JSON, or storing binary-like formats as Base64.
!!!

#### :memo: What Do These Methods Do?
When creating a custom `StringBasedPref<T>`, you're responsible for defining how the value is converted to/from a string. This is done by implementing two required methods:
- `ConvertValueToString(TValue value)`
This method should convert your data into a string representation. This string will then be optionally compressed, encrypted, and stored.
- `ConvertStringToValue(string valueStr)`
This method takes the stored string (after it has been decrypted and decompressed, if applicable) and turns it back into the object you're working with.

Use these methods to define how your type is represented internally — whether it’s JSON, a CSV-style string, XML, or something else entirely.

#### :label: PrefTypeName Attribute
When creating your own custom pref types, you can optionally decorate the class with a `PrefTypeName` attribute:
```csharp
[PrefTypeName("UserData")]
public class UserDataPref : JSONObjPref<UserData>
{
    public UserDataPref(IPrefStorageSource source) : base(source) { }
}
```

This attribute provides a **friendlier, cleaner name** for your pref type, which is especially useful in the **EazyPrefs Editor UI**. If the attribute is not used, EazyPrefs will fall back to using the **full type name** (e.g., `UserDataPref`), which might be unnecessarily verbose or less readable in some cases.

!!!warning
Make sure not to have more than one pref classes share the same name, as it might lead to unexpected behaviour, or some prefs classes being ignored
!!!

#### :label: PrefTypeSerializationAlias Attribute

When a pref is saved, EazyPrefs includes metadata about the pref's type so that it can be properly identified and rendered in the editor later.  
By default, this type info uses the full assembly-qualified class name — which can be **long**, **hard to read**, and **brittle** if the class is ever renamed or moved.

To solve this, you can use the `PrefTypeSerializationAliasAttribute` to assign a **short and stable alias** to your pref class:
```csharp
[PrefTypeSerializationAlias("UserData")]
public class UserDataPref : JSONObjPref<UserData>
{
    public UserDataPref(IPrefStorageSource source) : base(source) { }
}
```

This alias will be stored with the pref metadata instead of the full type name.

!!!warning
Make sure not to have more than one pref classes share the same alias, as it might lead to unexpected behaviour, or some prefs classes being correctly read/written.
!!!

:bulb: **Why Use It?**
- Makes the serialized data shorter and more human-readable
- Prevents issues if you rename or move your pref class
- Ensures the editor can always recognize and draw the pref correctly, as long as the alias stays the same

!!!success
Using a serialization alias is the **recommended approach** when creating custom pref types — especially if you're shipping tools, plugins, or working in larger codebases. 
!!!

!!!warning 
If you change the alias after a pref has been saved, the editor will no longer be able to associate it with the correct drawer or type.
Always treat aliases as stable identifiers. 
!!!

---

!!!danger Avoid renaming pref classes without a serialization alias
If you **rename or move a pref class** and haven't assigned a stable serialization alias, the editor may no longer recognize the pref type.  
This can result in fallback visuals, incorrect drawers, or warnings in the editor.

Accessing the pref from **code will still work**, since it relies only on the key — but the **editor experience may break**.

To prevent this, you should:

- Use the `PrefTypeSerializationAlias` attribute to assign a stable alias to your pref class (recommended)
- Keep the alias consistent even if the class name changes
- Avoid depending on the full class name as an identifier
!!!

---

### Using the new pref

With both options, you can now use your custom pref type like this:
```csharp
var source = new PlayerPrefStorageSource("UserData");
var userDataPref = new UserDataPref(source);

// We create a temporary UserData object for demonstration purposes.
// You could just pass your own reference directly (if you have one)
var userData = new UserData
{
    name = "Alex",
    coins = 200,
    selectedDifficulty = Difficulty.Medium
}

// Set the value
userDataPref.SetValue(userData);

// Use this when you want to retrieve the last stored value.
UserData data = userDataPref.GetValue();
```

!!!
Use JSONObjPref<T> for simplicity and flexibility.
Use StringBasedPref<T> if you want performance, minimal storage size, or a custom string format.
!!!

### :seedling: Overriding the Default Value

Another virtual method you can override in both `StringBasedPref<T>` and `JSONObjPref<T>` is:

```csharp
public virtual TValue GetDefaultValue() => default;
```

This method defines the fallback value returned when:

- The pref is not yet set
- The stored data is corrupted
- Deserialization fails
- You explicitly reset the pref

By default, this returns the standard .NET default for your type (`null` for reference types). You can override it to return something more meaningful to your use case.

```csharp
public override UserData GetDefaultValue()
{
    return new UserData
    {
        name = "Guest",
        coins = 0,
        selectedDifficulty = Difficulty.Easy
    };
}
```

### :wrench: Advanced: Custom JSON Converters
You can also optionally override the `GetJSONConverter()` method:
```csharp
protected override JsonConverter<UserData> GetJSONConverter()
{
    return new MyCustomUserDataConverter();
}
```

This allows you to plug in a custom `JsonConverter<T>` for full control over how your type is serialized and deserialized through JSON.NET. This is also useful for value types that cannot be correctly serialized/deserialized by default (like some Unity types like `Color`)

You can override this in either `JSONObjPref<T>` or your own `StringBasedPref<T>` implementation — useful if you're mixing default serialization with something more custom.

[!ref JSON.NET docs on JsonConverter<T>](https://www.newtonsoft.com/json/help/html/CustomJsonConverterGeneric.htm)

---
