CollectionService Tagging API should be part of the Instance class

The Problem

The current design of the CollectionService’s Tag API is a bit verbose when it comes to applying tags.
Consider the following example:

if not CollectionService:HasTag(instance, "SomeTagName") then
	CollectionService:AddTag(instance, "SomeTagName")
end

for _,tag in pairs(CollectionService:GetTags(instance)) do
	print(tag)
end

if CollectionService:HasTag(instance, "SomeTagName") then
	CollectionService:RemoveTag(instance, "SomeTagName")
end

As you can see, CollectionService is kind of awkwardly wedged into every single query and operation here, when ultimately its just wrapping around one instance in this context.


Proposed Solution

To make things less verbose and cleaner to read, it would be handy if the APIs were instead part of the Instance class itself:

if not instance:HasTag("SomeTag") then
	instance:AddTag("SomeTag")
end

for _,tag in pairs(instance:GetTags()) do
	print(tag)
end

if instance:HasTag("SomeTag") then
	instance:RemoveTag("SomeTag")
end

Backwards Compatibility Rationale

This change can be made without disrupting the existing API. Although its not a common practice, it is possible for API members on Roblox to “override” a member of its base classes.

Some examples of this include:

Function void Model:BreakJoints() [Virtual]
Function void Workspace:BreakJoints(Objects objects) {PluginSecurity} [Override]

The Workspace class is a model, and Workspace:BreakJoints overrides Model:BreakJoints

Event Instance.Changed(string property) [Virtual]
Event ValueBase.Changed(Variant value) [Override]

Each Changed event defined in the derived classes of ValueBase override Instance.Changed.


In practice, we would see the following “override” behavior introduced with this API change:

Function void Instance:AddTag(string tag) [Virtual]
Function void CollectionService:AddTag(Instance instance, string tag) [Override]

Function Array Instance:GetTags() [Virtual]
Function Array CollectionService:GetTags(Instance instance) [Override]

Function bool Instance:HasTag(string tag) [Virtual]
Function bool CollectionService:HasTag(Instance instance, string tag) [Override]

Function void Instance:RemoveTag(string tag) [Virtual]
Function void CollectionService:RemoveTag(Instance instance, string tag) [Override]

The tag functions that exist under the CollectionService would remain and override the new functions. As a side effect, managing tags on the CollectionService object itself would require you to pass it as an argument to these functions:

CollectionService:AddTag(CollectionService, "Tag")
CollectionService:RemoveTag(CollectionService, "Tag")

But this is what would be done anyway if people are (for whatever reason) adding tags to the CollectionService today, so this isn’t a breaking change.

Function Objects CollectionService:GetTagged(string tag)
Function RBXScriptSignal CollectionService:GetInstanceAddedSignal(string tag)
Function RBXScriptSignal CollectionService:GetInstanceRemovedSignal(string tag)

The CollectionService would still serve as a hub for querying objects that have a tag as well as listening for when tags are added and removed. It would just be more compact to read and write code for managing an individual object’s tags.


I hope this change will be considered. Thank you for reading!

61 Likes

I love this idea! Hate having to call GetService for a feature that’s mainly focused on instances themselves, not the service.

This would also be nice to go hand-in-hand when Attributes are finished.

8 Likes

Love the idea of this. I also feel like it would be useful to have a built-in CollectionService widget, to more easily edit and view tags.

5 Likes

As another useful function that could be added, Instance:GetTagged(tag) could act just like CollectionService:GetTagged except that it only returns descendants of itself.

I mentioned this at the bottom of a feature request here:

4 Likes