Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom RawRepresentable support #10

Merged
merged 5 commits into from
May 27, 2021
Merged

Conversation

basememara
Copy link
Collaborator

I ran into a situation where the compiler allowed me to implement a custom RawRepresentable type but crashed at runtime:

struct TestCustomRepresented: RawRepresentable, UserDefaultsSerializable {
    var rawValue: [String: TestFruit]

    init(rawValue: [String: TestFruit]) {
        self.rawValue = rawValue
    }
}

final class TestSettings {
    @WrappedDefaultOptional(keyName: "customRawRepresented", userDefaults: store)
    var customRawRepresented: TestCustomRepresented?
}

When I ran this and tried saving a value to it, I got this crash:

settings.customRawRepresented = TestCustomRepresented(rawValue: ["key1": .apple])

// =>

libc++abi: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object {
    key1 = "FoilTests.TestFruit.apple";
} for key customRawRepresented'
terminating with uncaught exception of type NSException

What I ended up doing is fine-tuning the conditional bounds for Self.RawValue conformance of RawRepresentable and adjusting the implementation:

// From this:
extension UserDefaultsSerializable where Self: RawRepresentable {
    public var storedValue: RawValue { self.rawValue }

    public init(storedValue: RawValue) {
        self = Self(rawValue: storedValue)!
    }
}

// To this:
extension UserDefaultsSerializable where Self: RawRepresentable, Self.RawValue: UserDefaultsSerializable {
    public var storedValue: RawValue.StoredValue { self.rawValue.storedValue }

    public init(storedValue: RawValue.StoredValue) {
        self = Self(rawValue: Self.RawValue(storedValue: storedValue))!
    }
}

This makes sense to leverage RawRepresentable this way since according to the docs, RawRepresentable was designed to interoperate with legacy systems, which UserDefaults could be considered:

Using the raw value of a conforming type streamlines interoperation with Objective-C and legacy APIs..

Doing this opened some interesting doors to UserDefaults while allowing existing RawRepresentable conformers to remain unchanged:

struct TestCustomRepresented: RawRepresentable, UserDefaultsSerializable {
    var rawValue: [String: TestFruit]

    init(rawValue: [String: TestFruit]) {
        self.rawValue = rawValue
    }
}

extension TestCustomRepresented {
    enum Key: String {
        case key1, key2, key3
    }

    subscript(_ key: Key) -> TestFruit? {
        get { rawValue[key.rawValue] }
        set { rawValue[key.rawValue] = newValue }
    }
}

settings.customRawRepresented[.key2] = .orange

@jessesquires jessesquires added this to the 1.1.0 milestone May 27, 2021
@jessesquires
Copy link
Owner

This is great 💯 🎉 Nice find @basememara!

Copy link
Owner

@jessesquires jessesquires left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes to add a changelog entry for this.

I made some updates in #11, so you'll want to rebase on main first and then just follow the pattern in the changelog. 😊

@basememara
Copy link
Collaborator Author

Cool updated the changelog, let me know if you need any other changes

Copy link
Owner

@jessesquires jessesquires left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@jessesquires jessesquires merged commit 798a0ff into main May 27, 2021
@jessesquires jessesquires deleted the feature/raw-representable branch May 27, 2021 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants