Skip to content

v0.1.9 - Entity hierarchy restructure, bug fixes, and dependency updates

Compare
Choose a tag to compare
@vestrel00 vestrel00 released this 18 Dec 01:34
· 347 commits to main since this release

There are a lot of changes in this release that I have put a lot of hours into 😅 All of those hours are worth it, because this library is now so much better because of it 😁. I am very excited to share this new version of Contacts, Reborn to the community! Please take a look, try it out, and let me know what ya think ❤️

Depending on how extensively you have integrated this library into your app, you may be affected be several breaking changes. Don't worry! They are easy to fix and I'll guide you. Just read the Migration guide section at the bottom of this release notes and you will be okay 🤞

Bugfixes

  1. #129 Photo thumbnail data is unintentionally included in all query results
    • This might have been negatively affecting your app's performance so be sure to adopt this new release if your apps shows all contacts without pagination.
  2. #135 Inserting or updating an event date to a single digit month or single digit day of month results in duplicate events
  3. #130 RawContactRefresh, DataRawContact, and BlankRawContactToRawContact extensions could return incorrect RawContact
  4. #131 DataRefresh extensions could return incorrect data
  5. #109 DataRefresh extension function throws exception for subclasses of MutableCommonDataEntity

Improvements and breaking changes

  1. #113 Fix hierarchy of CommonDataEntity
  2. #123 Use sealed interfaces!
  3. #115 Create an interface for creating a mutable copy of immutable entities
  4. #128 Revamped Entity hierarchy!
  5. #117 Create interface for "new entities" for insert APIs and remove nullable ID properties
  6. #134 Restructured all entities to either be "Existing" or "New"

Please keep in mind that releases below v1.0.0 may contain breaking changes!!! Anything below the semantic version v1.0.0 are implicitly experimental/non-stable. Backwards compatibility and deprecations will not be exercised until after v1.0.0 (the first true production release version) in order to keep the library clean and easy to change 👍

Dependency updates

I don't think these dependency bumps will affect you but let me know if it does!

  1. #121 Upgrade Kotlin to 1.6.0 (requires a bunch of other upgrades)
  2. #122 Remove explicit dependency on the Kotlin stdlib

New Contributors!

We got some new people in the house! 😍

  • @DriblingTrex made their first contribution in #127
    • Unfortunately, the GitHub account is not showing up in the Contributors list because the commits are not linked to the GitHub account 😭
  • @lau1944 created a very important issue; #116. It posed very important questions about the API design. Thank you for raising questions and even making suggestions. Because of you, this release contained a bunch of API improvements. Hence, the title of this release "Entity hierarchy restructure" 🔥
    • I still consider you a contributor even if you did not add code! Maybe, I should install the all-contributors bot; https://allcontributors.org...

Full Changelog

0.1.8...0.1.9

Want to discuss this release?

Head on over to the v0.1.9 Release Checklist and leave a comment!

Migration guide (from v0.1.8 to v0.1.9)

This should cover everything that could affect you. Let me know if I missed anything! If you don't care to look at the before-and-after, then go straight into the howto pages. I also made sure to update all documentation in code 😃

I. Entity and interfaces or abstract classes that inherit from it are now sealed except for custom data.

This means that you are no longer able to define your own entities, except for inheritors of CustomDataEntity.

II. Mutable entities have been split to either be "existing" or "new".

Previously, a mutable entity could represent both an "existing" entity (already inserted into the database and has non-null ID property values) or a "new" entity (not yet inserted into the database and has null ID property values). For example,

val mutableRawContact: MutableRawContact
val mutableEmail: MutableEmail

Now, a mutable entity can only represent either an "existing" or "new" entity...

val existingMutableRawContact: MutableRawContact
val existingMutableEmail: MutableEmail

val newMutableRawContact: NewRawContact
val newMutableEmail: NewEmail

The rest of the migration guide is directly related to this change.

III. Existing entities now have non-null ID properties.

You no longer have to worry about ID properties being null for existing entities.

Previously...

Contact {
    val id: Long?
}
RawContact {
    val id: Long?
    val contactId: Long?
}
Email {
    val id: Long?
    val rawContactId: Long?
    val contactId: Long?
}

Now...

Contact {
    val id: Long
}
RawContact {
    val id: Long
    val contactId: Long
}
Email {
    val id: Long
    val rawContactId: Long
    val contactId: Long
}

"New" entities do not have ID properties at all as they have not yet been inserted into the database.

IV. Getting instances of mutable entities have changed.

Previously...

val contact: Contact
val email: Email

val mutableContact: MutableContact = contact.toMutableContact()
val mutableEmail: MutableEmail = email.toMutableEmail()

Now...

val mutableContact: MutableContact = contact.mutableCopy()
val mutableEmail: MutableEmail = email.mutableCopy()

The toMutableXXX functions have been replaced with a generic mutableCopy function that is implemented by all inheritors of ImmutableEntityWithMutableType. I also added some more syntactic sugar for Kotlin users!

val mutableEmail = email.mutableCopy {
    address = "[email protected]"
}

Furthermore, you are no longer able to construct instances of existing mutable entities via constructors. This is done to ensure that existing entities can only come from the library APIs.

Yes, I am aware that data class provides a copy function that you can use to hack around this but I strongly discourage that. Read the "Creating Entities & data class" in the DEV_NOTES.md if you want to learn more about this.

V. Update and delete APIs now only accept existing entities.

This makes more sense now doesn't it? You can only update or delete something that already exists in the database.

✅ This will still work...

val existingMutableRawContact: MutableRawContact
Contacts(context).update().rawContacts(existingMutableRawContact).commit()
Contacts(context).delete().rawContacts(existingMutableRawContact).commit()

❌ This will not work (compile-time error)...

val newMutableRawContact: NewRawContact
Contacts(context).update().rawContacts(newMutableRawContact).commit()
Contacts(context).delete().rawContacts(newMutableRawContact).commit()

This applies to all update and delete APIs such as Update, ProfileUpdate, DataUpdate, GroupsUpdate, Delete, ProfileDelete, DataDelete, GroupsDelete,...

VI. Insert APIs now only accept new entities.

Inserting already "existing"/inserted entities doesn't really make sense. The only thing that will happen is that a duplicate will be created, which may or may not be what you intend to do.

❌ Anyways, this will no longer work (compile-time error)...

val existingMutableRawContact: MutableRawContact
Contacts(context).insert().rawContacts(existingMutableRawContact).commit()

✅ This will work...

val newMutableRawContact: NewRawContact
Contacts(context).insert().rawContacts(newMutableRawContact).commit()

This applies to all insert APIs such as Insert, ProfileInsert, GroupsInsert,...

VII. Extension functions in the util package are now only usable for existing entities.

Extension functions in the util package typically only work for existing entities (already inserted into the database- non-null ID property values). Prior to this release, those extension functions were also usable by "new" entities (not inserted into the database- null ID property values).

This led to consumers making incorrect, but valid assumptions. For example, in #116 Set photo did not save to system file, the consumer thought that this insert call would also set the photo...

Contacts(context)
            .insert()
            .rawContact {
                ...
                setPhoto(context, photoBitmap)
            }
            .commit()

It would make a lot of sense if it did. This should actually work and it will be supported in #119.

However, the above code does not actually set the photo because the RawContact in scope of the rawContact block has not yet been inserted, which means it does not have a non-null ID property value. So, setPhoto does nothing and fails.

Now, the setPhoto util extension function is only usable for existing Contacts or RawContacts. It will not accept new entities. The above code will not even compile! This avoids a lot of confusion 😁