iOS tv-casting-app: simplified command/attribute APIs (#31640)

* iOS tv-casting-app: simplified command/attribute APIs
diff --git a/examples/tv-casting-app/APIs.md b/examples/tv-casting-app/APIs.md
index 4afb4f6..f0fdb32 100644
--- a/examples/tv-casting-app/APIs.md
+++ b/examples/tv-casting-app/APIs.md
@@ -773,7 +773,8 @@
 
 ### Select an Endpoint on the Casting Player
 
-_{Complete Endpoint selection examples: [Linux](linux/simple-app-helper.cpp)}_
+_{Complete Endpoint selection examples: [Linux](linux/simple-app-helper.cpp) |
+[iOS](darwin/TvCasting/TvCasting/MCContentLauncherLaunchURLExampleViewModel.swift)}_
 
 On a successful connection with a `CastingPlayer`, a Casting Client may select
 one of the Endpoints to interact with based on its attributes (e.g. Vendor ID,
@@ -799,6 +800,19 @@
 }
 ```
 
+On iOS, it can select an `MCEndpoint` similarly and as shown below.
+
+```swift
+// VendorId of the MCEndpoint on the MCCastingPlayer that the MCCastingApp desires to interact with after connection
+let sampleEndpointVid: Int = 65521
+...
+// select the MCEndpoint on the MCCastingPlayer to invoke the command on
+if let endpoint: MCEndpoint = castingPlayer.endpoints().filter({ $0.vendorId().intValue == sampleEndpointVid}).first
+{
+...
+}
+```
+
 ## Interacting with a Casting Endpoint
 
 Once the Casting Client has selected an `Endpoint`, it is ready to
@@ -819,13 +833,14 @@
 
 ### Issuing Commands
 
-_{Complete Command invocation examples: [Linux](linux/simple-app-helper.cpp)}_
+_{Complete Command invocation examples: [Linux](linux/simple-app-helper.cpp) |
+[iOS](darwin/TvCasting/TvCasting/MCContentLauncherLaunchURLExampleViewModel.swift)}_
 
-The Casting Client can get a reference to a `endpoint` on a `CastingPlayer`,
+The Casting Client can get a reference to an `Endpoint` on a `CastingPlayer`,
 check if it supports the required cluster/command, and send commands to it. It
 can then handle any command response / error the `CastingPlayer` sends back.
 
-On Linux, for example, given an `endpoint`, it can send a `LaunchURL` command
+On Linux, for example, given an `Endpoint`, it can send a `LaunchURL` command
 (part of the Content Launcher cluster) by calling the `Invoke` API on a
 `Command` of type
 `matter::casting::core::Command<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type>`
@@ -865,9 +880,65 @@
 }
 ```
 
+On iOS, given an `MCEndpoint` endpoint, it can send a `LaunchURL` command (part
+of the Content Launcher cluster) by calling the `invoke` API on a
+`MCContentLauncherClusterLaunchURLCommand`
+
+```swift
+// validate that the selected endpoint supports the ContentLauncher cluster
+if(!endpoint.hasCluster(MCEndpointClusterTypeContentLauncher))
+{
+    self.Log.error("No ContentLauncher cluster supporting endpoint found")
+    DispatchQueue.main.async
+    {
+        self.status = "No ContentLauncher cluster supporting endpoint found"
+    }
+    return
+}
+
+// get ContentLauncher cluster from the endpoint
+let contentLaunchercluster: MCContentLauncherCluster = endpoint.cluster(for: MCEndpointClusterTypeContentLauncher) as! MCContentLauncherCluster
+
+// get the launchURLCommand from the contentLauncherCluster
+let launchURLCommand: MCContentLauncherClusterLaunchURLCommand? = contentLaunchercluster.launchURLCommand()
+if(launchURLCommand == nil)
+{
+    self.Log.error("LaunchURL not supported on cluster")
+    DispatchQueue.main.async
+    {
+        self.status = "LaunchURL not supported on cluster"
+    }
+    return
+}
+
+// create the LaunchURL request
+let request: MCContentLauncherClusterLaunchURLRequest = MCContentLauncherClusterLaunchURLRequest()
+request.contentURL = contentUrl
+request.displayString = displayString
+
+// call invoke on launchURLCommand while passing in a completion block
+launchURLCommand!.invoke(request, context: nil, completion: { context, err, response in
+    DispatchQueue.main.async
+    {
+        if(err == nil)
+        {
+            self.Log.info("LaunchURLCommand invoke completion success with \(String(describing: response))")
+            self.status = "Success. Response data: \(String(describing: response?.data))"
+        }
+        else
+        {
+            self.Log.error("LaunchURLCommand invoke completion failure with \(String(describing: err))")
+            self.status = "Failure: \(String(describing: err))"
+        }
+    }
+},
+timedInvokeTimeoutMs: 5000) // time out after 5000ms
+```
+
 ### Read Operations
 
-_{Complete Attribute Read examples: [Linux](linux/simple-app-helper.cpp)}_
+_{Complete Attribute Read examples: [Linux](linux/simple-app-helper.cpp) |
+[iOS](darwin/TvCasting/TvCasting/MCApplicationBasicReadVendorIDExampleViewModel.swift)}_
 
 The `CastingClient` may read an Attribute from the `Endpoint` on the
 `CastingPlayer`. It should ensure that the desired cluster / attribute are
@@ -914,11 +985,69 @@
 }
 ```
 
+On iOS, given a `MCEndpoint`, the `VendorID` can be read similarly, by calling
+the `read` API on the `MCApplicationBasicClusterVendorIDAttribute`
+
+```swift
+// validate that the selected endpoint supports the ApplicationBasic cluster
+if(!endpoint.hasCluster(MCEndpointClusterTypeApplicationBasic))
+{
+    self.Log.error("No ApplicationBasic cluster supporting endpoint found")
+    DispatchQueue.main.async
+    {
+        self.status = "No ApplicationBasic cluster supporting endpoint found"
+    }
+    return
+}
+
+// get ApplicationBasic cluster from the endpoint
+let applicationBasiccluster: MCApplicationBasicCluster = endpoint.cluster(for: MCEndpointClusterTypeApplicationBasic) as! MCApplicationBasicCluster
+
+// get the vendorIDAttribute from the applicationBasiccluster
+let vendorIDAttribute: MCApplicationBasicClusterVendorIDAttribute? = applicationBasiccluster.vendorIDAttribute()
+if(vendorIDAttribute == nil)
+{
+    self.Log.error("VendorID attribute not supported on cluster")
+    DispatchQueue.main.async
+    {
+        self.status = "VendorID attribute not supported on cluster"
+    }
+    return
+}
+
+// call read on vendorIDAttribute and pass in a completion block
+vendorIDAttribute!.read(nil) { context, before, after, err in
+    DispatchQueue.main.async
+    {
+        if(err != nil)
+        {
+            self.Log.error("Error when reading VendorID value \(String(describing: err))")
+            self.status = "Error when reading VendorID value \(String(describing: err))"
+            return
+        }
+
+        if(before != nil)
+        {
+            self.Log.info("Read VendorID value: \(String(describing: after)) Before: \(String(describing: before))")
+            self.status = "Read VendorID value: \(String(describing: after)) Before: \(String(describing: before))"
+        }
+        else
+        {
+            self.Log.info("Read VendorID value: \(String(describing: after))")
+            self.status = "Read VendorID value: \(String(describing: after))"
+        }
+    }
+}
+```
+
 ### Subscriptions
 
 _{Complete Attribute subscription examples:
 [Linux](linux/simple-app-helper.cpp)}_
 
+_{Complete Attribute Read examples: [Linux](linux/simple-app-helper.cpp) |
+[iOS](darwin/TvCasting/TvCasting/MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift)}_
+
 A Casting Client may subscribe to an attribute on an `Endpoint` of the
 `CastingPlayer` to get data reports when the attributes change.
 
@@ -966,7 +1095,69 @@
 }
 ```
 
+On iOS, given a `MCEndpoint`, `CurrentState` can be subscribed to by calling the
+`subscribe` API on the it can subscribe to the `CurrentState` (part of the Media
+Playback Basic cluster) by calling the `Subscribe` API on the
+`MCMediaPlaybackClusterCurrentStateAttribute`
+
+```swift
+// validate that the selected endpoint supports the MediaPlayback cluster
+if(!endpoint.hasCluster(MCEndpointClusterTypeMediaPlayback))
+{
+    self.Log.error("No MediaPlayback cluster supporting endpoint found")
+    DispatchQueue.main.async
+    {
+        self.status = "No MediaPlayback cluster supporting endpoint found"
+    }
+    return
+}
+
+// get MediaPlayback cluster from the endpoint
+let mediaPlaybackCluster: MCMediaPlaybackCluster = endpoint.cluster(for: MCEndpointClusterTypeMediaPlayback) as! MCMediaPlaybackCluster
+
+// get the currentStateAttribute from the mediaPlaybackCluster
+let currentStateAttribute: MCMediaPlaybackClusterCurrentStateAttribute? = mediaPlaybackCluster.currentStateAttribute()
+if(currentStateAttribute == nil)
+{
+    self.Log.error("CurrentState attribute not supported on cluster")
+    DispatchQueue.main.async
+    {
+        self.status = "CurrentState attribute not supported on cluster"
+    }
+    return
+}
+
+// call read on currentStateAttribute and pass in a completion block
+currentStateAttribute!.read(nil) { context, before, after, err in
+
+    let dateFormatter = DateFormatter()
+    dateFormatter.dateFormat = "HH:mm:ss"
+    let currentTime = dateFormatter.string(from: Date())
+    DispatchQueue.main.async
+    {
+        if(err != nil)
+        {
+            self.Log.error("Error when reading CurrentState value \(String(describing: err)) at \(currentTime)")
+            self.status = "Error when reading CurrentState value \(String(describing: err)) at \(currentTime)"
+            return
+        }
+        if(before != nil)
+        {
+            self.Log.info("Read CurrentState value: \(String(describing: after)) Before: \(String(describing: before)) at \(currentTime)")
+            self.status = "Read CurrentState value: \(String(describing: after)) Before: \(String(describing: before)) at \(currentTime)"
+        }
+        else
+        {
+            self.Log.info("Read CurrentState value: \(String(describing: after)) at \(currentTime)")
+            self.status = "Read CurrentState value: \(String(describing: after)) at \(currentTime)"
+        }
+    }
+}
+```
+
 The Casting client can Shutdown all running Subscriptions by calling the
-`ShutdownAllSubscriptions` API on the `CastingApp`. See API and its
-documentation for [Linux](tv-casting-common/core/CastingApp.h), Android and
-[iOS](darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingApp.h).
+`ShutdownAllSubscriptions` API on the `CastingApp` on Linux/Android and
+`MCCastingApp` on iOS. See API and its documentation for
+[Linux](tv-casting-common/core/CastingApp.h),
+[Android](android/App/app/src/main/jni/com/matter/casting/core/CastingApp.java)
+and [iOS](darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingApp.h).
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/project.pbxproj b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/project.pbxproj
index 8c438ce..8e87fc9 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/project.pbxproj
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/project.pbxproj
@@ -21,6 +21,29 @@
 		3C4E53B028E4F28100F293E8 /* MediaPlaybackTypes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C4E53AF28E4F28100F293E8 /* MediaPlaybackTypes.mm */; };
 		3C4E53B228E5184C00F293E8 /* TargetNavigatorTypes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C4E53B128E5184C00F293E8 /* TargetNavigatorTypes.mm */; };
 		3C4E53B628E5595A00F293E8 /* ContentLauncherTypes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C4E53B528E5595A00F293E8 /* ContentLauncherTypes.mm */; };
+		3C4F52102B4E18C800BB8A10 /* MCEndpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F520F2B4E18C800BB8A10 /* MCEndpoint.h */; };
+		3C4F52122B4E18ED00BB8A10 /* MCEndpoint.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F52112B4E18ED00BB8A10 /* MCEndpoint.mm */; };
+		3C4F52142B4F31DC00BB8A10 /* MCDeviceTypeStruct.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F52132B4F31DC00BB8A10 /* MCDeviceTypeStruct.h */; };
+		3C4F52162B4F31FA00BB8A10 /* MCDeviceTypeStruct.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F52152B4F31FA00BB8A10 /* MCDeviceTypeStruct.m */; };
+		3C4F521C2B4F4B1B00BB8A10 /* MCCastingPlayer_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F521B2B4F4B1B00BB8A10 /* MCCastingPlayer_Internal.h */; };
+		3C4F521E2B4F4B3B00BB8A10 /* MCEndpoint_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F521D2B4F4B3B00BB8A10 /* MCEndpoint_Internal.h */; };
+		3C4F52202B507C4A00BB8A10 /* MCEndpointClusterType.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F521F2B507C4A00BB8A10 /* MCEndpointClusterType.h */; };
+		3C4F52222B507C9300BB8A10 /* MCCluster.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F52212B507C9300BB8A10 /* MCCluster.h */; };
+		3C4F52242B507CA800BB8A10 /* MCCluster.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F52232B507CA800BB8A10 /* MCCluster.mm */; };
+		3C4F52262B50899700BB8A10 /* MCCluster_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F52252B50899700BB8A10 /* MCCluster_Internal.h */; };
+		3C4F52282B51DB3000BB8A10 /* MCCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F52272B51DB3000BB8A10 /* MCCommand.h */; };
+		3C4F522A2B51DFAE00BB8A10 /* MCCommand_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F52292B51DFAE00BB8A10 /* MCCommand_Internal.h */; };
+		3C4F522C2B51E02800BB8A10 /* MCCommand.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F522B2B51E02800BB8A10 /* MCCommand.mm */; };
+		3C4F52342B57338B00BB8A10 /* MCClusterObjects.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C4F52332B57338B00BB8A10 /* MCClusterObjects.h */; };
+		3C4F52362B5733A200BB8A10 /* MCClusterObjects.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F52352B5733A200BB8A10 /* MCClusterObjects.mm */; };
+		3C621CA12B5F6CF8005CDBA3 /* MCCommandObjects.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C621CA02B5F6CF8005CDBA3 /* MCCommandObjects.h */; };
+		3C621CA32B5F6D08005CDBA3 /* MCCommandObjects.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C621CA22B5F6D08005CDBA3 /* MCCommandObjects.mm */; };
+		3C621CA52B605A6A005CDBA3 /* MCAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C621CA42B605A6A005CDBA3 /* MCAttribute.h */; };
+		3C621CA72B605AA1005CDBA3 /* MCObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C621CA62B605AA1005CDBA3 /* MCObserver.h */; };
+		3C621CA92B605C52005CDBA3 /* MCAttribute_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C621CA82B605C52005CDBA3 /* MCAttribute_Internal.h */; };
+		3C621CAB2B605C6E005CDBA3 /* MCAttribute.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C621CAA2B605C6E005CDBA3 /* MCAttribute.mm */; };
+		3C621CAD2B605E94005CDBA3 /* MCAttributeObjects.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C621CAC2B605E94005CDBA3 /* MCAttributeObjects.h */; };
+		3C621CAF2B605EA7005CDBA3 /* MCAttributeObjects.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C621CAE2B605EA7005CDBA3 /* MCAttributeObjects.mm */; };
 		3C66FBFC290327BB00B63FE7 /* AppParameters.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C66FBFB290327BB00B63FE7 /* AppParameters.mm */; };
 		3C6920462AA1093300D0F613 /* MCDeviceAttestationCredentialsProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C6920452AA1093300D0F613 /* MCDeviceAttestationCredentialsProvider.h */; };
 		3C6920482AA1094000D0F613 /* MCDeviceAttestationCredentialsProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C6920472AA1094000D0F613 /* MCDeviceAttestationCredentialsProvider.mm */; };
@@ -76,6 +99,29 @@
 		3C4E53B328E5185F00F293E8 /* TargetNavigatorTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TargetNavigatorTypes.h; sourceTree = "<group>"; };
 		3C4E53B428E5593700F293E8 /* ContentLauncherTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContentLauncherTypes.h; sourceTree = "<group>"; };
 		3C4E53B528E5595A00F293E8 /* ContentLauncherTypes.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ContentLauncherTypes.mm; sourceTree = "<group>"; };
+		3C4F520F2B4E18C800BB8A10 /* MCEndpoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCEndpoint.h; sourceTree = "<group>"; };
+		3C4F52112B4E18ED00BB8A10 /* MCEndpoint.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MCEndpoint.mm; sourceTree = "<group>"; };
+		3C4F52132B4F31DC00BB8A10 /* MCDeviceTypeStruct.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCDeviceTypeStruct.h; sourceTree = "<group>"; };
+		3C4F52152B4F31FA00BB8A10 /* MCDeviceTypeStruct.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MCDeviceTypeStruct.m; sourceTree = "<group>"; };
+		3C4F521B2B4F4B1B00BB8A10 /* MCCastingPlayer_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCCastingPlayer_Internal.h; sourceTree = "<group>"; };
+		3C4F521D2B4F4B3B00BB8A10 /* MCEndpoint_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCEndpoint_Internal.h; sourceTree = "<group>"; };
+		3C4F521F2B507C4A00BB8A10 /* MCEndpointClusterType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCEndpointClusterType.h; sourceTree = "<group>"; };
+		3C4F52212B507C9300BB8A10 /* MCCluster.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCCluster.h; sourceTree = "<group>"; };
+		3C4F52232B507CA800BB8A10 /* MCCluster.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MCCluster.mm; sourceTree = "<group>"; };
+		3C4F52252B50899700BB8A10 /* MCCluster_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCCluster_Internal.h; sourceTree = "<group>"; };
+		3C4F52272B51DB3000BB8A10 /* MCCommand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCCommand.h; sourceTree = "<group>"; };
+		3C4F52292B51DFAE00BB8A10 /* MCCommand_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCCommand_Internal.h; sourceTree = "<group>"; };
+		3C4F522B2B51E02800BB8A10 /* MCCommand.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MCCommand.mm; sourceTree = "<group>"; };
+		3C4F52332B57338B00BB8A10 /* MCClusterObjects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCClusterObjects.h; sourceTree = "<group>"; };
+		3C4F52352B5733A200BB8A10 /* MCClusterObjects.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MCClusterObjects.mm; sourceTree = "<group>"; };
+		3C621CA02B5F6CF8005CDBA3 /* MCCommandObjects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCCommandObjects.h; sourceTree = "<group>"; };
+		3C621CA22B5F6D08005CDBA3 /* MCCommandObjects.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MCCommandObjects.mm; sourceTree = "<group>"; };
+		3C621CA42B605A6A005CDBA3 /* MCAttribute.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCAttribute.h; sourceTree = "<group>"; };
+		3C621CA62B605AA1005CDBA3 /* MCObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCObserver.h; sourceTree = "<group>"; };
+		3C621CA82B605C52005CDBA3 /* MCAttribute_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCAttribute_Internal.h; sourceTree = "<group>"; };
+		3C621CAA2B605C6E005CDBA3 /* MCAttribute.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MCAttribute.mm; sourceTree = "<group>"; };
+		3C621CAC2B605E94005CDBA3 /* MCAttributeObjects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCAttributeObjects.h; sourceTree = "<group>"; usesTabs = 0; };
+		3C621CAE2B605EA7005CDBA3 /* MCAttributeObjects.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MCAttributeObjects.mm; sourceTree = "<group>"; };
 		3C66FBFA2903279A00B63FE7 /* AppParameters.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppParameters.h; sourceTree = "<group>"; };
 		3C66FBFB290327BB00B63FE7 /* AppParameters.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppParameters.mm; sourceTree = "<group>"; };
 		3C6920452AA1093300D0F613 /* MCDeviceAttestationCredentialsProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCDeviceAttestationCredentialsProvider.h; sourceTree = "<group>"; };
@@ -182,7 +228,30 @@
 				3C2346222B362B9500FA276E /* MCCastingPlayerDiscovery.h */,
 				3C2346242B362BBB00FA276E /* MCCastingPlayerDiscovery.mm */,
 				3C2346202B362B4F00FA276E /* MCCastingPlayer.h */,
+				3C4F521B2B4F4B1B00BB8A10 /* MCCastingPlayer_Internal.h */,
 				3C9437882B364F5E0096E5F4 /* MCCastingPlayer.mm */,
+				3C4F520F2B4E18C800BB8A10 /* MCEndpoint.h */,
+				3C4F521D2B4F4B3B00BB8A10 /* MCEndpoint_Internal.h */,
+				3C4F52112B4E18ED00BB8A10 /* MCEndpoint.mm */,
+				3C4F52212B507C9300BB8A10 /* MCCluster.h */,
+				3C4F52252B50899700BB8A10 /* MCCluster_Internal.h */,
+				3C4F52232B507CA800BB8A10 /* MCCluster.mm */,
+				3C4F52272B51DB3000BB8A10 /* MCCommand.h */,
+				3C4F52292B51DFAE00BB8A10 /* MCCommand_Internal.h */,
+				3C4F522B2B51E02800BB8A10 /* MCCommand.mm */,
+				3C621CA62B605AA1005CDBA3 /* MCObserver.h */,
+				3C621CA42B605A6A005CDBA3 /* MCAttribute.h */,
+				3C621CA82B605C52005CDBA3 /* MCAttribute_Internal.h */,
+				3C621CAA2B605C6E005CDBA3 /* MCAttribute.mm */,
+				3C4F52332B57338B00BB8A10 /* MCClusterObjects.h */,
+				3C4F52352B5733A200BB8A10 /* MCClusterObjects.mm */,
+				3C621CA02B5F6CF8005CDBA3 /* MCCommandObjects.h */,
+				3C621CA22B5F6D08005CDBA3 /* MCCommandObjects.mm */,
+				3C621CAC2B605E94005CDBA3 /* MCAttributeObjects.h */,
+				3C621CAE2B605EA7005CDBA3 /* MCAttributeObjects.mm */,
+				3C4F521F2B507C4A00BB8A10 /* MCEndpointClusterType.h */,
+				3C4F52132B4F31DC00BB8A10 /* MCDeviceTypeStruct.h */,
+				3C4F52152B4F31FA00BB8A10 /* MCDeviceTypeStruct.m */,
 				3C0474052B3F7E5F0012AE95 /* MCEndpointFilter.h */,
 				3C2696FA2B4A5FC50026E771 /* MCEndpointFilter.m */,
 				3CF71C0D2A992DA2003A5CE5 /* MCDataSource.h */,
@@ -248,20 +317,35 @@
 				3C2346232B362B9500FA276E /* MCCastingPlayerDiscovery.h in Headers */,
 				3CD6D01A298CDA2100D7569A /* CommissionerDiscoveryDelegateImpl.h in Headers */,
 				3CF71C0E2A992DA2003A5CE5 /* MCDataSource.h in Headers */,
+				3C4F52142B4F31DC00BB8A10 /* MCDeviceTypeStruct.h in Headers */,
 				3C26AC8C2926FE0C00BA6881 /* DeviceAttestationCredentialsProviderImpl.hpp in Headers */,
+				3C621CA52B605A6A005CDBA3 /* MCAttribute.h in Headers */,
 				3CD73F1C2A9E8396009D82D1 /* MCCommissionableDataProvider.h in Headers */,
+				3C4F521E2B4F4B3B00BB8A10 /* MCEndpoint_Internal.h in Headers */,
+				3C4F52262B50899700BB8A10 /* MCCluster_Internal.h in Headers */,
 				3CF71C0A2A992D0D003A5CE5 /* MCCastingApp.h in Headers */,
+				3C4F521C2B4F4B1B00BB8A10 /* MCCastingPlayer_Internal.h in Headers */,
 				3CD73F172A9E6884009D82D1 /* MCRotatingDeviceIdUniqueIdProvider.h in Headers */,
+				3C4F52102B4E18C800BB8A10 /* MCEndpoint.h in Headers */,
+				3C4F52282B51DB3000BB8A10 /* MCCommand.h in Headers */,
+				3C621CA92B605C52005CDBA3 /* MCAttribute_Internal.h in Headers */,
 				3CF71C102A99312D003A5CE5 /* MCCommissionableData.h in Headers */,
+				3C4F522A2B51DFAE00BB8A10 /* MCCommand_Internal.h in Headers */,
 				3CD73F202A9EA060009D82D1 /* MCDeviceAttestationCredentials.h in Headers */,
 				3CCB8740286A593700771BAD /* CastingServerBridge.h in Headers */,
+				3C4F52342B57338B00BB8A10 /* MCClusterObjects.h in Headers */,
 				3CE5ECCE2A673B30007CF331 /* CommissioningCallbackHandlers.h in Headers */,
 				3CCB8742286A593700771BAD /* ConversionUtils.hpp in Headers */,
+				3C621CA72B605AA1005CDBA3 /* MCObserver.h in Headers */,
 				3C9437922B3B478E0096E5F4 /* MCErrorUtils.h in Headers */,
+				3C4F52222B507C9300BB8A10 /* MCCluster.h in Headers */,
 				3CCB8741286A593700771BAD /* DiscoveredNodeData.h in Headers */,
 				3C0474062B3F7E5F0012AE95 /* MCEndpointFilter.h in Headers */,
 				3CCB87212869085400771BAD /* MatterTvCastingBridge.h in Headers */,
+				3C621CA12B5F6CF8005CDBA3 /* MCCommandObjects.h in Headers */,
+				3C4F52202B507C4A00BB8A10 /* MCEndpointClusterType.h in Headers */,
 				3C6920462AA1093300D0F613 /* MCDeviceAttestationCredentialsProvider.h in Headers */,
+				3C621CAD2B605E94005CDBA3 /* MCAttributeObjects.h in Headers */,
 				3C04740C2B4604CF0012AE95 /* MCCryptoUtils.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -368,21 +452,29 @@
 				3CE5ECD02A673E2C007CF331 /* CommissioningCallbackHandlers.m in Sources */,
 				3CD73F192A9E68A7009D82D1 /* MCRotatingDeviceIdUniqueIdProvider.mm in Sources */,
 				3CF8532728E37F1000F07B9F /* MatterError.mm in Sources */,
+				3C4F522C2B51E02800BB8A10 /* MCCommand.mm in Sources */,
 				3C4E53B628E5595A00F293E8 /* ContentLauncherTypes.mm in Sources */,
 				3C81C75028F7A7D3001CB9D1 /* VideoPlayer.m in Sources */,
 				3CCB8744286A593700771BAD /* ConversionUtils.mm in Sources */,
+				3C621CA32B5F6D08005CDBA3 /* MCCommandObjects.mm in Sources */,
 				3CF71C0C2A992D25003A5CE5 /* MCCastingApp.mm in Sources */,
+				3C621CAB2B605C6E005CDBA3 /* MCAttribute.mm in Sources */,
+				3C4F52122B4E18ED00BB8A10 /* MCEndpoint.mm in Sources */,
 				3C4E53B028E4F28100F293E8 /* MediaPlaybackTypes.mm in Sources */,
 				3C04740E2B4605B40012AE95 /* MCCryptoUtils.mm in Sources */,
+				3C4F52362B5733A200BB8A10 /* MCClusterObjects.mm in Sources */,
 				3CD73F1E2A9E83C1009D82D1 /* MCCommissionableDataProvider.mm in Sources */,
 				3CD73F222A9EA078009D82D1 /* MCDeviceAttestationCredentials.mm in Sources */,
+				3C621CAF2B605EA7005CDBA3 /* MCAttributeObjects.mm in Sources */,
 				3C2346252B362BBB00FA276E /* MCCastingPlayerDiscovery.mm in Sources */,
+				3C4F52242B507CA800BB8A10 /* MCCluster.mm in Sources */,
 				3C66FBFC290327BB00B63FE7 /* AppParameters.mm in Sources */,
 				3C9437942B3B47A10096E5F4 /* MCErrorUtils.mm in Sources */,
 				3CE868F42946D76200FCB92B /* CommissionableDataProviderImpl.mm in Sources */,
 				3C26AC9329282B8100BA6881 /* DeviceAttestationCredentialsHolder.m in Sources */,
 				3C26AC902927008900BA6881 /* DeviceAttestationCredentialsProviderImpl.mm in Sources */,
 				3CCB873F286A593700771BAD /* DiscoveredNodeData.mm in Sources */,
+				3C4F52162B4F31FA00BB8A10 /* MCDeviceTypeStruct.m in Sources */,
 				3C2696FB2B4A5FC50026E771 /* MCEndpointFilter.m in Sources */,
 				3C81C74C28F7A777001CB9D1 /* ContentApp.mm in Sources */,
 				3CF71C122A993298003A5CE5 /* MCCommissionableData.mm in Sources */,
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/xcshareddata/xcschemes/MatterTvCastingBridge.xcscheme b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/xcshareddata/xcschemes/MatterTvCastingBridge.xcscheme
new file mode 100644
index 0000000..0aadefd
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge.xcodeproj/xcshareddata/xcschemes/MatterTvCastingBridge.xcscheme
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1430"
+   version = "1.7">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "3CCB871C2869085400771BAD"
+               BuildableName = "MatterTvCastingBridge.framework"
+               BlueprintName = "MatterTvCastingBridge"
+               ReferencedContainer = "container:MatterTvCastingBridge.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      shouldAutocreateTestPlan = "YES">
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "3CCB871C2869085400771BAD"
+            BuildableName = "MatterTvCastingBridge.framework"
+            BlueprintName = "MatterTvCastingBridge"
+            ReferencedContainer = "container:MatterTvCastingBridge.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttribute.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttribute.h
new file mode 100644
index 0000000..ba9f40c
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttribute.h
@@ -0,0 +1,48 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCObserver.h"
+
+#import <Foundation/Foundation.h>
+
+#ifndef MCAttribute_h
+#define MCAttribute_h
+
+@interface MCAttribute<__covariant ObjectType> : NSObject
+
+/**
+ * @brief Reads the value of the MCAttribute that belongs to the associated MCEndpoint and corresponding MCCluster
+ * @param context current context passed back in completion block
+ * @param completion Called when attribute read completes with nil NSError if successful and before, after values. On failure, the NSError describes the error
+ */
+- (void)read:(void * _Nullable)context
+    completion:(void (^_Nonnull)(void * _Nullable context, ObjectType _Nullable before, ObjectType _Nullable after, NSError * _Nullable error))completion;
+
+/**
+ * @brief Subscribe to the value of the MCAttribute that belongs to the associated MCEndpoint and corresponding MCCluster
+ * @param context current context passed back in completion block
+ * @param completion Called when attribute read completes with nil NSError if successful and before, after values. On failure, the NSError describes the error
+ * @param minInterval the requested minimum interval boundary floor in seconds for attribute udpates
+ * @param maxInterval the requested maximum interval boundary ceiling in seconds for attribute udpates
+ */
+- (void)subscribe:(void * _Nullable)context
+       completion:(void (^_Nonnull)(void * _Nullable context, ObjectType _Nullable before, ObjectType _Nullable after, NSError * _Nullable error))completion
+      minInterval:(NSNumber * _Nonnull)minInterval
+      maxInterval:(NSNumber * _Nonnull)maxInterval;
+@end
+
+#endif /* MCAttribute_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttribute.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttribute.mm
new file mode 100644
index 0000000..09397ab
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttribute.mm
@@ -0,0 +1,50 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCAttribute_Internal.h"
+#import "MCCastingApp.h"
+
+#import <Foundation/Foundation.h>
+
+@implementation MCAttribute
+
+- (instancetype _Nonnull)initWithCppAttribute:(void *)cppAttribute
+{
+    if (self = [super init]) {
+        _cppAttribute = cppAttribute;
+    }
+    return self;
+}
+
+- (void)read:(void * _Nullable)context completion:(void (^_Nonnull __strong)(void * _Nullable, id _Nullable __strong, id _Nullable __strong, NSError * _Nullable __strong))completion
+{
+}
+
+- (void)subscribe:(void * _Nullable)context completion:(void (^_Nonnull __strong)(void * _Nullable, id _Nullable __strong, id _Nullable __strong, NSError * _Nullable __strong))completion minInterval:(NSNumber *)minInterval maxInterval:(NSNumber *)maxInterval
+{
+}
+
+- (void)write:(id _Nonnull)value withCompletionBlock:(void (^_Nonnull __strong)(NSError * _Nullable __strong))completionBlock
+{
+}
+
+- (id _Nullable)getObjTypeFromCpp:(std::any)cppValue
+{
+    return nil;
+}
+
+@end
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttributeObjects.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttributeObjects.h
new file mode 100644
index 0000000..d8f371e
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttributeObjects.h
@@ -0,0 +1,30 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCAttribute.h"
+#import <Foundation/Foundation.h>
+
+#ifndef MCAttributeObjects_h
+#define MCAttributeObjects_h
+
+@interface MCApplicationBasicClusterVendorIDAttribute : MCAttribute <NSNumber *>
+@end
+
+@interface MCMediaPlaybackClusterCurrentStateAttribute : MCAttribute <NSNumber *>
+@end
+
+#endif /* MCAttributeObjects_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttributeObjects.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttributeObjects.mm
new file mode 100644
index 0000000..5ce91d5
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttributeObjects.mm
@@ -0,0 +1,109 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCAttributeObjects.h"
+
+#import "MCAttribute_Internal.h"
+#import "MCCastingApp.h"
+#import "MCErrorUtils.h"
+
+#include "core/Attribute.h"
+#include <app-common/zap-generated/cluster-objects.h>
+
+#import <Foundation/Foundation.h>
+
+@implementation MCApplicationBasicClusterVendorIDAttribute
+- (void)read:(void * _Nullable)context
+    completion:(void (^_Nonnull __strong)(void * _Nullable, id _Nullable __strong before, id _Nullable __strong after, NSError * _Nullable __strong error))completion
+{
+    MCAttributeTemplate<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo> * mcAttribute = new MCAttributeTemplate<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo>(self.cppAttribute,
+        [self](std::any cppValue) {
+            return [self getObjTypeFromCpp:cppValue];
+        });
+    mcAttribute->read(context, [mcAttribute, completion](void * context, id before, id after, NSError * err) {
+        completion(context, before, after, err);
+        delete mcAttribute;
+    });
+}
+
+- (void)subscribe:(void * _Nullable)context
+       completion:(void (^_Nonnull __strong)(void * _Nullable, id _Nullable __strong before, id _Nullable __strong after, NSError * _Nullable __strong error))completion
+      minInterval:(NSNumber * _Nonnull)minInterval
+      maxInterval:(NSNumber * _Nonnull)maxInterval
+{
+    MCAttributeTemplate<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo> * mcAttribute = new MCAttributeTemplate<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo>(self.cppAttribute,
+        [self](std::any cppValue) {
+            return [self getObjTypeFromCpp:cppValue];
+        });
+    mcAttribute->subscribe(
+        context, [mcAttribute, completion](void * context, id before, id after, NSError * err) {
+            completion(context, before, after, err);
+            delete mcAttribute;
+        }, minInterval, maxInterval);
+}
+
+- (id _Nullable)getObjTypeFromCpp:(std::any)cppValue
+{
+    NSNumber * outValue = nil;
+    if (cppValue.type() == typeid(std::shared_ptr<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType>)) {
+        std::shared_ptr<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType> valueSharedPtr = std::any_cast<std::shared_ptr<chip::app::Clusters::ApplicationBasic::Attributes::VendorID::TypeInfo::DecodableArgType>>(cppValue);
+        outValue = valueSharedPtr != nil ? [NSNumber numberWithUnsignedInteger:*valueSharedPtr] : nil;
+    }
+    return outValue;
+}
+@end
+
+@implementation MCMediaPlaybackClusterCurrentStateAttribute
+- (void)read:(void * _Nullable)context
+    completion:(void (^_Nonnull __strong)(void * _Nullable, id _Nullable __strong before, id _Nullable __strong after, NSError * _Nullable __strong error))completion
+{
+    MCAttributeTemplate<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo> * mcAttribute = new MCAttributeTemplate<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo>(self.cppAttribute,
+        [self](std::any cppValue) {
+            return [self getObjTypeFromCpp:cppValue];
+        });
+    mcAttribute->read(context, [mcAttribute, completion](void * context, id before, id after, NSError * err) {
+        completion(context, before, after, err);
+        delete mcAttribute;
+    });
+}
+
+- (void)subscribe:(void * _Nullable)context
+       completion:(void (^_Nonnull __strong)(void * _Nullable, id _Nullable __strong before, id _Nullable __strong after, NSError * _Nullable __strong error))completion
+      minInterval:(NSNumber * _Nonnull)minInterval
+      maxInterval:(NSNumber * _Nonnull)maxInterval
+{
+    MCAttributeTemplate<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo> * mcAttribute = new MCAttributeTemplate<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo>(self.cppAttribute,
+        [self](std::any cppValue) {
+            return [self getObjTypeFromCpp:cppValue];
+        });
+    mcAttribute->subscribe(
+        context, [mcAttribute, completion](void * context, id before, id after, NSError * err) {
+            completion(context, before, after, err);
+            delete mcAttribute;
+        }, minInterval, maxInterval);
+}
+
+- (id _Nullable)getObjTypeFromCpp:(std::any)cppValue
+{
+    NSNumber * outValue = nil;
+    if (cppValue.type() == typeid(std::shared_ptr<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType>)) {
+        std::shared_ptr<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType> valueSharedPtr = std::any_cast<std::shared_ptr<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType>>(cppValue);
+        outValue = valueSharedPtr != nil ? [NSNumber numberWithUnsignedInteger:static_cast<uint8_t>(*valueSharedPtr)] : nil;
+    }
+    return outValue;
+}
+@end
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttribute_Internal.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttribute_Internal.h
new file mode 100644
index 0000000..9a0c7e0
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCAttribute_Internal.h
@@ -0,0 +1,120 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCAttribute.h"
+
+#import "MCCastingApp.h"
+#import "MCErrorUtils.h"
+#include "core/Attribute.h"
+#include <lib/core/CHIPError.h>
+
+#import <Foundation/Foundation.h>
+#include <any>
+#include <functional>
+#include <memory>
+
+#ifndef MCAttribute_Internal_h
+#define MCAttribute_Internal_h
+
+@interface MCAttribute <ObjectType>()
+
+@property (nonatomic, readwrite) void * _Nonnull cppAttribute;
+
+- (instancetype _Nonnull)initWithCppAttribute:(void * _Nonnull)cppAttribute;
+
+- (id _Nullable)getObjTypeFromCpp:(std::any)cppValue;
+
+@end
+
+template <typename TypeInfo>
+class MCAttributeTemplate {
+public:
+    MCAttributeTemplate(void * _Nonnull cppAttribute,
+        std::function<id(std::any)> getObjTypeFromCppFn)
+    {
+        mCppAttribute = cppAttribute;
+        mGetObjTypeFromCppFn = getObjTypeFromCppFn;
+    }
+
+    void read(void * _Nullable context, std::function<void(void * _Nullable, id _Nullable, id _Nullable, NSError * _Nullable)> completion)
+    {
+        dispatch_queue_t workQueue = [[MCCastingApp getSharedInstance] getWorkQueue], clientQueue = [[MCCastingApp getSharedInstance] getClientQueue];
+        dispatch_sync(workQueue, ^{
+            matter::casting::core::Attribute<TypeInfo> * attribute = static_cast<matter::casting::core::Attribute<TypeInfo> *>(mCppAttribute);
+
+            attribute->Read(
+                context,
+                [clientQueue, completion, this](void * context, chip::Optional<typename TypeInfo::DecodableArgType> before, typename TypeInfo::DecodableArgType after) {
+                    NSNumber *objCBefore = nil, *objCAfter = nil;
+                    if (before.HasValue()) {
+                        ChipLogProgress(AppServer, "<MCAttributeTemplate> converting 'before' value from Cpp to ObjC");
+                        std::any anyBefore = std::any(std::make_shared<typename TypeInfo::DecodableArgType>(before.Value()));
+                        objCBefore = mGetObjTypeFromCppFn(anyBefore);
+                    }
+                    ChipLogProgress(AppServer, "<MCAttributeTemplate> converting 'after' value from Cpp to ObjC");
+                    std::any anyAfter = std::any(std::make_shared<typename TypeInfo::DecodableArgType>(after));
+                    objCAfter = mGetObjTypeFromCppFn(anyAfter);
+                    dispatch_async(clientQueue, ^{
+                        completion(context, objCBefore, objCAfter, nil);
+                    });
+                },
+                [clientQueue, completion](void * context, CHIP_ERROR error) {
+                    dispatch_async(clientQueue, ^{
+                        completion(context, nil, nil, [MCErrorUtils NSErrorFromChipError:error]);
+                    });
+                });
+        });
+    }
+
+    void subscribe(void * _Nullable context, std::function<void(void * _Nullable, id _Nullable, id _Nullable, NSError * _Nullable)> completion,
+        NSNumber * _Nonnull minInterval, NSNumber * _Nonnull maxInterval)
+    {
+        dispatch_queue_t workQueue = [[MCCastingApp getSharedInstance] getWorkQueue], clientQueue = [[MCCastingApp getSharedInstance] getClientQueue];
+        dispatch_sync(workQueue, ^{
+            matter::casting::core::Attribute<TypeInfo> * attribute = static_cast<matter::casting::core::Attribute<TypeInfo> *>(mCppAttribute);
+
+            attribute->Subscribe(
+                context,
+                [clientQueue, completion, this](void * context, chip::Optional<typename TypeInfo::DecodableArgType> before, typename TypeInfo::DecodableArgType after) {
+                    NSNumber *objCBefore = nil, *objCAfter = nil;
+                    if (before.HasValue()) {
+                        ChipLogProgress(AppServer, "<MCAttributeTemplate> converting 'before' value from Cpp to ObjC");
+                        std::any anyBefore = std::any(std::make_shared<typename TypeInfo::DecodableArgType>(before.Value()));
+                        objCBefore = mGetObjTypeFromCppFn(anyBefore);
+                    }
+                    ChipLogProgress(AppServer, "<MCAttributeTemplate> converting 'after' value from Cpp to ObjC");
+                    std::any anyAfter = std::any(std::make_shared<typename TypeInfo::DecodableArgType>(after));
+                    objCAfter = mGetObjTypeFromCppFn(anyAfter);
+                    dispatch_async(clientQueue, ^{
+                        completion(context, objCBefore, objCAfter, nil);
+                    });
+                },
+                [clientQueue, completion](void * context, CHIP_ERROR error) {
+                    dispatch_async(clientQueue, ^{
+                        completion(context, nil, nil, [MCErrorUtils NSErrorFromChipError:error]);
+                    });
+                },
+                minInterval.intValue,
+                maxInterval.intValue);
+        });
+    }
+
+private:
+    void * _Nonnull mCppAttribute;
+    std::function<id(std::any)> mGetObjTypeFromCppFn;
+};
+#endif /* MCAttribute_Internal_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.h
index 4259fe5..cc3d90e 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.h
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.h
@@ -48,20 +48,20 @@
 - (void)verifyOrEstablishConnectionWithCompletionBlock:(void (^_Nonnull)(NSError * _Nullable))completion timeout:(long long)timeout desiredEndpointFilter:(MCEndpointFilter * _Nullable)desiredEndpointFilter;
 
 /**
- * @brief (async) Verifies that a connection exists with this CastingPlayer, or triggers a new session request. If the
- * CastingApp does not have the nodeId and fabricIndex of this CastingPlayer cached on disk, this will execute the user
+ * @brief (async) Verifies that a connection exists with this MCCastingPlayer, or triggers a new session request. If the
+ * MCCastingApp does not have the nodeId and fabricIndex of this MCCastingPlayer cached on disk, this will execute the user
  * directed commissioning process.
  *
  * @param completion - called back when the connection process completes. Parameter is nil if it completed successfully
- * @param desiredEndpointFilter - Attributes (such as VendorId) describing an Endpoint that the client wants to interact
+ * @param desiredEndpointFilter - Attributes (such as VendorId) describing an MCEndpoint that the client wants to interact
  * with after commissioning. If this value is passed in, the VerifyOrEstablishConnection will force User Directed
- * Commissioning, in case the desired Endpoint is not found in the on-device cached information about the CastingPlayer
+ * Commissioning, in case the desired Endpoint is not found in the on-device cached information about the MCCastingPlayer
  * (if any)
  */
 - (void)verifyOrEstablishConnectionWithCompletionBlock:(void (^_Nonnull)(NSError * _Nullable))completion desiredEndpointFilter:(MCEndpointFilter * _Nullable)desiredEndpointFilter;
 
 /**
- * @brief Sets the internal connection state of this CastingPlayer to "disconnected"
+ * @brief Sets the internal connection state of this MCCastingPlayer to "disconnected"
  */
 - (void)disconnect;
 
@@ -72,8 +72,10 @@
 - (uint32_t)deviceType;
 - (NSArray * _Nonnull)ipAddresses;
 
-// TODO
-// - (NSArray<MCEndpoint *> * _Nonnull)endpoints;
+/**
+ * @brief Returns the NSArray of MCEndpoints associated with this MCCastingPlayer
+ */
+- (NSArray<MCEndpoint *> * _Nonnull)endpoints;
 
 - (nonnull instancetype)init UNAVAILABLE_ATTRIBUTE;
 + (nonnull instancetype)new UNAVAILABLE_ATTRIBUTE;
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.mm
index 40ea160..45cc956 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.mm
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer.mm
@@ -18,6 +18,7 @@
 #import "MCCastingPlayer.h"
 
 #import "MCCastingApp.h"
+#import "MCEndpoint_Internal.h"
 #import "MCErrorUtils.h"
 
 #import "core/CastingPlayer.h"
@@ -78,12 +79,6 @@
     });
 }
 
-- (NSString * _Nonnull)description
-{
-    return [NSString stringWithFormat:@"%@ with Product ID: %d and Vendor ID: %d. Resolved IPAddr?: %@",
-                     self.deviceName, self.productId, self.vendorId, self.ipAddresses != nil && self.ipAddresses.count > 0 ? @"YES" : @"NO"];
-}
-
 - (instancetype _Nonnull)initWithCppCastingPlayer:(matter::casting::memory::Strong<matter::casting::core::CastingPlayer>)cppCastingPlayer
 {
     if (self = [super init]) {
@@ -92,6 +87,12 @@
     return self;
 }
 
+- (NSString * _Nonnull)description
+{
+    return [NSString stringWithFormat:@"%@ with Product ID: %hu and Vendor ID: %hu. Resolved IPAddr?: %@",
+                     self.deviceName, self.productId, self.vendorId, self.ipAddresses != nil && self.ipAddresses.count > 0 ? @"YES" : @"NO"];
+}
+
 - (NSString * _Nonnull)identifier
 {
     return [NSString stringWithCString:_cppCastingPlayer->GetId() encoding:NSUTF8StringEncoding];
@@ -128,11 +129,16 @@
     return ipAddresses;
 }
 
-// TODO convert to Obj-C endpoints and return
-/*- (NSArray<MCEndpoint *> * _Nonnull)endpoints
+- (NSArray<MCEndpoint *> * _Nonnull)endpoints
 {
-    return [NSMutableArray new];
-}*/
+    NSMutableArray * endpoints = [NSMutableArray new];
+    const std::vector<matter::casting::memory::Strong<matter::casting::core::Endpoint>> cppEndpoints = _cppCastingPlayer->GetEndpoints();
+    for (matter::casting::memory::Strong<matter::casting::core::Endpoint> cppEndpoint : cppEndpoints) {
+        MCEndpoint * endpoint = [[MCEndpoint alloc] initWithCppEndpoint:cppEndpoint];
+        [endpoints addObject:endpoint];
+    }
+    return endpoints;
+}
 
 - (BOOL)isEqualToMCCastingPlayer:(MCCastingPlayer * _Nullable)other
 {
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer_Internal.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer_Internal.h
new file mode 100644
index 0000000..6c52733
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCastingPlayer_Internal.h
@@ -0,0 +1,33 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCCastingPlayer.h"
+
+#import "core/CastingPlayer.h"
+
+#import <Foundation/Foundation.h>
+
+#ifndef MCCastingPlayer_Internal_h
+#define MCCastingPlayer_Internal_h
+
+@interface MCCastingPlayer ()
+
+- (instancetype _Nonnull)initWithCppCastingPlayer:(matter::casting::memory::Strong<matter::casting::core::CastingPlayer>)cppCastingPlayer;
+
+@end
+
+#endif /* MCCastingPlayer_Internal_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCluster.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCluster.h
new file mode 100644
index 0000000..bd150b7
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCluster.h
@@ -0,0 +1,36 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCCommand.h"
+#import "MCEndpoint.h"
+#import <Foundation/Foundation.h>
+
+#ifndef MCCluster_h
+#define MCCluster_h
+
+@class MCEndpoint;
+
+@interface MCCluster : NSObject
+
+/**
+ * @brief Get the MCEndpoint corresponding to this MCCluster
+ */
+- (MCEndpoint * _Nonnull)endpoint;
+
+@end
+
+#endif /* MCCluster_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCluster.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCluster.mm
new file mode 100644
index 0000000..ecb7a91
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCluster.mm
@@ -0,0 +1,42 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCClusterObjects.h"
+#import "MCCluster_Internal.h"
+
+#import "MCEndpoint_Internal.h"
+
+#import "core/Endpoint.h"
+
+#import <Foundation/Foundation.h>
+
+@implementation MCCluster
+
+- (instancetype _Nonnull)initWithCppCluster:(matter::casting::memory::Strong<matter::casting::core::BaseCluster>)cppCluster
+{
+    if (self = [super init]) {
+        _cppCluster = cppCluster;
+    }
+    return self;
+}
+
+- (MCEndpoint * _Nonnull)endpoint
+{
+    return [[MCEndpoint alloc] initWithCppEndpoint:_cppCluster->GetEndpoint().lock()];
+}
+
+@end
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCClusterObjects.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCClusterObjects.h
new file mode 100644
index 0000000..290b747
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCClusterObjects.h
@@ -0,0 +1,47 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCAttributeObjects.h"
+#import "MCCluster.h"
+#import "MCCommandObjects.h"
+#import <Foundation/Foundation.h>
+
+#ifndef MCClusterObjects_h
+#define MCClusterObjects_h
+
+@interface MCContentLauncherCluster : MCCluster
+/**
+ * @brief Returns non-nil pointer to MCContentLauncherClusterLaunchURLCommand if supported, nil otherwise
+ */
+- (MCContentLauncherClusterLaunchURLCommand * _Nullable)launchURLCommand;
+@end
+
+@interface MCApplicationBasicCluster : MCCluster
+/**
+ * @brief Returns non-nil pointer to MCApplicationBasicClusterVendorIDAttribute if supported, nil otherwise
+ */
+- (MCApplicationBasicClusterVendorIDAttribute * _Nullable)vendorIDAttribute;
+@end
+
+@interface MCMediaPlaybackCluster : MCCluster
+/**
+ * @brief Returns non-nil pointer to MCMediaPlaybackClusterCurrentStateAttribute if supported, nil otherwise
+ */
+- (MCMediaPlaybackClusterCurrentStateAttribute * _Nullable)currentStateAttribute;
+@end
+
+#endif /* MCClusterObjects_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCClusterObjects.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCClusterObjects.mm
new file mode 100644
index 0000000..397dc62
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCClusterObjects.mm
@@ -0,0 +1,52 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCClusterObjects.h"
+
+#import "MCAttribute_Internal.h"
+#import "MCCluster_Internal.h"
+#import "MCCommand_Internal.h"
+
+#include "core/Attribute.h"
+#include "core/Command.h"
+#include <app-common/zap-generated/cluster-objects.h>
+
+#import <Foundation/Foundation.h>
+
+@implementation MCContentLauncherCluster
+- (id)launchURLCommand
+{
+    void * cppCommand = self.cppCluster->GetCommand(chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Id);
+    return cppCommand != nil ? [[MCContentLauncherClusterLaunchURLCommand alloc] initWithCppCommand:cppCommand] : nil;
+}
+@end
+
+@implementation MCApplicationBasicCluster
+- (id)vendorIDAttribute
+{
+    void * cppAttribute = self.cppCluster->GetAttribute(chip::app::Clusters::ApplicationBasic::Attributes::VendorID::Id);
+    return cppAttribute != nil ? [[MCApplicationBasicClusterVendorIDAttribute alloc] initWithCppAttribute:cppAttribute] : nil;
+}
+@end
+
+@implementation MCMediaPlaybackCluster
+- (id)currentStateAttribute
+{
+    void * cppAttribute = self.cppCluster->GetAttribute(chip::app::Clusters::MediaPlayback::Attributes::CurrentState::Id);
+    return cppAttribute != nil ? [[MCMediaPlaybackClusterCurrentStateAttribute alloc] initWithCppAttribute:cppAttribute] : nil;
+}
+@end
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCluster_Internal.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCluster_Internal.h
new file mode 100644
index 0000000..5342d5e
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCluster_Internal.h
@@ -0,0 +1,35 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCCluster.h"
+
+#import "core/Endpoint.h"
+
+#import <Foundation/Foundation.h>
+
+#ifndef MCCluster_Internal_h
+#define MCCluster_Internal_h
+
+@interface MCCluster ()
+
+@property (nonatomic, readwrite) matter::casting::memory::Strong<matter::casting::core::BaseCluster> cppCluster;
+
+- (instancetype _Nonnull)initWithCppCluster:(matter::casting::memory::Strong<matter::casting::core::BaseCluster>)cppCluster;
+
+@end
+
+#endif /* MCCluster_Internal_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommand.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommand.h
new file mode 100644
index 0000000..634d72a
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommand.h
@@ -0,0 +1,39 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+#ifndef MCCommand_h
+#define MCCommand_h
+
+@interface MCCommand<RequestType, ResponseType> : NSObject
+
+/**
+ * @brief Invokes this MCCommand on the associated MCEndpoint and corresponding MCCluster
+ *
+ * @param request request data corresponding to this command invocation
+ * @param completion Called when command execution completes with nil NSError if successful and responseData. On failure, the NSError describes the error
+ * @param timedInvokeTimeoutMs command timeout in ms
+ */
+- (void)invoke:(RequestType _Nonnull)request
+                 context:(void * _Nullable)context
+              completion:(void (^_Nonnull)(void * _Nullable, NSError * _Nullable, ResponseType _Nullable))completion
+    timedInvokeTimeoutMs:(NSNumber * _Nullable)timedInvokeTimeoutMs;
+
+@end
+
+#endif /* MCCommand_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommand.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommand.mm
new file mode 100644
index 0000000..b70945c
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommand.mm
@@ -0,0 +1,50 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCCastingApp.h"
+#import "MCCommand_Internal.h"
+
+#import <Foundation/Foundation.h>
+
+@implementation MCCommand
+
+- (instancetype _Nonnull)initWithCppCommand:(void *)cppCommand
+{
+    if (self = [super init]) {
+        _cppCommand = cppCommand;
+    }
+    return self;
+}
+
+- (void)invoke:(id _Nonnull)request
+                 context:(void * _Nullable)context
+              completion:(void (^_Nonnull __strong)(void * _Nullable, NSError * _Nullable __strong, id _Nullable __strong))completion
+    timedInvokeTimeoutMs:(NSNumber * _Nullable)timedInvokeTimeoutMs
+{
+}
+
+- (std::any)getCppRequestFromObjC:(id _Nonnull)objcRequest
+{
+    return nil;
+}
+
+- (id _Nullable)getObjCResponseFromCpp:(std::any)cppResponse
+{
+    return nil;
+}
+
+@end
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommandObjects.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommandObjects.h
new file mode 100644
index 0000000..c600d15
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommandObjects.h
@@ -0,0 +1,60 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCCommand.h"
+#import <Foundation/Foundation.h>
+
+#ifndef MCCommandObjects_h
+#define MCCommandObjects_h
+
+@interface MCContentLauncherClusterDimensionStruct : NSObject <NSCopying>
+@property (nonatomic, copy) NSNumber * _Nonnull width;
+@property (nonatomic, copy) NSNumber * _Nonnull height;
+@property (nonatomic, copy) NSNumber * _Nonnull metric;
+@end
+
+@interface MCContentLauncherClusterStyleInformationStruct : NSObject <NSCopying>
+@property (nonatomic, copy) NSString * _Nullable imageURL;
+@property (nonatomic, copy) NSString * _Nullable imageUrl;
+@property (nonatomic, copy) NSString * _Nullable color;
+@property (nonatomic, copy) MCContentLauncherClusterDimensionStruct * _Nullable size;
+@end
+
+@interface MCContentLauncherClusterBrandingInformationStruct : NSObject <NSCopying>
+@property (nonatomic, copy) NSString * _Nonnull providerName;
+@property (nonatomic, copy) MCContentLauncherClusterStyleInformationStruct * _Nullable background;
+@property (nonatomic, copy) MCContentLauncherClusterStyleInformationStruct * _Nullable logo;
+@property (nonatomic, copy) MCContentLauncherClusterStyleInformationStruct * _Nullable progressBar;
+@property (nonatomic, copy) MCContentLauncherClusterStyleInformationStruct * _Nullable splash;
+@property (nonatomic, copy) MCContentLauncherClusterStyleInformationStruct * _Nullable waterMark;
+@end
+
+@interface MCContentLauncherClusterLaunchURLRequest : NSObject <NSCopying>
+@property (nonatomic, copy) NSString * _Nonnull contentURL;
+@property (nonatomic, copy) NSString * _Nullable displayString;
+@property (nonatomic, copy) MCContentLauncherClusterBrandingInformationStruct * _Nullable brandingInformation;
+@end
+
+@interface MCContentLauncherClusterLauncherResponse : NSObject <NSCopying>
+@property (nonatomic, copy) NSNumber * _Nonnull status;
+@property (nonatomic, copy) NSString * _Nullable data;
+@end
+
+@interface MCContentLauncherClusterLaunchURLCommand : MCCommand <MCContentLauncherClusterLaunchURLRequest *, MCContentLauncherClusterLauncherResponse *>
+@end
+
+#endif /* MCCommandObjects_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommandObjects.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommandObjects.mm
new file mode 100644
index 0000000..8c37d09
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommandObjects.mm
@@ -0,0 +1,132 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCCommandObjects.h"
+
+#import "MCCastingApp.h"
+#import "MCCommand_Internal.h"
+#import "MCErrorUtils.h"
+
+#include "core/Command.h"
+#include <app-common/zap-generated/cluster-objects.h>
+
+#import <Foundation/Foundation.h>
+
+@implementation MCContentLauncherClusterLaunchURLRequest
+
+- (instancetype)init
+{
+    if (self = [super init]) {
+        _contentURL = @"";
+        _displayString = nil;
+        _brandingInformation = nil;
+    }
+    return self;
+}
+
+- (id)copyWithZone:(NSZone * _Nullable)zone;
+{
+    auto other = [[MCContentLauncherClusterLaunchURLRequest alloc] init];
+    other.contentURL = self.contentURL;
+    other.displayString = self.displayString;
+    other.brandingInformation = self.brandingInformation;
+    return other;
+}
+
+- (NSString *)description
+{
+    NSString * descriptionString = [NSString stringWithFormat:@"<%@: contentURL:%@; displayString:%@; brandingInformation:%@; >", NSStringFromClass([self class]), _contentURL, _displayString, _brandingInformation];
+    return descriptionString;
+}
+@end
+
+@implementation MCContentLauncherClusterLauncherResponse
+
+- (instancetype)init
+{
+    if (self = [super init]) {
+        _status = @(0);
+        _data = nil;
+    }
+    return self;
+}
+
+- (id)copyWithZone:(NSZone * _Nullable)zone;
+{
+    auto other = [[MCContentLauncherClusterLauncherResponse alloc] init];
+
+    other.status = self.status;
+    other.data = self.data;
+    return other;
+}
+
+- (instancetype)initWithDecodableStruct:(const chip::app::Clusters::ContentLauncher::Commands::LauncherResponse::DecodableType &)decodableStruct
+{
+    if (self = [super init]) {
+        _status = [NSNumber numberWithUnsignedChar:chip::to_underlying(decodableStruct.status)];
+        _data = [[NSString alloc] initWithBytes:decodableStruct.data.Value().data() length:decodableStruct.data.Value().size() encoding:NSUTF8StringEncoding];
+    }
+    return self;
+}
+
+@end
+
+@implementation MCContentLauncherClusterLaunchURLCommand
+
+- (void)invoke:(id)request
+                 context:(void * _Nullable)context
+              completion:(void (^_Nonnull __strong)(void *, NSError *, id))completion
+    timedInvokeTimeoutMs:(NSNumber * _Nullable)timedInvokeTimeoutMs
+{
+    MCCommandTemplate<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type> * mcCommand = new MCCommandTemplate<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type>(
+        self.cppCommand,
+        [self](id objCRequest) {
+            return [self getCppRequestFromObjC:objCRequest];
+        },
+        [self](std::any cppResponse) {
+            return [self getObjCResponseFromCpp:cppResponse];
+        });
+    mcCommand->invoke(
+        request, context, [mcCommand, completion](void * context, NSError * err, id response) {
+            completion(context, err, response);
+            delete mcCommand;
+        }, timedInvokeTimeoutMs);
+}
+
+- (std::any)getCppRequestFromObjC:(MCContentLauncherClusterLaunchURLRequest *)objcRequest
+{
+    VerifyOrReturnValue(objcRequest != nil, nullptr);
+
+    std::shared_ptr<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type> cppRequest = std::make_shared<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type>();
+    cppRequest->contentURL = chip::CharSpan([objcRequest.contentURL UTF8String], [objcRequest.contentURL lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
+    if (objcRequest.displayString != nil) {
+        cppRequest->displayString = chip::Optional<chip::CharSpan>(chip::CharSpan([objcRequest.displayString UTF8String], [objcRequest.displayString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]));
+    }
+    cppRequest->brandingInformation = chip::MakeOptional(chip::app::Clusters::ContentLauncher::Structs::BrandingInformationStruct::Type()); // TODO: map brandingInformation
+    return std::any(cppRequest);
+}
+
+- (id)getObjCResponseFromCpp:(std::any)cppResponse
+{
+    MCContentLauncherClusterLauncherResponse * objCResponse = nil;
+    if (cppResponse.type() == typeid(std::shared_ptr<const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType>)) {
+        std::shared_ptr<const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType> responseSharedPtr = std::any_cast<std::shared_ptr<const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType>>(cppResponse);
+        objCResponse = responseSharedPtr != nil ? [[MCContentLauncherClusterLauncherResponse alloc] initWithDecodableStruct:*responseSharedPtr] : nil;
+    }
+    return objCResponse;
+}
+@end
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommand_Internal.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommand_Internal.h
new file mode 100644
index 0000000..3840044
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommand_Internal.h
@@ -0,0 +1,98 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCCommand.h"
+
+#import "MCCastingApp.h"
+#import "MCErrorUtils.h"
+#include "core/Command.h"
+#include <lib/core/CHIPError.h>
+
+#import <Foundation/Foundation.h>
+#include <any>
+#include <functional>
+#include <memory>
+
+#ifndef MCCommand_Internal_h
+#define MCCommand_Internal_h
+
+@interface MCCommand <RequestType, ResponseType>()
+
+@property (nonatomic, readwrite) void * _Nonnull cppCommand;
+
+- (instancetype _Nonnull)initWithCppCommand:(void * _Nonnull)cppCommand;
+
+- (std::any)getCppRequestFromObjC:(id _Nonnull)objcRequest;
+
+- (id _Nullable)getObjCResponseFromCpp:(std::any)cppResponse;
+@end
+
+template <typename Type>
+class MCCommandTemplate {
+public:
+    MCCommandTemplate(void * _Nonnull cppCommand,
+        std::function<std::any(id _Nonnull)> getCppRequestFromObjCFn,
+        std::function<id(std::any)> getObjCResponseFromCppFn)
+    {
+        mCppCommand = cppCommand;
+        mGetCppRequestFromObjCFn = getCppRequestFromObjCFn;
+        mGetObjCResponseFromCppFn = getObjCResponseFromCppFn;
+    }
+
+    void invoke(id _Nonnull request, void * _Nullable context, std::function<void(void * _Nullable, NSError * _Nullable, id _Nullable)> completion, NSNumber * _Nullable timedInvokeTimeoutMs)
+    {
+        dispatch_queue_t workQueue = [[MCCastingApp getSharedInstance] getWorkQueue], clientQueue = [[MCCastingApp getSharedInstance] getClientQueue];
+        dispatch_sync(workQueue, ^{
+            ChipLogProgress(AppServer, "<MCCommandTemplate> converting 'request' from ObjC to Cpp");
+            std::shared_ptr<Type> cppRequest = nil;
+            std::any anyRequest = mGetCppRequestFromObjCFn(request);
+            if (anyRequest.type() == typeid(std::shared_ptr<Type>)) {
+                cppRequest = std::any_cast<std::shared_ptr<Type>>(anyRequest);
+            }
+            if (cppRequest == nil) {
+                dispatch_async(clientQueue, ^{
+                    completion(context, [MCErrorUtils NSErrorFromChipError:CHIP_ERROR_INVALID_ARGUMENT], nil);
+                });
+                return;
+            }
+
+            matter::casting::core::Command<Type> * command = static_cast<matter::casting::core::Command<Type> *>(mCppCommand);
+            command->Invoke(
+                *cppRequest, context,
+                [clientQueue, completion, this](void * context, const typename Type::ResponseType & response) {
+                    ChipLogProgress(AppServer, "<MCCommandTemplate> converting 'response' from Cpp to ObjC");
+                    id objCResponse = mGetObjCResponseFromCppFn(std::any(std::make_shared<const typename Type::ResponseType>(response)));
+                    dispatch_async(clientQueue, ^{
+                        completion(context, nil, objCResponse);
+                    });
+                },
+                [clientQueue, completion](void * context, CHIP_ERROR err) {
+                    dispatch_async(clientQueue, ^{
+                        completion(context, [MCErrorUtils NSErrorFromChipError:err], nil);
+                    });
+                },
+                timedInvokeTimeoutMs != nil ? chip::MakeOptional(timedInvokeTimeoutMs.intValue) : chip::NullOptional);
+        });
+    }
+
+private:
+    void * _Nonnull mCppCommand;
+    std::function<std::any(id _Nonnull)> mGetCppRequestFromObjCFn;
+    std::function<id(std::any)> mGetObjCResponseFromCppFn;
+};
+
+#endif /* MCCommand_Internal_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCDeviceTypeStruct.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCDeviceTypeStruct.h
new file mode 100644
index 0000000..e893f65
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCDeviceTypeStruct.h
@@ -0,0 +1,33 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+#ifndef MCDeviceTypeStruct_h
+#define MCDeviceTypeStruct_h
+
+@interface MCDeviceTypeStruct : NSObject
+
+@property (nonatomic, strong, readonly) NSNumber * _Nonnull deviceType;
+
+@property (nonatomic, strong, readonly) NSNumber * _Nonnull revision;
+
+- (instancetype _Nonnull)initWithDeviceType:(uint32_t)deviceType revision:(uint16_t)revision;
+
+@end
+
+#endif /* MCDeviceTypeStruct_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCDeviceTypeStruct.m b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCDeviceTypeStruct.m
new file mode 100644
index 0000000..d50e81a
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCDeviceTypeStruct.m
@@ -0,0 +1,31 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCDeviceTypeStruct.h"
+
+@implementation MCDeviceTypeStruct
+
+- (instancetype _Nonnull)initWithDeviceType:(uint32_t)deviceType revision:(uint16_t)revision
+{
+    if (self = [super init]) {
+        _deviceType = [NSNumber numberWithUnsignedInt:deviceType];
+        _revision = [NSNumber numberWithUnsignedShort:revision];
+    }
+    return self;
+}
+
+@end
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpoint.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpoint.h
new file mode 100644
index 0000000..d252bd3
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpoint.h
@@ -0,0 +1,46 @@
+/**
+ *
+ *    Copyright (c) 2020-2023 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCCastingPlayer.h"
+#import "MCCluster.h"
+#import "MCEndpointClusterType.h"
+
+#import <Foundation/Foundation.h>
+
+#ifndef MCEndpoint_h
+#define MCEndpoint_h
+
+@class MCCastingPlayer;
+@class MCCluster;
+
+@interface MCEndpoint : NSObject
+
+- (NSNumber * _Nonnull)identifier;
+- (NSNumber * _Nonnull)vendorId;
+- (NSNumber * _Nonnull)productId;
+- (NSArray * _Nonnull)deviceTypeList;
+- (MCCastingPlayer * _Nonnull)castingPlayer;
+
+- (nonnull instancetype)init UNAVAILABLE_ATTRIBUTE;
++ (nonnull instancetype)new UNAVAILABLE_ATTRIBUTE;
+
+- (BOOL)hasCluster:(MCEndpointClusterType)type;
+- (MCCluster * _Nullable)clusterForType:(MCEndpointClusterType)type;
+
+@end
+
+#endif /* MCEndpoint_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpoint.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpoint.mm
new file mode 100644
index 0000000..006488a
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpoint.mm
@@ -0,0 +1,133 @@
+/**
+ *
+ *    Copyright (c) 2023 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCEndpoint_Internal.h"
+
+#import "MCCastingPlayer_Internal.h"
+#import "MCCluster_Internal.h"
+#import "MCDeviceTypeStruct.h"
+
+#import "MCClusterObjects.h"
+
+#import "clusters/Clusters.h"
+#import "core/Endpoint.h"
+
+#import <Foundation/Foundation.h>
+
+@interface MCEndpoint ()
+
+@property (nonatomic, readwrite) matter::casting::memory::Strong<matter::casting::core::Endpoint> cppEndpoint;
+
+@end
+
+@implementation MCEndpoint
+
+- (instancetype _Nonnull)initWithCppEndpoint:(matter::casting::memory::Strong<matter::casting::core::Endpoint>)cppEndpoint
+{
+    if (self = [super init]) {
+        _cppEndpoint = cppEndpoint;
+    }
+    return self;
+}
+
+- (NSNumber * _Nonnull)identifier
+{
+    return [NSNumber numberWithUnsignedShort:_cppEndpoint->GetId()];
+}
+
+- (NSNumber * _Nonnull)productId
+{
+    return [NSNumber numberWithUnsignedShort:_cppEndpoint->GetProductId()];
+}
+
+- (NSNumber * _Nonnull)vendorId
+{
+    return [NSNumber numberWithUnsignedShort:_cppEndpoint->GetVendorId()];
+}
+
+- (NSArray * _Nonnull)deviceTypeList
+{
+    NSMutableArray * deviceTypeList = [NSMutableArray new];
+    std::vector<chip::app::Clusters::Descriptor::Structs::DeviceTypeStruct::DecodableType> cppDeviceTypeList = _cppEndpoint->GetDeviceTypeList();
+    for (chip::app::Clusters::Descriptor::Structs::DeviceTypeStruct::DecodableType cppDeviceTypeStruct : cppDeviceTypeList) {
+        MCDeviceTypeStruct * deviceTypeStruct = [[MCDeviceTypeStruct alloc] initWithDeviceType:cppDeviceTypeStruct.deviceType revision:cppDeviceTypeStruct.revision];
+        [deviceTypeList addObject:deviceTypeStruct];
+    }
+    return deviceTypeList;
+}
+
+- (MCCastingPlayer * _Nonnull)castingPlayer
+{
+    return [[MCCastingPlayer alloc] initWithCppCastingPlayer:std::shared_ptr<matter::casting::core::CastingPlayer>(_cppEndpoint->GetCastingPlayer())];
+}
+
+- (MCCluster * _Nullable)clusterForType:(MCEndpointClusterType)type
+{
+    switch (type) {
+    case MCEndpointClusterTypeContentLauncher:
+        return [[MCContentLauncherCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::content_launcher::ContentLauncherCluster>()];
+
+    case MCEndpointClusterTypeApplicationBasic:
+        return [[MCApplicationBasicCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::application_basic::ApplicationBasicCluster>()];
+
+    case MCEndpointClusterTypeMediaPlayback:
+        return [[MCMediaPlaybackCluster alloc] initWithCppCluster:_cppEndpoint->GetCluster<matter::casting::clusters::media_playback::MediaPlaybackCluster>()];
+
+    default:
+        ChipLogError(AppServer, "MCEndpointClusterType not found");
+        break;
+    }
+    return nil;
+}
+
+- (BOOL)hasCluster:(MCEndpointClusterType)type
+{
+    return [self clusterForType:type] != nil;
+}
+
+- (BOOL)isEqualToMCEndpoint:(MCEndpoint * _Nullable)other
+{
+    return [self.identifier isEqualToNumber:other.identifier];
+}
+
+- (BOOL)isEqual:(id _Nullable)other
+{
+    if (other == nil) {
+        return NO;
+    }
+
+    if (self == other) {
+        return YES;
+    }
+
+    if (![other isKindOfClass:[MCEndpoint class]]) {
+        return NO;
+    }
+
+    return [self isEqualToMCEndpoint:(MCEndpoint *) other];
+}
+
+- (NSUInteger)hash
+{
+    const NSUInteger prime = 31;
+    NSUInteger result = 1;
+
+    result = prime * result + [self.identifier hash];
+
+    return result;
+}
+@end
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpointClusterType.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpointClusterType.h
new file mode 100644
index 0000000..7137dd1
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpointClusterType.h
@@ -0,0 +1,28 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef MCEndpointClusterType_h
+#define MCEndpointClusterType_h
+
+typedef enum _MCEndpointClusterType
+{
+    MCEndpointClusterTypeApplicationBasic,
+    MCEndpointClusterTypeContentLauncher,
+    MCEndpointClusterTypeMediaPlayback
+} MCEndpointClusterType;
+
+#endif /* MCEndpointClusterType_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpoint_Internal.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpoint_Internal.h
new file mode 100644
index 0000000..53b1d6b
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCEndpoint_Internal.h
@@ -0,0 +1,33 @@
+/**
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import "MCEndpoint.h"
+
+#import "core/Endpoint.h"
+
+#import <Foundation/Foundation.h>
+
+#ifndef MCEndpoint_Internal_h
+#define MCEndpoint_Internal_h
+
+@interface MCEndpoint ()
+
+- (instancetype _Nonnull)initWithCppEndpoint:(matter::casting::memory::Strong<matter::casting::core::Endpoint>)cppEndpoint;
+
+@end
+
+#endif /* MCEndpoint_Internal_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCObserver.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCObserver.h
new file mode 100644
index 0000000..a1ca48f
--- /dev/null
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCObserver.h
@@ -0,0 +1,29 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+#ifndef MCObserver_h
+#define MCObserver_h
+
+@protocol MCObserver
+
+- (void)attribute:(NSObject * _Nonnull)sender valueDidChange:(NSValue * _Nullable)value oldValue:(NSValue * _Nullable)oldValue;
+
+@end
+
+#endif /* MCObserver_h */
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MatterTvCastingBridge.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MatterTvCastingBridge.h
index 9329bc6..093f02b 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MatterTvCastingBridge.h
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MatterTvCastingBridge.h
@@ -26,11 +26,19 @@
 #import "CastingServerBridge.h"
 
 // Add simplified casting API headers here
+#import "MCAttribute.h"
+#import "MCAttributeObjects.h"
 #import "MCCastingApp.h"
 #import "MCCastingPlayer.h"
 #import "MCCastingPlayerDiscovery.h"
+#import "MCCluster.h"
+#import "MCClusterObjects.h"
+#import "MCCommand.h"
+#import "MCCommandObjects.h"
 #import "MCCommissionableData.h"
 #import "MCCryptoUtils.h"
 #import "MCDataSource.h"
 #import "MCDeviceAttestationCredentials.h"
+#import "MCEndpointClusterType.h"
 #import "MCEndpointFilter.h"
+#import "MCObserver.h"
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/project.pbxproj b/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/project.pbxproj
index 1fd70f6..e700512 100644
--- a/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/project.pbxproj
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/project.pbxproj
@@ -7,6 +7,13 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		3C40586E2B632DC500C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C40586D2B632DC500C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleView.swift */; };
+		3C4058702B632DDB00C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C40586F2B632DDB00C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift */; };
+		3C4F52302B51F32000BB8A10 /* MCContentLauncherLaunchURLExampleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F522F2B51F32000BB8A10 /* MCContentLauncherLaunchURLExampleViewModel.swift */; };
+		3C4F52322B5721D000BB8A10 /* MCContentLauncherLaunchURLExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F52312B5721D000BB8A10 /* MCContentLauncherLaunchURLExampleView.swift */; };
+		3C621CB12B6078A9005CDBA3 /* MCApplicationBasicReadVendorIDExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C621CB02B6078A9005CDBA3 /* MCApplicationBasicReadVendorIDExampleView.swift */; };
+		3C621CB32B6078B7005CDBA3 /* MCApplicationBasicReadVendorIDExampleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C621CB22B6078B7005CDBA3 /* MCApplicationBasicReadVendorIDExampleViewModel.swift */; };
+		3C621CB52B607FFD005CDBA3 /* MCActionSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C621CB42B607FFD005CDBA3 /* MCActionSelectorView.swift */; };
 		3C69204A2AA1368F00D0F613 /* MCInitializationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C6920492AA1368F00D0F613 /* MCInitializationExample.swift */; };
 		3C81C75328F8C79E001CB9D1 /* StartFromCacheView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C81C75228F8C79E001CB9D1 /* StartFromCacheView.swift */; };
 		3C81C75528F8C7B6001CB9D1 /* StartFromCacheViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C81C75428F8C7B6001CB9D1 /* StartFromCacheViewModel.swift */; };
@@ -51,6 +58,13 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		3C40586D2B632DC500C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCMediaPlaybackSubscribeToCurrentStateExampleView.swift; sourceTree = "<group>"; };
+		3C40586F2B632DDB00C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift; sourceTree = "<group>"; };
+		3C4F522F2B51F32000BB8A10 /* MCContentLauncherLaunchURLExampleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCContentLauncherLaunchURLExampleViewModel.swift; sourceTree = "<group>"; };
+		3C4F52312B5721D000BB8A10 /* MCContentLauncherLaunchURLExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCContentLauncherLaunchURLExampleView.swift; sourceTree = "<group>"; };
+		3C621CB02B6078A9005CDBA3 /* MCApplicationBasicReadVendorIDExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCApplicationBasicReadVendorIDExampleView.swift; sourceTree = "<group>"; };
+		3C621CB22B6078B7005CDBA3 /* MCApplicationBasicReadVendorIDExampleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCApplicationBasicReadVendorIDExampleViewModel.swift; sourceTree = "<group>"; };
+		3C621CB42B607FFD005CDBA3 /* MCActionSelectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCActionSelectorView.swift; sourceTree = "<group>"; };
 		3C6920492AA1368F00D0F613 /* MCInitializationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCInitializationExample.swift; sourceTree = "<group>"; };
 		3C75075E284C1DF800D7DB3A /* TvCasting.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TvCasting.entitlements; sourceTree = "<group>"; };
 		3C7507AC285299DF00D7DB3A /* CommissionerDiscoveryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommissionerDiscoveryView.swift; sourceTree = "<group>"; };
@@ -131,6 +145,13 @@
 				3C94377C2B364D380096E5F4 /* MCDiscoveryExampleViewModel.swift */,
 				3C94378F2B3B3FF90096E5F4 /* MCConnectionExampleView.swift */,
 				3C94377E2B364D510096E5F4 /* MCConnectionExampleViewModel.swift */,
+				3C621CB42B607FFD005CDBA3 /* MCActionSelectorView.swift */,
+				3C4F52312B5721D000BB8A10 /* MCContentLauncherLaunchURLExampleView.swift */,
+				3C4F522F2B51F32000BB8A10 /* MCContentLauncherLaunchURLExampleViewModel.swift */,
+				3C621CB02B6078A9005CDBA3 /* MCApplicationBasicReadVendorIDExampleView.swift */,
+				3C621CB22B6078B7005CDBA3 /* MCApplicationBasicReadVendorIDExampleViewModel.swift */,
+				3C40586D2B632DC500C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleView.swift */,
+				3C40586F2B632DDB00C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift */,
 				EAF14298296D561900E17793 /* CertTestView.swift */,
 				EAF1429A296D57DF00E17793 /* CertTestViewModel.swift */,
 				3CC0E8FB2841DD3400EC6A18 /* ContentView.swift */,
@@ -248,9 +269,14 @@
 			files = (
 				3C81C75328F8C79E001CB9D1 /* StartFromCacheView.swift in Sources */,
 				3C94377F2B364D510096E5F4 /* MCConnectionExampleViewModel.swift in Sources */,
+				3C4058702B632DDB00C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift in Sources */,
 				3C81C75528F8C7B6001CB9D1 /* StartFromCacheViewModel.swift in Sources */,
 				3CCB8745286A5D0F00771BAD /* CommissionerDiscoveryView.swift in Sources */,
+				3C621CB12B6078A9005CDBA3 /* MCApplicationBasicReadVendorIDExampleView.swift in Sources */,
 				3CCB8746286A5D0F00771BAD /* CommissionerDiscoveryViewModel.swift in Sources */,
+				3C4F52302B51F32000BB8A10 /* MCContentLauncherLaunchURLExampleViewModel.swift in Sources */,
+				3C621CB32B6078B7005CDBA3 /* MCApplicationBasicReadVendorIDExampleViewModel.swift in Sources */,
+				3C621CB52B607FFD005CDBA3 /* MCActionSelectorView.swift in Sources */,
 				3C81C75928F8E42D001CB9D1 /* ConnectionViewModel.swift in Sources */,
 				3CA1CA7A28E281080023ED44 /* ClusterSelectorView.swift in Sources */,
 				3C94378E2B3B3CB00096E5F4 /* MCDiscoveryExampleView.swift in Sources */,
@@ -259,6 +285,7 @@
 				3C94377D2B364D380096E5F4 /* MCDiscoveryExampleViewModel.swift in Sources */,
 				3CCB8747286A5D0F00771BAD /* CommissioningView.swift in Sources */,
 				3CCB8748286A5D0F00771BAD /* CommissioningViewModel.swift in Sources */,
+				3C4F52322B5721D000BB8A10 /* MCContentLauncherLaunchURLExampleView.swift in Sources */,
 				3CAC955B29BA948700BEA5C3 /* ExampleDAC.swift in Sources */,
 				3CA1CA7E28E284950023ED44 /* MediaPlaybackViewModel.swift in Sources */,
 				3CCB8749286A5D0F00771BAD /* ContentLauncherView.swift in Sources */,
@@ -268,6 +295,7 @@
 				3CC0E8FC2841DD3400EC6A18 /* ContentView.swift in Sources */,
 				3CA1CA7C28E282150023ED44 /* MediaPlaybackView.swift in Sources */,
 				3CC0E8FA2841DD3400EC6A18 /* TvCastingApp.swift in Sources */,
+				3C40586E2B632DC500C7C6D6 /* MCMediaPlaybackSubscribeToCurrentStateExampleView.swift in Sources */,
 				3C9437902B3B3FF90096E5F4 /* MCConnectionExampleView.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/xcshareddata/xcschemes/TvCasting Release.xcscheme b/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/xcshareddata/xcschemes/TvCasting Release.xcscheme
index 05333fb..e769afb 100644
--- a/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/xcshareddata/xcschemes/TvCasting Release.xcscheme
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/xcshareddata/xcschemes/TvCasting Release.xcscheme
@@ -53,7 +53,7 @@
       <EnvironmentVariables>
          <EnvironmentVariable
             key = "CHIP_CASTING_SIMPLIFIED"
-            value = "0"
+            value = "1"
             isEnabled = "YES">
          </EnvironmentVariable>
       </EnvironmentVariables>
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/xcshareddata/xcschemes/TvCasting.xcscheme b/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/xcshareddata/xcschemes/TvCasting.xcscheme
index 2bdbabd..c64d516 100644
--- a/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/xcshareddata/xcschemes/TvCasting.xcscheme
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/xcshareddata/xcschemes/TvCasting.xcscheme
@@ -53,7 +53,7 @@
       <EnvironmentVariables>
          <EnvironmentVariable
             key = "CHIP_CASTING_SIMPLIFIED"
-            value = "0"
+            value = "1"
             isEnabled = "YES">
          </EnvironmentVariable>
       </EnvironmentVariables>
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCActionSelectorView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCActionSelectorView.swift
new file mode 100644
index 0000000..1ddc8d9
--- /dev/null
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCActionSelectorView.swift
@@ -0,0 +1,72 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+import SwiftUI
+
+struct MCActionSelectorView: View {
+    var selectedCastingPlayer: MCCastingPlayer?
+
+    init(_selectedCastingPlayer: MCCastingPlayer?) {
+        self.selectedCastingPlayer = _selectedCastingPlayer
+    }
+    
+    var body: some View {
+        VStack(alignment: .leading) {
+            NavigationLink(
+                destination: MCContentLauncherLaunchURLExampleView(_selectedCastingPlayer: self.selectedCastingPlayer),
+                label: {
+                    Text("ContentLauncher Launch URL")
+                        .frame(width: 300, height: 30, alignment: .center)
+                        .border(Color.black, width: 1)
+                }
+            ).background(Color.blue)
+                .foregroundColor(Color.white)
+                .padding()
+            
+            NavigationLink(
+                destination: MCApplicationBasicReadVendorIDExampleView(_selectedCastingPlayer: self.selectedCastingPlayer),
+                label: {
+                    Text("ApplicationBasic Read VendorID")
+                        .frame(width: 300, height: 30, alignment: .center)
+                        .border(Color.black, width: 1)
+                }
+            ).background(Color.blue)
+                .foregroundColor(Color.white)
+                .padding()
+
+            NavigationLink(
+                destination: MCMediaPlaybackSubscribeToCurrentStateExampleView(_selectedCastingPlayer: self.selectedCastingPlayer),
+                label: {
+                    Text("MediaPlayback Subscribe to CurrentState")
+                        .frame(width: 300, height: 30, alignment: .center)
+                        .border(Color.black, width: 1)
+                }
+            ).background(Color.blue)
+                .foregroundColor(Color.white)
+                .padding()
+
+        }
+        .navigationTitle("Select an action")
+        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
+    }
+}
+
+struct MCActionSelectorView_Previews: PreviewProvider {
+    static var previews: some View {
+        MCActionSelectorView(_selectedCastingPlayer: nil)
+    }
+}
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCApplicationBasicReadVendorIDExampleView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCApplicationBasicReadVendorIDExampleView.swift
new file mode 100644
index 0000000..7a84abf
--- /dev/null
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCApplicationBasicReadVendorIDExampleView.swift
@@ -0,0 +1,52 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+import SwiftUI
+
+struct MCApplicationBasicReadVendorIDExampleView: View {
+    @StateObject var viewModel = MCApplicationBasicReadVendorIDExampleViewModel()
+    
+    var selectedCastingPlayer: MCCastingPlayer?
+
+    init(_selectedCastingPlayer: MCCastingPlayer?) {
+        self.selectedCastingPlayer = _selectedCastingPlayer
+    }
+    
+    var body: some View {
+        VStack(alignment: .leading)
+        {
+            Button("Read VendorID!") {
+                viewModel.read(castingPlayer: self.selectedCastingPlayer!)
+            }
+            .background(Color.blue)
+            .foregroundColor(Color.white)
+            .cornerRadius(4)
+            .border(Color.black, width: 1)
+            .padding()
+                
+            Text(viewModel.status ?? "")
+        }
+        .navigationTitle("ApplicationBasic Read VendorID")
+        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
+    }
+}
+
+struct MCApplicationBasicReadVendorIDExampleView_Previews: PreviewProvider {
+    static var previews: some View {
+        MCApplicationBasicReadVendorIDExampleView(_selectedCastingPlayer: nil)
+    }
+}
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCApplicationBasicReadVendorIDExampleViewModel.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCApplicationBasicReadVendorIDExampleViewModel.swift
new file mode 100644
index 0000000..32e20e4
--- /dev/null
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCApplicationBasicReadVendorIDExampleViewModel.swift
@@ -0,0 +1,96 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+
+import Foundation
+import os.log
+
+class MCApplicationBasicReadVendorIDExampleViewModel: ObservableObject {
+    let Log = Logger(subsystem: "com.matter.casting",
+                     category: "MCApplicationBasicReadVendorIDExampleViewModel")
+    
+    @Published var status: String?;
+
+    // VendorId of the MCEndpoint on the MCCastingPlayer that the MCCastingApp desires to interact with after connection
+    let sampleEndpointVid: Int = 65521
+
+    func read(castingPlayer: MCCastingPlayer)
+    {
+        // select the MCEndpoint on the MCCastingPlayer to invoke the command on
+        if let endpoint: MCEndpoint = castingPlayer.endpoints().filter({ $0.vendorId().intValue == sampleEndpointVid}).first
+        {
+            // validate that the selected endpoint supports the ApplicationBasic cluster
+            if(!endpoint.hasCluster(MCEndpointClusterTypeApplicationBasic))
+            {
+                self.Log.error("No ApplicationBasic cluster supporting endpoint found")
+                DispatchQueue.main.async
+                {
+                    self.status = "No ApplicationBasic cluster supporting endpoint found"
+                }
+                return
+            }
+            
+            // get ApplicationBasic cluster from the endpoint
+            let applicationBasiccluster: MCApplicationBasicCluster = endpoint.cluster(for: MCEndpointClusterTypeApplicationBasic) as! MCApplicationBasicCluster
+
+            // get the vendorIDAttribute from the applicationBasiccluster
+            let vendorIDAttribute: MCApplicationBasicClusterVendorIDAttribute? = applicationBasiccluster.vendorIDAttribute()
+            if(vendorIDAttribute == nil)
+            {
+                self.Log.error("VendorID attribute not supported on cluster")
+                DispatchQueue.main.async
+                {
+                    self.status = "VendorID attribute not supported on cluster"
+                }
+                return
+            }
+                
+                    
+            // call read on vendorIDAttribute and pass in a completion block
+            vendorIDAttribute!.read(nil) { context, before, after, err in
+                DispatchQueue.main.async
+                {
+                    if(err != nil)
+                    {
+                        self.Log.error("Error when reading VendorID value \(String(describing: err))")
+                        self.status = "Error when reading VendorID value \(String(describing: err))"
+                        return
+                    }
+                
+                    if(before != nil)
+                    {
+                        self.Log.info("Read VendorID value: \(String(describing: after)) Before: \(String(describing: before))")
+                        self.status = "Read VendorID value: \(String(describing: after)) Before: \(String(describing: before))"
+                    }
+                    else
+                    {
+                        self.Log.info("Read VendorID value: \(String(describing: after))")
+                        self.status = "Read VendorID value: \(String(describing: after))"
+                    }
+                }
+            }
+        }
+        else
+        {
+            self.Log.error("No endpoint matching the example VID found")
+            DispatchQueue.main.async
+            {
+                self.status = "No endpoint matching the example VID found"
+            }
+        }
+    }
+}
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCConnectionExampleView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCConnectionExampleView.swift
index 0dd6d30..87448bf 100644
--- a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCConnectionExampleView.swift
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCConnectionExampleView.swift
@@ -39,9 +39,8 @@
                 
                 if(connectionSuccess)
                 {
-                    /* TODO add this back in
-                     NavigationLink(
-                        destination: ClusterSelectorView(),
+                    NavigationLink(
+                        destination: MCActionSelectorView(_selectedCastingPlayer: self.selectedCastingPlayer), 
                         label: {
                             Text("Next")
                                 .frame(width: 100, height: 30, alignment: .center)
@@ -50,7 +49,7 @@
                     ).background(Color.blue)
                         .foregroundColor(Color.white)
                         .frame(maxHeight: .infinity, alignment: .bottom)
-                        .padding()*/
+                        .padding()
                 }
             }
         }
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCConnectionExampleViewModel.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCConnectionExampleViewModel.swift
index 9d130b5..13cdad3 100644
--- a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCConnectionExampleViewModel.swift
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCConnectionExampleViewModel.swift
@@ -35,15 +35,18 @@
         desiredEndpointFilter.vendorId = kDesiredEndpointVendorId
         selectedCastingPlayer?.verifyOrEstablishConnection(completionBlock: { err in
             self.Log.error("MCConnectionExampleViewModel connect() completed with \(err)")
-            if(err == nil)
+            DispatchQueue.main.async
             {
-                self.connectionSuccess = true
-                self.connectionStatus = "Connected!"
-            }
-            else
-            {
-                self.connectionSuccess = false
-                self.connectionStatus = "Connection failed with \(String(describing: err))"
+                if(err == nil)
+                {
+                    self.connectionSuccess = true
+                    self.connectionStatus = "Connected!"
+                }
+                else
+                {
+                    self.connectionSuccess = false
+                    self.connectionStatus = "Connection failed with \(String(describing: err))"
+                }
             }
         }, desiredEndpointFilter: desiredEndpointFilter)
     }
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCContentLauncherLaunchURLExampleView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCContentLauncherLaunchURLExampleView.swift
new file mode 100644
index 0000000..520ea86
--- /dev/null
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCContentLauncherLaunchURLExampleView.swift
@@ -0,0 +1,79 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+import SwiftUI
+
+struct MCContentLauncherLaunchURLExampleView : View {
+    @StateObject var viewModel = MCContentLauncherLaunchURLExampleViewModel()
+
+    var selectedCastingPlayer: MCCastingPlayer?
+    
+    @State private var contentUrl: String = ""
+    @State private var displayString: String = ""
+    
+    init(_selectedCastingPlayer: MCCastingPlayer?) {
+        self.selectedCastingPlayer = _selectedCastingPlayer
+    }
+    
+    var body: some View {
+        VStack(alignment: .leading) {
+
+            HStack() {
+                Text("Content URL")
+                
+                TextField(
+                    "https://www.test.com/videoid",
+                    text: $contentUrl
+                )
+                .textInputAutocapitalization(.never)
+                .disableAutocorrection(true)
+                .border(.secondary)
+            }
+            
+            HStack() {
+                Text("Display string")
+                
+                TextField(
+                    "Test video",
+                    text: $displayString
+                )
+                .textInputAutocapitalization(.never)
+                .disableAutocorrection(true)
+                .border(.secondary)
+            }
+            
+            Button("Invoke Launch URL!") {
+                viewModel.invokeCommand(castingPlayer: self.selectedCastingPlayer!, contentUrl: contentUrl, displayString: displayString)
+            }
+            .background(Color.blue)
+            .foregroundColor(Color.white)
+            .cornerRadius(4)
+            .border(Color.black, width: 1)
+            .padding()
+            
+            Text(viewModel.status ?? "")
+        }
+        .navigationTitle("Content Launcher Launch URL")
+        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
+    }
+}
+
+struct MCCommandInvocationExampleView_Previews: PreviewProvider {
+    static var previews: some View {
+        MCContentLauncherLaunchURLExampleView(_selectedCastingPlayer: nil)
+    }
+}
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCContentLauncherLaunchURLExampleViewModel.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCContentLauncherLaunchURLExampleViewModel.swift
new file mode 100644
index 0000000..b74a91a
--- /dev/null
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCContentLauncherLaunchURLExampleViewModel.swift
@@ -0,0 +1,94 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+
+import Foundation
+import os.log
+
+class MCContentLauncherLaunchURLExampleViewModel: ObservableObject {
+    let Log = Logger(subsystem: "com.matter.casting",
+                     category: "MCContentLauncherLaunchURLExampleViewModel")
+
+    @Published var status: String?;
+
+    // VendorId of the MCEndpoint on the MCCastingPlayer that the MCCastingApp desires to interact with after connection
+    let sampleEndpointVid: Int = 65521
+
+    func invokeCommand(castingPlayer: MCCastingPlayer, contentUrl: String, displayString: String)
+    {
+        // select the MCEndpoint on the MCCastingPlayer to invoke the command on
+        if let endpoint: MCEndpoint = castingPlayer.endpoints().filter({ $0.vendorId().intValue == sampleEndpointVid}).first
+        {
+            // validate that the selected endpoint supports the ContentLauncher cluster
+            if(!endpoint.hasCluster(MCEndpointClusterTypeContentLauncher))
+            {
+                self.Log.error("No ContentLauncher cluster supporting endpoint found")
+                DispatchQueue.main.async
+                {
+                    self.status = "No ContentLauncher cluster supporting endpoint found"
+                }
+                return
+            }
+            
+            // get ContentLauncher cluster from the endpoint
+            let contentLaunchercluster: MCContentLauncherCluster = endpoint.cluster(for: MCEndpointClusterTypeContentLauncher) as! MCContentLauncherCluster
+
+            // get the launchURLCommand from the contentLauncherCluster
+            let launchURLCommand: MCContentLauncherClusterLaunchURLCommand? = contentLaunchercluster.launchURLCommand()
+            if(launchURLCommand == nil)
+            {
+                self.Log.error("LaunchURL not supported on cluster")
+                DispatchQueue.main.async
+                {
+                    self.status = "LaunchURL not supported on cluster"
+                }
+                return
+            }
+                
+            // create the LaunchURL request
+            let request: MCContentLauncherClusterLaunchURLRequest = MCContentLauncherClusterLaunchURLRequest()
+            request.contentURL = contentUrl
+            request.displayString = displayString
+                    
+            // call invoke on launchURLCommand while passing in a completion block
+            launchURLCommand!.invoke(request, context: nil, completion: { context, err, response in
+                DispatchQueue.main.async
+                {
+                    if(err == nil)
+                    {
+                        self.Log.info("LaunchURLCommand invoke completion success with \(String(describing: response))")
+                        self.status = "Success. Response data: \(String(describing: response?.data))"
+                    }
+                    else
+                    {
+                        self.Log.error("LaunchURLCommand invoke completion failure with \(String(describing: err))")
+                        self.status = "Failure: \(String(describing: err))"
+                    }
+                }
+            },
+            timedInvokeTimeoutMs: 5000) // time out after 5000ms
+        }
+        else
+        {
+            self.Log.error("No endpoint matching the example VID found")
+            DispatchQueue.main.async
+            {
+                self.status = "No endpoint matching the example VID found"
+            }
+        }
+    }
+}
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCMediaPlaybackSubscribeToCurrentStateExampleView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCMediaPlaybackSubscribeToCurrentStateExampleView.swift
new file mode 100644
index 0000000..f99ea71
--- /dev/null
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCMediaPlaybackSubscribeToCurrentStateExampleView.swift
@@ -0,0 +1,52 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+import SwiftUI
+
+struct MCMediaPlaybackSubscribeToCurrentStateExampleView: View {
+    @StateObject var viewModel = MCMediaPlaybackSubscribeToCurrentStateExampleViewModel()
+    
+    var selectedCastingPlayer: MCCastingPlayer?
+
+    init(_selectedCastingPlayer: MCCastingPlayer?) {
+        self.selectedCastingPlayer = _selectedCastingPlayer
+    }
+    
+    var body: some View {
+        VStack(alignment: .leading)
+        {
+            Button("Subscribe to CurrentState!") {
+                viewModel.subscribe(castingPlayer: self.selectedCastingPlayer!)
+            }
+            .background(Color.blue)
+            .foregroundColor(Color.white)
+            .cornerRadius(4)
+            .border(Color.black, width: 1)
+            .padding()
+                
+            Text(viewModel.status ?? "")
+        }
+        .navigationTitle("MediaPlayback Subscribe to CurrentState")
+        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
+    }
+}
+
+struct MCMediaPlaybackSubscribeToCurrentStateExampleView_Previews: PreviewProvider {
+    static var previews: some View {
+        MCMediaPlaybackSubscribeToCurrentStateExampleView(_selectedCastingPlayer: nil)
+    }
+}
diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift
new file mode 100644
index 0000000..5d30f09
--- /dev/null
+++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift
@@ -0,0 +1,99 @@
+/**
+ *
+ *    Copyright (c) 2020-2024 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+
+import Foundation
+import os.log
+
+class MCMediaPlaybackSubscribeToCurrentStateExampleViewModel: ObservableObject {
+    let Log = Logger(subsystem: "com.matter.casting",
+                     category: "MCMediaPlaybackSubscribeToCurrentStateExampleViewModel")
+    
+    @Published var status: String?;
+
+    // VendorId of the MCEndpoint on the MCCastingPlayer that the MCCastingApp desires to interact with after connection
+    let sampleEndpointVid: Int = 65521
+
+    func subscribe(castingPlayer: MCCastingPlayer)
+    {
+        // select the MCEndpoint on the MCCastingPlayer to invoke the command on
+        if let endpoint: MCEndpoint = castingPlayer.endpoints().filter({ $0.vendorId().intValue == sampleEndpointVid}).first
+        {
+            // validate that the selected endpoint supports the MediaPlayback cluster
+            if(!endpoint.hasCluster(MCEndpointClusterTypeMediaPlayback))
+            {
+                self.Log.error("No MediaPlayback cluster supporting endpoint found")
+                DispatchQueue.main.async
+                {
+                    self.status = "No MediaPlayback cluster supporting endpoint found"
+                }
+                return
+            }
+            
+            // get MediaPlayback cluster from the endpoint
+            let mediaPlaybackCluster: MCMediaPlaybackCluster = endpoint.cluster(for: MCEndpointClusterTypeMediaPlayback) as! MCMediaPlaybackCluster
+
+            // get the currentStateAttribute from the mediaPlaybackCluster
+            let currentStateAttribute: MCMediaPlaybackClusterCurrentStateAttribute? = mediaPlaybackCluster.currentStateAttribute()
+            if(currentStateAttribute == nil)
+            {
+                self.Log.error("CurrentState attribute not supported on cluster")
+                DispatchQueue.main.async
+                {
+                    self.status = "CurrentState attribute not supported on cluster"
+                }
+                return
+            }
+                
+                    
+            // call read on currentStateAttribute and pass in a completion block
+            currentStateAttribute!.read(nil) { context, before, after, err in
+                
+                let dateFormatter = DateFormatter()
+                dateFormatter.dateFormat = "HH:mm:ss"
+                let currentTime = dateFormatter.string(from: Date())
+                DispatchQueue.main.async
+                {
+                    if(err != nil)
+                    {
+                        self.Log.error("Error when reading CurrentState value \(String(describing: err)) at \(currentTime)")
+                        self.status = "Error when reading CurrentState value \(String(describing: err)) at \(currentTime)"
+                        return
+                    }
+                    if(before != nil)
+                    {
+                        self.Log.info("Read CurrentState value: \(String(describing: after)) Before: \(String(describing: before)) at \(currentTime)")
+                        self.status = "Read CurrentState value: \(String(describing: after)) Before: \(String(describing: before)) at \(currentTime)"
+                    }
+                    else
+                    {
+                        self.Log.info("Read CurrentState value: \(String(describing: after)) at \(currentTime)")
+                        self.status = "Read CurrentState value: \(String(describing: after)) at \(currentTime)"
+                    }
+                }
+            }
+        }
+        else
+        {
+            self.Log.error("No endpoint matching the example VID found")
+            DispatchQueue.main.async
+            {
+                self.status = "No endpoint matching the example VID found"
+            }
+        }
+    }
+}
diff --git a/examples/tv-casting-app/tv-casting-common/core/Attribute.h b/examples/tv-casting-app/tv-casting-common/core/Attribute.h
index 0829613..6ce2aa0 100644
--- a/examples/tv-casting-app/tv-casting-common/core/Attribute.h
+++ b/examples/tv-casting-app/tv-casting-common/core/Attribute.h
@@ -67,8 +67,7 @@
 
     /**
      * @brief Reads the value of the Attribute that belongs to the associated Endpoint and corresponding Cluster
-     *
-     * @param context
+     * @param context current context passed back in successCb/FailureCb
      * @param successCb Called when the Attribute is read successfully, with the value of the attribute after reading, as well as
      * before (if the Attribute had been previously read)
      * @param failureCb Called when there is a failure in reading the Attribute
@@ -159,7 +158,7 @@
      * @brief Writes the value of the Attribute to an associated Endpoint and corresponding Cluster
      *
      * @param requestData value of the Attribute to be written
-     * @param context
+     * @param context current context passed back in successCb/FailureCb
      * @param successCb Called when the Attribute is written successfully
      * @param failureCb Called when there is a failure in writing the Attribute
      * @param aTimedWriteTimeoutMs write timeout
@@ -239,12 +238,12 @@
     /**
      * @brief Subscribes to the value of the Attribute that belongs to the associated Endpoint and corresponding Cluster
      *
-     * @param context
+     * @param context current context passed back in successCb/FailureCb
      * @param successCb Called when the Attribute is read successfully, with the value of the attribute after reading, as well as
      * before (if the Attribute had been previously read)
      * @param failureCb Called when there is a failure in reading the Attribute
-     * @param minIntervalFloorSeconds
-     * @param maxIntervalCeilingSeconds
+     * @param minIntervalFloorSeconds the requested minimum interval boundary floor in seconds for attribute udpates
+     * @param maxIntervalCeilingSeconds the requested maximum interval boundary ceiling in seconds for attribute udpates
      */
     void Subscribe(void * context, ReadResponseSuccessCallbackFn<typename TypeInfo::DecodableType> successCb,
                    ReadResponseFailureCallbackFn failureCb, uint16_t minIntervalFloorSeconds, uint16_t maxIntervalCeilingSeconds)
diff --git a/examples/tv-casting-app/tv-casting-common/core/CastingApp.h b/examples/tv-casting-app/tv-casting-common/core/CastingApp.h
index ed26e24..f286aad 100644
--- a/examples/tv-casting-app/tv-casting-common/core/CastingApp.h
+++ b/examples/tv-casting-app/tv-casting-common/core/CastingApp.h
@@ -47,7 +47,7 @@
     /**
      * @brief Initializes the CastingApp with appParameters
      *
-     * @param appParameters
+     * @param appParameters AppParameters required to Start up the CastingApp
      * @return CHIP_ERROR
      */
     CHIP_ERROR Initialize(const matter::casting::support::AppParameters & appParameters);
diff --git a/examples/tv-casting-app/tv-casting-common/core/Command.h b/examples/tv-casting-app/tv-casting-common/core/Command.h
index 3075053..b3e3e44 100644
--- a/examples/tv-casting-app/tv-casting-common/core/Command.h
+++ b/examples/tv-casting-app/tv-casting-common/core/Command.h
@@ -43,7 +43,6 @@
      * @brief Invokes this command on the associated Endpoint and corresponding Cluster
      *
      * @param request request data corresponding to this command invocation
-     * @param context
      * @param successCb Called on command execution success, with responseData
      * @param failureCb Called on command execution failure
      * @param timedInvokeTimeoutMs command timeout
diff --git a/examples/tv-casting-app/tv-casting-common/core/Endpoint.h b/examples/tv-casting-app/tv-casting-common/core/Endpoint.h
index 006fde0..4da8cd5 100644
--- a/examples/tv-casting-app/tv-casting-common/core/Endpoint.h
+++ b/examples/tv-casting-app/tv-casting-common/core/Endpoint.h
@@ -46,6 +46,7 @@
 };
 
 class CastingPlayer;
+class BaseCluster;
 
 /**
  * @brief An Endpoint on a CastingPlayer e.g. a Speaker or a Matter Content App