diff --git a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj
index cc9f004573bff8..145e043b15828c 100644
--- a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj
+++ b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj
@@ -42,6 +42,7 @@
 		51B22C262740CB32008D5055 /* CHIPStructsObjc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51B22C252740CB32008D5055 /* CHIPStructsObjc.mm */; };
 		51B22C2A2740CB47008D5055 /* CHIPCommandPayloadsObjc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51B22C292740CB47008D5055 /* CHIPCommandPayloadsObjc.mm */; };
 		51E24E73274E0DAC007CCF6E /* CHIPErrorTestUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51E24E72274E0DAC007CCF6E /* CHIPErrorTestUtils.mm */; };
+		5A60370827EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A60370727EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h */; };
 		5A6FEC9027B563D900F25F42 /* CHIPDeviceControllerOverXPC.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A6FEC8F27B563D900F25F42 /* CHIPDeviceControllerOverXPC.m */; };
 		5A6FEC9227B5669C00F25F42 /* CHIPDeviceControllerOverXPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A6FEC8D27B5624E00F25F42 /* CHIPDeviceControllerOverXPC.h */; };
 		5A6FEC9627B5983000F25F42 /* CHIPDeviceControllerXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A6FEC9527B5983000F25F42 /* CHIPDeviceControllerXPCConnection.m */; };
@@ -52,9 +53,7 @@
 		5A7947DE27BEC3F500434CF2 /* CHIPXPCListenerSampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A7947DD27BEC3F500434CF2 /* CHIPXPCListenerSampleTests.m */; };
 		5A7947E427C0129600434CF2 /* CHIPDeviceController+XPC.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A7947E327C0129500434CF2 /* CHIPDeviceController+XPC.m */; };
 		5A7947E527C0129F00434CF2 /* CHIPDeviceController+XPC.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A7947E227C0101200434CF2 /* CHIPDeviceController+XPC.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		5A830D6A27CFCB640053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A830D6927CFCB570053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h */; };
 		5A830D6C27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A830D6B27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h */; };
-		5A830D6E27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A830D6D27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m */; };
 		5ACDDD7A27CD129700EFD68A /* CHIPAttributeCacheContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5ACDDD7927CD129700EFD68A /* CHIPAttributeCacheContainer.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		5ACDDD7D27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5ACDDD7C27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm */; };
 		5ACDDD7E27CD3F3A00EFD68A /* CHIPAttributeCacheContainer_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5ACDDD7B27CD14AF00EFD68A /* CHIPAttributeCacheContainer_Internal.h */; };
@@ -134,6 +133,7 @@
 		51B22C252740CB32008D5055 /* CHIPStructsObjc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CHIPStructsObjc.mm; path = "zap-generated/CHIPStructsObjc.mm"; sourceTree = "<group>"; };
 		51B22C292740CB47008D5055 /* CHIPCommandPayloadsObjc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CHIPCommandPayloadsObjc.mm; path = "zap-generated/CHIPCommandPayloadsObjc.mm"; sourceTree = "<group>"; };
 		51E24E72274E0DAC007CCF6E /* CHIPErrorTestUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPErrorTestUtils.mm; sourceTree = "<group>"; };
+		5A60370727EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CHIPAttributeCacheContainer+XPC.h"; sourceTree = "<group>"; };
 		5A6FEC8B27B5609C00F25F42 /* CHIPDeviceOverXPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceOverXPC.h; sourceTree = "<group>"; };
 		5A6FEC8D27B5624E00F25F42 /* CHIPDeviceControllerOverXPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceControllerOverXPC.h; sourceTree = "<group>"; };
 		5A6FEC8F27B563D900F25F42 /* CHIPDeviceControllerOverXPC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CHIPDeviceControllerOverXPC.m; sourceTree = "<group>"; };
@@ -144,9 +144,7 @@
 		5A7947DD27BEC3F500434CF2 /* CHIPXPCListenerSampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CHIPXPCListenerSampleTests.m; sourceTree = "<group>"; };
 		5A7947E227C0101200434CF2 /* CHIPDeviceController+XPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CHIPDeviceController+XPC.h"; sourceTree = "<group>"; };
 		5A7947E327C0129500434CF2 /* CHIPDeviceController+XPC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CHIPDeviceController+XPC.m"; sourceTree = "<group>"; };
-		5A830D6927CFCB570053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CHIPDeviceControllerOverXPC+AttributeCache.h"; sourceTree = "<group>"; };
 		5A830D6B27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPDeviceControllerOverXPC_Internal.h; sourceTree = "<group>"; };
-		5A830D6D27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CHIPDeviceControllerOverXPC+AttributeCache.m"; sourceTree = "<group>"; };
 		5ACDDD7927CD129700EFD68A /* CHIPAttributeCacheContainer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPAttributeCacheContainer.h; sourceTree = "<group>"; };
 		5ACDDD7B27CD14AF00EFD68A /* CHIPAttributeCacheContainer_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CHIPAttributeCacheContainer_Internal.h; sourceTree = "<group>"; };
 		5ACDDD7C27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CHIPAttributeCacheContainer.mm; sourceTree = "<group>"; };
@@ -272,6 +270,7 @@
 				2C222ACF255C620600E446B9 /* CHIPDevice.mm */,
 				2C8C8FBE253E0C2100797F05 /* CHIPPersistentStorageDelegate.h */,
 				5ACDDD7927CD129700EFD68A /* CHIPAttributeCacheContainer.h */,
+				5A60370727EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h */,
 				5ACDDD7B27CD14AF00EFD68A /* CHIPAttributeCacheContainer_Internal.h */,
 				5ACDDD7C27CD16D200EFD68A /* CHIPAttributeCacheContainer.mm */,
 				997DED152695343400975E97 /* CHIPThreadOperationalDataset.mm */,
@@ -309,8 +308,6 @@
 				5A6FEC8D27B5624E00F25F42 /* CHIPDeviceControllerOverXPC.h */,
 				5A830D6B27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h */,
 				5A6FEC8F27B563D900F25F42 /* CHIPDeviceControllerOverXPC.m */,
-				5A830D6927CFCB570053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h */,
-				5A830D6D27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m */,
 				5A6FEC9427B5976200F25F42 /* CHIPDeviceControllerXPCConnection.h */,
 				5A6FEC9527B5983000F25F42 /* CHIPDeviceControllerXPCConnection.m */,
 			);
@@ -369,6 +366,7 @@
 				997DED182695344800975E97 /* CHIPThreadOperationalDataset.h in Headers */,
 				9956064426420367000C28DE /* CHIPSetupPayload_Internal.h in Headers */,
 				5A830D6C27CFCF590053B85D /* CHIPDeviceControllerOverXPC_Internal.h in Headers */,
+				5A60370827EA1FF60020DB79 /* CHIPAttributeCacheContainer+XPC.h in Headers */,
 				5ACDDD7E27CD3F3A00EFD68A /* CHIPAttributeCacheContainer_Internal.h in Headers */,
 				998F286D26D55E10001846C6 /* CHIPKeypair.h in Headers */,
 				1ED276E426C5832500547A89 /* CHIPCluster.h in Headers */,
@@ -379,7 +377,6 @@
 				1EC4CE6425CC276600D7304F /* CHIPClustersObjc.h in Headers */,
 				2C5EEEF6268A85C400CAE3D3 /* CHIPDeviceConnectionBridge.h in Headers */,
 				2C8C8FC0253E0C2100797F05 /* CHIPPersistentStorageDelegateBridge.h in Headers */,
-				5A830D6A27CFCB640053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.h in Headers */,
 				998F286F26D55EC5001846C6 /* CHIPP256KeypairBridge.h in Headers */,
 				2C222ADF255C811800E446B9 /* CHIPDevice_Internal.h in Headers */,
 				991DC08B247704DC00C13860 /* CHIPLogging.h in Headers */,
@@ -538,7 +535,6 @@
 				B2E0D7B6245B0B5C003C5B48 /* CHIPManualSetupPayloadParser.mm in Sources */,
 				5A6FEC9827B5C6AF00F25F42 /* CHIPDeviceOverXPC.m in Sources */,
 				51431AF927D2973E008A7943 /* CHIPIMDispatch.mm in Sources */,
-				5A830D6E27CFCFF90053B85D /* CHIPDeviceControllerOverXPC+AttributeCache.m in Sources */,
 				51431AFB27D29CA4008A7943 /* ota-provider.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -612,7 +608,7 @@
 				GCC_OPTIMIZATION_LEVEL = 0;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"DEBUG=1",
-          "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=<lib/address_resolve/AddressResolve_DefaultImpl.h>",
+					"CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=<lib/address_resolve/AddressResolve_DefaultImpl.h>",
 					"$(inherited)",
 				);
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -647,7 +643,7 @@
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					CHIP_HAVE_CONFIG_H,
-          "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=<lib/address_resolve/AddressResolve_DefaultImpl.h>",
+					"CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=<lib/address_resolve/AddressResolve_DefaultImpl.h>",
 					"$(inherited)",
 				);
 				HEADER_SEARCH_PATHS = (
@@ -790,7 +786,7 @@
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					CHIP_HAVE_CONFIG_H,
-          "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=<lib/address_resolve/AddressResolve_DefaultImpl.h>",
+					"CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=<lib/address_resolve/AddressResolve_DefaultImpl.h>",
 					"$(inherited)",
 				);
 				HEADER_SEARCH_PATHS = (
diff --git a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.h b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer+XPC.h
similarity index 50%
rename from src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.h
rename to src/darwin/Framework/CHIP/CHIPAttributeCacheContainer+XPC.h
index 407bb9d4404dc3..fb9cc654d7eb9a 100644
--- a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.h
+++ b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer+XPC.h
@@ -17,24 +17,14 @@
 
 #import <Foundation/Foundation.h>
 
-#import "CHIPDeviceControllerOverXPC.h"
+#import "CHIPAttributeCacheContainer.h"
 
 NS_ASSUME_NONNULL_BEGIN
 
-@class CHIPSubscribeParams;
-
-@interface CHIPDeviceControllerOverXPC (AttributeCache)
-
-- (void)subscribeAttributeCacheWithNodeId:(uint64_t)nodeId
-                                   params:(CHIPSubscribeParams * _Nullable)params
-                               completion:(void (^)(NSError * _Nullable error))completion;
-
-- (void)readAttributeCacheWithNodeId:(uint64_t)nodeId
-                          endpointId:(NSNumber * _Nullable)endpointId
-                           clusterId:(NSNumber * _Nullable)clusterId
-                         attributeId:(NSNumber * _Nullable)attributeId
-                          completion:(void (^)(id _Nullable values, NSError * _Nullable error))completion;
-
+@interface CHIPAttributeCacheContainer (XPC)
+- (void)setXPCConnection:(CHIPDeviceControllerXPCConnection *)xpcConnection
+            controllerId:(id<NSCopying>)controllerId
+                deviceId:(uint64_t)deviceId;
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.h b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.h
index 2c72c24cf65f2b..d695cb23977271 100644
--- a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.h
+++ b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.h
@@ -25,21 +25,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 @interface CHIPAttributeCacheContainer : NSObject
 
-/**
- * Subscribes to all attributes to update attribute cache.
- *
- * @param deviceController   device controller to retrieve connected device from
- * @param deviceId  device identifier of the device to cache attributes of
- * @param params  subscription parameters
- * @param clientQueue  client queue to dispatch the completion handler through
- * @param completion  completion handler
- */
-- (void)subscribeWithDeviceController:(CHIPDeviceController *)deviceController
-                             deviceId:(uint64_t)deviceId
-                               params:(CHIPSubscribeParams * _Nullable)params
-                          clientQueue:(dispatch_queue_t)clientQueue
-                           completion:(void (^)(NSError * _Nullable error))completion;
-
 /**
  * Reads an attribute with specific attribute path
  *
diff --git a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm
index 59c7ec701ff051..6b060374b94bc6 100644
--- a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm
+++ b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm
@@ -19,7 +19,7 @@
 
 #import "CHIPAttributeCacheContainer_Internal.h"
 #import "CHIPCluster.h"
-#import "CHIPDeviceControllerOverXPC+AttributeCache.h"
+#import "CHIPDeviceControllerXPCConnection.h"
 #import "CHIPDevice_Internal.h"
 #import "CHIPError.h"
 #import "CHIPError_Internal.h"
@@ -31,123 +31,25 @@
 
 using namespace chip;
 
-void ContainerAttributeCacheCallback::OnDone()
-{
-    dispatch_async(DeviceLayer::PlatformMgrImpl().GetWorkQueue(), ^{
-        CHIPAttributeCacheContainer * container = attributeCacheContainer;
-        if (container) {
-            CHIP_LOG_ERROR("Attribute cache read client done for device %llu", container.deviceId);
-            if (container.cppReadClient) {
-                delete container.cppReadClient;
-                container.cppReadClient = nullptr;
-            }
-            if (container.cppAttributeCache) {
-                delete container.cppAttributeCache;
-                container.cppAttributeCache = nullptr;
-            }
-        } else {
-            CHIP_LOG_ERROR("Attribute cache read client done for a released cache container");
-        }
-    });
-}
-
 @implementation CHIPAttributeCacheContainer
 
 - (instancetype)init
 {
     if ([super init]) {
         _cppAttributeCache = nullptr;
-        _cppReadClient = nullptr;
-        _attributeCacheCallback = new ContainerAttributeCacheCallback;
-        if (_attributeCacheCallback) {
-            _attributeCacheCallback->SetContainer(self);
-        }
+        _shouldUseXPC = NO;
     }
     return self;
 }
 
-- (void)dealloc
-{
-    if (_cppReadClient) {
-        delete _cppReadClient;
-    }
-    if (_cppAttributeCache) {
-        delete _cppAttributeCache;
-    }
-    if (_attributeCacheCallback) {
-        delete _attributeCacheCallback;
-    }
-}
-
-- (void)subscribeWithDeviceController:(CHIPDeviceController *)deviceController
-                             deviceId:(uint64_t)deviceId
-                               params:(CHIPSubscribeParams * _Nullable)params
-                          clientQueue:clientQueue
-                           completion:(void (^)(NSError * _Nullable error))completion
+- (void)setXPCConnection:(CHIPDeviceControllerXPCConnection *)xpcConnection
+            controllerId:(id<NSCopying>)controllerId
+                deviceId:(uint64_t)deviceId
 {
-    __auto_type workQueue = DeviceLayer::PlatformMgrImpl().GetWorkQueue();
-    __auto_type completionHandler = ^(NSError * _Nullable error) {
-        dispatch_async(clientQueue, ^{
-            completion(error);
-        });
-    };
-    if ([deviceController isKindOfClass:[CHIPDeviceControllerOverXPC class]]) {
-        self.deviceId = deviceId;
-        CHIPDeviceControllerOverXPC * xpcDeviceController = (CHIPDeviceControllerOverXPC *) deviceController;
-        self.xpcDeviceController = xpcDeviceController;
-        [xpcDeviceController subscribeAttributeCacheWithNodeId:deviceId params:params completion:completionHandler];
-        return;
-    }
-    [deviceController
-        getConnectedDevice:deviceId
-                     queue:workQueue
-         completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) {
-             if (error) {
-                 CHIP_LOG_ERROR("Error: Failed to get connected device (%llu) for attribute cache: %@", deviceId, error);
-                 completionHandler(error);
-                 return;
-             }
-             if (self.cppReadClient) {
-                 delete self.cppReadClient;
-                 self.cppReadClient = nullptr;
-             }
-             if (self.cppAttributeCache) {
-                 delete self.cppAttributeCache;
-             }
-             self.cppAttributeCache = new app::AttributeCache(*self.attributeCacheCallback);
-             if (!self.cppAttributeCache) {
-                 CHIP_LOG_ERROR("Error: Failed to allocate attribute cache for device %llu", deviceId);
-                 completionHandler([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
-                 return;
-             }
-
-             __auto_type engine = app::InteractionModelEngine::GetInstance();
-             __auto_type readClient = new app::ReadClient(engine, [device internalDevice]->GetExchangeManager(),
-                 self.cppAttributeCache->GetBufferedCallback(), app::ReadClient::InteractionType::Subscribe);
-             if (!readClient) {
-                 CHIP_LOG_ERROR("Error: Failed to allocate attribute cache read client for device %llu", deviceId);
-                 completionHandler([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
-                 return;
-             }
-             self.deviceId = deviceId;
-             app::ReadPrepareParams readParams([device internalDevice]->GetSecureSession().Value());
-             static app::AttributePathParams attributePath;
-             readParams.mpAttributePathParamsList = &attributePath;
-             readParams.mAttributePathParamsListSize = 1;
-             readParams.mMaxIntervalCeilingSeconds = 43200;
-             readParams.mIsFabricFiltered = (params == nil || params.fabricFiltered == nil || [params.fabricFiltered boolValue]);
-             readParams.mKeepSubscriptions
-                 = (params != nil && params.keepPreviousSubscriptions != nil && [params.keepPreviousSubscriptions boolValue]);
-             __auto_type err = readClient->SendAutoResubscribeRequest(std::move(readParams));
-             if (err != CHIP_NO_ERROR) {
-                 CHIP_LOG_ERROR("Error: attribute cache subscription failed for device %llu: %s", deviceId, ErrorStr(err));
-                 completionHandler([NSError errorWithDomain:CHIPErrorDomain code:err.AsInteger() userInfo:nil]);
-                 return;
-             }
-             self.cppReadClient = readClient;
-             CHIP_LOG_DEBUG("Attribute cache subscription succeeded for device %llu", deviceId);
-             completionHandler(nil);
-         }];
+    self.xpcConnection = xpcConnection;
+    self.xpcControllerId = controllerId;
+    self.deviceId = deviceId;
+    self.shouldUseXPC = YES;
 }
 
 static CHIP_ERROR AppendAttibuteValueToArray(
@@ -189,21 +91,36 @@ - (void)readAttributeWithEndpointId:(NSNumber * _Nullable)endpointId
         });
     };
 
-    if (self.xpcDeviceController) {
-        CHIPDeviceControllerOverXPC * strongController = self.xpcDeviceController;
-        if (strongController) {
-            [strongController
-                readAttributeCacheWithNodeId:self.deviceId
-                                  endpointId:endpointId
-                                   clusterId:clusterId
-                                 attributeId:attributeId
-                                  completion:(void (^)(id _Nullable values, NSError * _Nullable error)) completionHandler];
-        } else {
-            CHIP_LOG_ERROR("Reading attribute cache failed when associated device controller is deleted");
-            completionHandler(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
+    if (self.shouldUseXPC) {
+        CHIPDeviceControllerXPCConnection * xpcConnection = self.xpcConnection;
+        if (!xpcConnection) {
+            CHIP_LOG_ERROR("Attribute cache read failed: CHIPDeviceController was already disposed");
+            completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
+            return;
         }
+        __auto_type controllerId = self.xpcControllerId;
+        uint64_t nodeId = self.deviceId;
+        [xpcConnection getProxyHandleWithCompletion:^(
+            dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) {
+            if (handle) {
+                [handle.proxy readAttributeCacheWithController:controllerId
+                                                        nodeId:nodeId
+                                                    endpointId:endpointId
+                                                     clusterId:clusterId
+                                                   attributeId:attributeId
+                                                    completion:^(id _Nullable values, NSError * _Nullable error) {
+                                                        completion([CHIPDeviceController decodeXPCResponseValues:values], error);
+                                                        __auto_type handleRetainer = handle;
+                                                        (void) handleRetainer;
+                                                    }];
+            } else {
+                CHIP_LOG_ERROR("Attribute cache read failed due to XPC connection failure");
+                completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
+            }
+        }];
         return;
     }
+
     dispatch_async(DeviceLayer::PlatformMgrImpl().GetWorkQueue(), ^{
         if (endpointId == nil && clusterId == nil) {
             CHIP_LOG_ERROR("Error: currently read from attribute cache does not support wildcards for both endpoint and cluster");
diff --git a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer_Internal.h b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer_Internal.h
index ed9370bcfe952b..9e58fc1c457426 100644
--- a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer_Internal.h
+++ b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer_Internal.h
@@ -24,23 +24,13 @@
 
 NS_ASSUME_NONNULL_BEGIN
 
-class ContainerAttributeCacheCallback : public chip::app::AttributeCache::Callback {
-public:
-    void SetContainer(CHIPAttributeCacheContainer * container) { attributeCacheContainer = container; }
-
-    void OnDone() override;
-
-private:
-    __weak CHIPAttributeCacheContainer * _Nullable attributeCacheContainer;
-};
-
 @interface CHIPAttributeCacheContainer ()
 
-@property (nonatomic, readwrite) chip::app::AttributeCache * _Nullable cppAttributeCache;
-@property (nonatomic, readwrite) chip::app::ReadClient * _Nullable cppReadClient;
-@property (nonatomic, readwrite) ContainerAttributeCacheCallback * _Nullable attributeCacheCallback;
+@property (atomic, readwrite, nullable) chip::app::AttributeCache * cppAttributeCache;
 @property (nonatomic, readwrite) uint64_t deviceId;
-@property (atomic, readwrite, weak) CHIPDeviceControllerOverXPC * _Nullable xpcDeviceController;
+@property (nonatomic, readwrite, weak, nullable) CHIPDeviceControllerXPCConnection * xpcConnection;
+@property (nonatomic, readwrite, strong, nullable) id<NSCopying> xpcControllerId;
+@property (atomic, readwrite) BOOL shouldUseXPC;
 
 @end
 
diff --git a/src/darwin/Framework/CHIP/CHIPDevice.h b/src/darwin/Framework/CHIP/CHIPDevice.h
index eac211d1593ba8..f321beb9287246 100644
--- a/src/darwin/Framework/CHIP/CHIPDevice.h
+++ b/src/darwin/Framework/CHIP/CHIPDevice.h
@@ -89,6 +89,7 @@ extern NSString * const kCHIPNullValueType;
 extern NSString * const kCHIPStructureValueType;
 extern NSString * const kCHIPArrayValueType;
 
+@class CHIPAttributeCacheContainer;
 @class CHIPReadParams;
 @class CHIPSubscribeParams;
 
@@ -101,6 +102,9 @@ extern NSString * const kCHIPArrayValueType;
  * Subscribe to receive attribute reports for everything (all endpoints, all
  * clusters, all attributes, all events) on the device.
  *
+ * A non-nil attribute cache container will cache attribute values, retrievable
+ * through the designated attribute cache container.
+ *
  * reportHandler will be called any time a data update is available (with a
  * non-nil "value" and nil "error"), or any time there is an error for the
  * entire subscription (with a nil "value" and non-nil "error").  If it's called
@@ -110,6 +114,8 @@ extern NSString * const kCHIPArrayValueType;
  * instances.  Errors for specific paths, not the whole subscription, will be
  * reported via those objects.
  *
+ * reportHandler is not supported over XPC at the moment.
+ *
  * subscriptionEstablished block, if not nil, will be called once the
  * subscription is established.  This will be _after_ the first (priming) call
  * to reportHandler.  Note that if the CHIPSubscribeParams are set to
@@ -121,6 +127,7 @@ extern NSString * const kCHIPArrayValueType;
                 minInterval:(uint16_t)minInterval
                 maxInterval:(uint16_t)maxInterval
                      params:(nullable CHIPSubscribeParams *)params
+             cacheContainer:(CHIPAttributeCacheContainer * _Nullable)attributeCacheContainer
               reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler
     subscriptionEstablished:(nullable void (^)(void))subscriptionEstablishedHandler;
 
diff --git a/src/darwin/Framework/CHIP/CHIPDevice.mm b/src/darwin/Framework/CHIP/CHIPDevice.mm
index 9528022aadea4f..a46ec0dc21099e 100644
--- a/src/darwin/Framework/CHIP/CHIPDevice.mm
+++ b/src/darwin/Framework/CHIP/CHIPDevice.mm
@@ -15,6 +15,7 @@
  *    limitations under the License.
  */
 
+#import "CHIPAttributeCacheContainer_Internal.h"
 #import "CHIPAttributeTLVValueDecoder_Internal.h"
 #import "CHIPCallbackBridgeBase_internal.h"
 #import "CHIPCluster.h"
@@ -26,6 +27,7 @@
 #include "lib/core/CHIPError.h"
 #include "lib/core/DataModelTypes.h"
 
+#include <app/AttributeCache.h>
 #include <app/AttributePathParams.h>
 #include <app/BufferedReadCallback.h>
 #include <app/InteractionModelEngine.h>
@@ -236,7 +238,7 @@ - (instancetype)initWithDevice:(chip::DeviceProxy *)device
 
 namespace {
 
-class SubscriptionCallback final : public ReadClient::Callback {
+class SubscriptionCallback final : public AttributeCache::Callback {
 public:
     SubscriptionCallback(dispatch_queue_t queue, ReportCallback reportCallback,
         SubscriptionEstablishedHandler _Nullable subscriptionEstablishedHandler)
@@ -247,10 +249,21 @@ - (instancetype)initWithDevice:(chip::DeviceProxy *)device
     {
     }
 
+    SubscriptionCallback(dispatch_queue_t queue, ReportCallback reportCallback,
+        SubscriptionEstablishedHandler _Nullable subscriptionEstablishedHandler, void (^onDoneHandler)(void))
+        : mQueue(queue)
+        , mReportCallback(reportCallback)
+        , mSubscriptionEstablishedHandler(subscriptionEstablishedHandler)
+        , mBufferedReadAdapter(*this)
+        , mOnDoneHandler(onDoneHandler)
+    {
+    }
+
     BufferedReadCallback & GetBufferedCallback() { return mBufferedReadAdapter; }
 
     // We need to exist to get a ReadClient, so can't take this as a constructor argument.
     void AdoptReadClient(std::unique_ptr<ReadClient> aReadClient) { mReadClient = std::move(aReadClient); }
+    void AdoptAttributeCache(std::unique_ptr<AttributeCache> aAttributeCache) { mAttributeCache = std::move(aAttributeCache); }
 
 private:
     void OnReportBegin() override;
@@ -293,7 +306,9 @@ - (instancetype)initWithDevice:(chip::DeviceProxy *)device
     //    error callback, but not both, by tracking whether we have a queued-up
     //    deletion.
     std::unique_ptr<ReadClient> mReadClient;
+    std::unique_ptr<AttributeCache> mAttributeCache;
     bool mHaveQueuedDeletion = false;
+    void (^mOnDoneHandler)(void) = nil;
 };
 
 } // anonymous namespace
@@ -302,6 +317,7 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue
                 minInterval:(uint16_t)minInterval
                 maxInterval:(uint16_t)maxInterval
                      params:(nullable CHIPSubscribeParams *)params
+             cacheContainer:(CHIPAttributeCacheContainer * _Nullable)attributeCacheContainer
               reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler
     subscriptionEstablished:(nullable void (^)(void))subscriptionEstablishedHandler
 {
@@ -323,9 +339,25 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue
     readParams.mKeepSubscriptions
         = (params != nil) && (params.keepPreviousSubscriptions != nil) && [params.keepPreviousSubscriptions boolValue];
 
-    auto callback = std::make_unique<SubscriptionCallback>(queue, reportHandler, subscriptionEstablishedHandler);
-    auto readClient = std::make_unique<ReadClient>(InteractionModelEngine::GetInstance(), device->GetExchangeManager(),
-        callback->GetBufferedCallback(), ReadClient::InteractionType::Subscribe);
+    std::unique_ptr<SubscriptionCallback> callback;
+    std::unique_ptr<ReadClient> readClient;
+    std::unique_ptr<AttributeCache> attributeCache;
+    if (attributeCacheContainer) {
+        __weak CHIPAttributeCacheContainer * weakPtr = attributeCacheContainer;
+        callback = std::make_unique<SubscriptionCallback>(queue, reportHandler, subscriptionEstablishedHandler, ^{
+            CHIPAttributeCacheContainer * container = weakPtr;
+            if (container) {
+                container.cppAttributeCache = nullptr;
+            }
+        });
+        attributeCache = std::make_unique<AttributeCache>(*callback.get());
+        readClient = std::make_unique<ReadClient>(InteractionModelEngine::GetInstance(), device->GetExchangeManager(),
+            attributeCache->GetBufferedCallback(), ReadClient::InteractionType::Subscribe);
+    } else {
+        callback = std::make_unique<SubscriptionCallback>(queue, reportHandler, subscriptionEstablishedHandler);
+        readClient = std::make_unique<ReadClient>(InteractionModelEngine::GetInstance(), device->GetExchangeManager(),
+            callback->GetBufferedCallback(), ReadClient::InteractionType::Subscribe);
+    }
 
     CHIP_ERROR err;
     if (params != nil && params.autoResubscribe != nil && ![params.autoResubscribe boolValue]) {
@@ -344,6 +376,11 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue
         return;
     }
 
+    if (attributeCacheContainer) {
+        attributeCacheContainer.cppAttributeCache = attributeCache.get();
+        // AttributeCache will be deleted when OnDone is called or an error is encountered as well.
+        callback->AdoptAttributeCache(std::move(attributeCache));
+    }
     // Callback and ReadClient will be deleted when OnDone is called or an error is
     // encountered.
     callback->AdoptReadClient(std::move(readClient));
@@ -1310,6 +1347,10 @@ - (instancetype)initWithPath:(const ConcreteDataAttributePath &)path value:(null
 
 void SubscriptionCallback::OnDone()
 {
+    if (mOnDoneHandler) {
+        mOnDoneHandler();
+        mOnDoneHandler = nil;
+    }
     if (!mHaveQueuedDeletion) {
         delete this;
         return; // Make sure we touch nothing else.
@@ -1363,8 +1404,13 @@ - (instancetype)initWithPath:(const ConcreteDataAttributePath &)path value:(null
     __block ReportCallback callback = mReportCallback;
     __block auto * myself = this;
     mReportCallback = nil;
+    __auto_type onDoneHandler = mOnDoneHandler;
+    mOnDoneHandler = nil;
     dispatch_async(mQueue, ^{
         callback(nil, err);
+        if (onDoneHandler) {
+            onDoneHandler();
+        }
 
         // Deletion of our ReadClient (and hence of ourselves, since the
         // ReadClient has a pointer to us) needs to happen on the Matter work
diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h b/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h
index 8bb366ba99235f..a81eadfcd6099c 100644
--- a/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h
+++ b/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h
@@ -139,12 +139,15 @@ NS_ASSUME_NONNULL_BEGIN
 - (void)stopReportsWithController:(id _Nullable)controller nodeId:(uint64_t)nodeId completion:(void (^)(void))completion;
 
 /**
- * Requests a specific node attribute subscription into a cache
- */
-- (void)subscribeAttributeCacheWithController:(id _Nullable)controller
-                                       nodeId:(uint64_t)nodeId
-                                       params:(NSDictionary<NSString *, id> * _Nullable)params
-                                   completion:(void (^)(NSError * _Nullable error))completion;
+ * Requests subscription of all attributes.
+ */
+- (void)subscribeWithController:(id _Nullable)controller
+                         nodeId:(uint64_t)nodeId
+                    minInterval:(NSNumber *)minInterval
+                    maxInterval:(NSNumber *)maxInterval
+                         params:(NSDictionary<NSString *, id> * _Nullable)params
+                    shouldCache:(BOOL)shouldCache
+                     completion:(void (^)(NSError * _Nullable error))completion;
 
 /**
  * Requests reading attribute cache
diff --git a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m b/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m
deleted file mode 100644
index 7ea55db94246b5..00000000000000
--- a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m
+++ /dev/null
@@ -1,124 +0,0 @@
-/**
- *
- *    Copyright (c) 2022 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 "CHIPDeviceControllerOverXPC+AttributeCache.h"
-#import "CHIPDeviceControllerOverXPC_Internal.h"
-#import "CHIPError.h"
-#import "CHIPLogging.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@implementation CHIPDeviceControllerOverXPC (AttributeCache)
-
-- (void)subscribeAttributeCacheWithNodeId:(uint64_t)nodeId
-                                   params:(CHIPSubscribeParams * _Nullable)params
-                               completion:(void (^)(NSError * _Nullable error))completion
-{
-    dispatch_async(self.workQueue, ^{
-        dispatch_group_t group = dispatch_group_create();
-        if (!self.controllerId) {
-            dispatch_group_enter(group);
-            [self.xpcConnection getProxyHandleWithCompletion:^(
-                dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) {
-                if (handle) {
-                    [handle.proxy getAnyDeviceControllerWithCompletion:^(id _Nullable controller, NSError * _Nullable error) {
-                        if (error) {
-                            CHIP_LOG_ERROR("Failed to fetch any shared remote controller");
-                        } else {
-                            self.controllerId = controller;
-                        }
-                        dispatch_group_leave(group);
-                        __auto_type handleRetainer = handle;
-                        (void) handleRetainer;
-                    }];
-                } else {
-                    CHIP_LOG_ERROR("XPC disconnected while retrieving any shared remote controller");
-                    dispatch_group_leave(group);
-                }
-            }];
-        }
-        dispatch_group_notify(group, self.workQueue, ^{
-            if (self.controllerId) {
-                [self.xpcConnection getProxyHandleWithCompletion:^(
-                    dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) {
-                    if (handle) {
-                        [handle.proxy subscribeAttributeCacheWithController:self.controllerId
-                                                                     nodeId:nodeId
-                                                                     params:[CHIPDeviceController encodeXPCSubscribeParams:params]
-                                                                 completion:^(NSError * _Nullable error) {
-                                                                     if (error) {
-                                                                         CHIP_LOG_ERROR("Attribute cache subscription for "
-                                                                                        "controller %@ and node %llu failed: %@",
-                                                                             self.controllerId, nodeId, error);
-                                                                         completion(error);
-                                                                         return;
-                                                                     }
-                                                                     completion(nil);
-                                                                     __auto_type handleRetainer = handle;
-                                                                     (void) handleRetainer;
-                                                                 }];
-                    } else {
-                        CHIP_LOG_ERROR(
-                            "Attribute cache subscription for controller %@ and node %llu failed due to XPC connection failure",
-                            self.controllerId, nodeId);
-                        completion([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
-                    }
-                }];
-            } else {
-                CHIP_LOG_ERROR("Attribute cache subscription for node %llu failed due to lack of controller ID", nodeId);
-                completion([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
-            }
-        });
-    });
-}
-
-- (void)readAttributeCacheWithNodeId:(uint64_t)nodeId
-                          endpointId:(NSNumber * _Nullable)endpointId
-                           clusterId:(NSNumber * _Nullable)clusterId
-                         attributeId:(NSNumber * _Nullable)attributeId
-                          completion:(void (^)(id _Nullable values, NSError * _Nullable error))completion
-{
-    dispatch_async(self.workQueue, ^{
-        if (!self.controllerId) {
-            CHIP_LOG_ERROR("Attribute cache wasn't subscribed yet.");
-            completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
-            return;
-        }
-        [self.xpcConnection getProxyHandleWithCompletion:^(
-            dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) {
-            if (handle) {
-                [handle.proxy readAttributeCacheWithController:self.controllerId
-                                                        nodeId:nodeId
-                                                    endpointId:endpointId
-                                                     clusterId:clusterId
-                                                   attributeId:attributeId
-                                                    completion:^(id _Nullable values, NSError * _Nullable error) {
-                                                        completion([CHIPDeviceController decodeXPCResponseValues:values], error);
-                                                        __auto_type handleRetainer = handle;
-                                                        (void) handleRetainer;
-                                                    }];
-            } else {
-                CHIP_LOG_ERROR("Attribute cache read failed due to XPC connection failure");
-                completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
-            }
-        }];
-    });
-}
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC.m b/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC.m
index 3c4ca4d586e704..4ae6f7051693ee 100644
--- a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC.m
+++ b/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC.m
@@ -15,7 +15,6 @@
  *    limitations under the License.
  */
 
-#import "CHIPDeviceControllerOverXPC+AttributeCache.h"
 #import "CHIPDeviceControllerOverXPC_Internal.h"
 
 #import "CHIPDeviceController+XPC.h"
diff --git a/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m b/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m
index 8d11e8cb902cee..cd329a11dc8f1a 100644
--- a/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m
+++ b/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m
@@ -17,6 +17,7 @@
 
 #import "CHIPDeviceOverXPC.h"
 
+#import "CHIPAttributeCacheContainer+XPC.h"
 #import "CHIPCluster.h"
 #import "CHIPDeviceController+XPC.h"
 #import "CHIPDeviceControllerXPCConnection.h"
@@ -48,14 +49,42 @@ - (instancetype)initWithController:(id<NSCopying>)controller
 - (void)subscribeWithQueue:(dispatch_queue_t)queue
                 minInterval:(uint16_t)minInterval
                 maxInterval:(uint16_t)maxInterval
+                     params:(nullable CHIPSubscribeParams *)params
+             cacheContainer:(CHIPAttributeCacheContainer * _Nullable)attributeCacheContainer
               reportHandler:(void (^)(NSArray * _Nullable value, NSError * _Nullable error))reportHandler
     subscriptionEstablished:(void (^_Nullable)(void))subscriptionEstablishedHandler
 {
-    dispatch_async(queue, ^{
-        CHIP_LOG_ERROR("All attribute subscription is not supported by remote device");
-        subscriptionEstablishedHandler();
-        reportHandler(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
-    });
+    CHIP_LOG_DEBUG("Subscribing all attributes... Note that reportHandler is not supported.");
+    if (attributeCacheContainer) {
+        [attributeCacheContainer setXPCConnection:_xpcConnection controllerId:self.controller deviceId:self.nodeId];
+    }
+    [_xpcConnection getProxyHandleWithCompletion:^(
+        dispatch_queue_t _Nonnull proxyQueue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) {
+        if (handle) {
+            [handle.proxy subscribeWithController:self.controller
+                                           nodeId:self.nodeId
+                                      minInterval:@(minInterval)
+                                      maxInterval:@(maxInterval)
+                                           params:[CHIPDeviceController encodeXPCSubscribeParams:params]
+                                      shouldCache:(attributeCacheContainer != nil)
+                                       completion:^(NSError * _Nullable error) {
+                                           dispatch_async(queue, ^{
+                                               if (error) {
+                                                   reportHandler(nil, error);
+                                               } else {
+                                                   subscriptionEstablishedHandler();
+                                               }
+                                           });
+                                           __auto_type handleRetainer = handle;
+                                           (void) handleRetainer;
+                                       }];
+        } else {
+            CHIP_LOG_ERROR("Failed to obtain XPC connection to write attribute");
+            dispatch_async(queue, ^{
+                reportHandler(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
+            });
+        }
+    }];
 }
 
 - (void)readAttributeWithEndpointId:(NSNumber * _Nullable)endpointId
diff --git a/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m b/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m
index b075c9e4d45838..ace90dc5ea2895 100644
--- a/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m
+++ b/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m
@@ -688,16 +688,25 @@ - (void)test011_ReadCachedAttribute
     XCTestExpectation * subscribeExpectation = [self expectationWithDescription:@"Subscription complete"];
 
     NSLog(@"Subscribing...");
-    [attributeCacheContainer subscribeWithDeviceController:controller
-                                                  deviceId:kDeviceId
-                                                    params:nil
-                                               clientQueue:queue
-                                                completion:^(NSError * _Nullable error) {
-                                                    NSLog(@"Subscription complete with error: %@", error);
-                                                    XCTAssertNil(error);
-                                                    [subscribeExpectation fulfill];
-                                                }];
-    [self waitForExpectations:[NSArray arrayWithObject:subscribeExpectation] timeout:kTimeoutInSeconds];
+    __block void (^reportHandler)(NSArray * _Nullable value, NSError * _Nullable error);
+    [device subscribeWithQueue:queue
+        minInterval:2
+        maxInterval:60
+        params:nil
+        cacheContainer:attributeCacheContainer
+        reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) {
+            NSLog(@"Received report: %@, error: %@", value, error);
+            if (reportHandler) {
+                __auto_type handler = reportHandler;
+                reportHandler = nil;
+                handler(value, error);
+            }
+        }
+        subscriptionEstablished:^{
+            NSLog(@"Subscription established");
+            [subscribeExpectation fulfill];
+        }];
+    [self waitForExpectations:@[ subscribeExpectation ] timeout:60];
 
     // Invoke command to set the attribute to a known state
     XCTestExpectation * commandExpectation = [self expectationWithDescription:@"Command invoked"];
@@ -712,11 +721,9 @@ - (void)test011_ReadCachedAttribute
     }];
     [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds];
 
-    // Wait till reports arrive from accessory. It turned out accessory could generate very lengthy initial reports.
+    // Wait till reports arrive from accessory.
     NSLog(@"Waiting for reports from accessory...");
-    __auto_type idleExpectation = [self expectationWithDescription:@"Must not break out of idle"];
-    idleExpectation.inverted = YES;
-    [self waitForExpectations:[NSArray arrayWithObject:idleExpectation] timeout:60];
+    sleep(5);
 
     // Read cache
     NSLog(@"Reading from cache...");
@@ -749,6 +756,22 @@ - (void)test011_ReadCachedAttribute
         }];
     [self waitForExpectations:[NSArray arrayWithObject:newSubscriptionEstablished] timeout:kTimeoutInSeconds];
 
+    __auto_type reportExpectation = [self expectationWithDescription:@"Report handler called"];
+    reportHandler = ^(NSArray * _Nullable value, NSError * _Nullable error) {
+        NSLog(@"Report received: %@, error: %@", value, error);
+        for (CHIPAttributeReport * report in value) {
+            if ([report.path.endpoint isEqualToNumber:@1] && [report.path.cluster isEqualToNumber:@6] &&
+                [report.path.attribute isEqualToNumber:@0]) {
+                NSLog(@"Report value for OnOff: %@", report.value);
+                XCTAssertNotNil(report.value);
+                XCTAssertTrue([report.value isKindOfClass:[NSNumber class]]);
+                XCTAssertEqual([report.value boolValue], NO);
+                [reportExpectation fulfill];
+                break;
+            }
+        }
+    };
+
     NSLog(@"Invoking another command...");
     commandExpectation = [self expectationWithDescription:@"Command invoked"];
     [cluster offWithCompletionHandler:^(NSError * _Nullable err) {
@@ -760,12 +783,10 @@ - (void)test011_ReadCachedAttribute
 
     // Wait till reports arrive from accessory.
     NSLog(@"Waiting for reports from accessory...");
-    idleExpectation = [self expectationWithDescription:@"Must not break out of idle"];
-    idleExpectation.inverted = YES;
-    [self waitForExpectations:[NSArray arrayWithObject:idleExpectation] timeout:3];
+    [self waitForExpectations:@[ reportExpectation ] timeout:kTimeoutInSeconds];
 
     NSLog(@"Disconnect accessory to test cache...");
-    idleExpectation = [self expectationWithDescription:@"Must not break out of idle"];
+    __auto_type idleExpectation = [self expectationWithDescription:@"Must not break out of idle"];
     idleExpectation.inverted = YES;
     [self waitForExpectations:[NSArray arrayWithObject:idleExpectation] timeout:10];
 
diff --git a/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m b/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m
index 5e5502d7c35973..90e56447df9df7 100644
--- a/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m
+++ b/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m
@@ -341,30 +341,54 @@ - (void)stopReportsWithController:(id _Nullable)controller nodeId:(uint64_t)node
     }
 }
 
-- (void)subscribeAttributeCacheWithController:(id _Nullable)controller
-                                       nodeId:(uint64_t)nodeId
-                                       params:(NSDictionary<NSString *, id> * _Nullable)params
-                                   completion:(void (^)(NSError * _Nullable error))completion
+- (void)subscribeWithController:(id _Nullable)controller
+                         nodeId:(uint64_t)nodeId
+                    minInterval:(NSNumber *)minInterval
+                    maxInterval:(NSNumber *)maxInterval
+                         params:(NSDictionary<NSString *, id> * _Nullable)params
+                    shouldCache:(BOOL)shouldCache
+                     completion:(void (^)(NSError * _Nullable error))completion
 {
     __auto_type sharedController = [CHIPDeviceController sharedController];
     if (sharedController) {
-        CHIPAttributeCacheContainer * attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init];
-        [attributeCacheContainer
-            subscribeWithDeviceController:sharedController
-                                 deviceId:nodeId
+        CHIPAttributeCacheContainer * attributeCacheContainer;
+        if (shouldCache) {
+            attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init];
+        }
+
+        [sharedController getConnectedDevice:nodeId
+                                       queue:dispatch_get_main_queue()
+                           completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) {
+                               if (error) {
+                                   NSLog(@"Error: Failed to get connected device (%llu) for attribute cache: %@", nodeId, error);
+                                   completion(error);
+                                   return;
+                               }
+                               NSMutableArray * established = [NSMutableArray arrayWithCapacity:1];
+                               [established addObject:@NO];
+                               [device subscribeWithQueue:dispatch_get_main_queue()
+                                   minInterval:[minInterval unsignedShortValue]
+                                   maxInterval:[maxInterval unsignedShortValue]
                                    params:[CHIPDeviceController decodeXPCSubscribeParams:params]
-                              clientQueue:dispatch_get_main_queue()
-                               completion:^(NSError * _Nullable error) {
-                                   NSNumber * nodeIdNumber = [NSNumber numberWithUnsignedLongLong:nodeId];
-                                   if (error) {
-                                       NSLog(@"Failed to have subscribe attribute by cache");
-                                       [self.attributeCacheDictionary removeObjectForKey:nodeIdNumber];
-                                   } else {
-                                       NSLog(@"Attribute cache for node %llu successfully subscribed attributes", nodeId);
-                                       [self.attributeCacheDictionary setObject:attributeCacheContainer forKey:nodeIdNumber];
+                                   cacheContainer:attributeCacheContainer
+                                   reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) {
+                                       NSLog(@"Report received: %@, error: %@", value, error);
+                                       if (error && ![established[0] boolValue]) {
+                                           established[0] = @YES;
+                                           completion(error);
+                                       }
                                    }
-                                   completion(error);
-                               }];
+                                   subscriptionEstablished:^{
+                                       NSLog(@"Attribute cache subscription succeeded for device %llu", nodeId);
+                                       if (attributeCacheContainer) {
+                                           [self.attributeCacheDictionary setObject:attributeCacheContainer forKey:@(nodeId)];
+                                       }
+                                       if (![established[0] boolValue]) {
+                                           established[0] = @YES;
+                                           completion(nil);
+                                       }
+                                   }];
+                           }];
     } else {
         NSLog(@"Failed to get shared controller");
         completion([NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]);
@@ -418,12 +442,6 @@ - (void)readAttributeCacheWithController:(id _Nullable)controller
     return mConnectedDevice;
 }
 
-static CHIPDeviceController * GetDeviceController(void)
-{
-    XCTAssertNotNil(mDeviceController);
-    return mDeviceController;
-}
-
 @interface CHIPRemoteDeviceSampleTestPairingDelegate : NSObject <CHIPDevicePairingDelegate>
 @property (nonatomic, strong) XCTestExpectation * expectation;
 @end
@@ -1738,23 +1756,22 @@ - (void)test900_SubscribeAttributeCache
     CHIPDevice * device = GetConnectedDevice();
     dispatch_queue_t queue = dispatch_get_main_queue();
 
-    __auto_type * deviceController = GetDeviceController();
     CHIPAttributeCacheContainer * attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init];
-    NSLog(@"Setting up attribute cache...");
-    [attributeCacheContainer subscribeWithDeviceController:deviceController
-                                                  deviceId:kDeviceId
-                                                    params:nil
-                                               clientQueue:queue
-                                                completion:^(NSError * _Nullable error) {
-                                                    NSLog(@"Attribute cache subscribed attributes");
-                                                    [expectation fulfill];
-                                                }];
-    [self waitForExpectations:@[ expectation ] timeout:kTimeoutInSeconds];
-
-    // Wait for initial report to be collected. This can take very long.
+    NSLog(@"Setting up attribute cache subscription...");
+    [device subscribeWithQueue:queue
+        minInterval:1
+        maxInterval:60
+        params:nil
+        cacheContainer:attributeCacheContainer
+        reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) {
+            NSLog(@"Report for attribute cache: %@, error: %@", value, error);
+        }
+        subscriptionEstablished:^{
+            NSLog(@"Attribute cache subscribed attributes");
+            [expectation fulfill];
+        }];
+    // Wait for subscription establishment. This can take very long to collect initial reports.
     NSLog(@"Waiting for initial report...");
-    expectation = [self expectationWithDescription:@"Must not jump out while waiting for initial report"];
-    expectation.inverted = YES;
     [self waitForExpectations:@[ expectation ] timeout:120];
 
     // Send command to reset attribute state
diff --git a/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m b/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m
index 706b326a63697f..692144636d2bc7 100644
--- a/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m
+++ b/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m
@@ -76,6 +76,61 @@ - (NSString *)description
 }
 @end
 
+@interface CHIPAttributeCacheContainer (Test)
+// Obsolete method is moved to this test suite to keep tests compatible
+- (void)subscribeWithDeviceController:(CHIPDeviceController *)deviceController
+                             deviceId:(uint64_t)deviceId
+                               params:(CHIPSubscribeParams * _Nullable)params
+                          clientQueue:(dispatch_queue_t)clientQueue
+                           completion:(void (^)(NSError * _Nullable error))completion;
+@end
+
+@implementation CHIPAttributeCacheContainer (Test)
+- (void)subscribeWithDeviceController:(CHIPDeviceController *)deviceController
+                             deviceId:(uint64_t)deviceId
+                               params:(CHIPSubscribeParams * _Nullable)params
+                          clientQueue:clientQueue
+                           completion:(void (^)(NSError * _Nullable error))completion
+{
+    __auto_type workQueue = dispatch_get_main_queue();
+    __auto_type completionHandler = ^(NSError * _Nullable error) {
+        dispatch_async(clientQueue, ^{
+            completion(error);
+        });
+    };
+    [deviceController getConnectedDevice:deviceId
+                                   queue:workQueue
+                       completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) {
+                           if (error) {
+                               NSLog(@"Error: Failed to get connected device (%llu) for attribute cache: %@", deviceId, error);
+                               completionHandler(error);
+                               return;
+                           }
+                           __auto_type established = [NSMutableArray arrayWithCapacity:1];
+                           [established addObject:@NO];
+                           [device subscribeWithQueue:clientQueue
+                               minInterval:1
+                               maxInterval:43200
+                               params:params
+                               cacheContainer:self
+                               reportHandler:^(NSArray * _Nullable value, NSError * _Nullable error) {
+                                   NSLog(@"Report received for attribute cache: %@, error: %@", value, error);
+                                   if (![established[0] boolValue]) {
+                                       established[0] = @YES;
+                                       completionHandler(error);
+                                   }
+                               }
+                               subscriptionEstablished:^{
+                                   NSLog(@"Attribute cache subscription succeeded for device %llu", deviceId);
+                                   if (![established[0] boolValue]) {
+                                       established[0] = @YES;
+                                       completionHandler(nil);
+                                   }
+                               }];
+                       }];
+}
+@end
+
 @interface CHIPXPCProtocolTests<NSXPCListenerDelegate, CHIPRemoteDeviceProtocol> : XCTestCase
 
 @property (nonatomic, readwrite, strong) NSXPCListener * xpcListener;
@@ -103,8 +158,9 @@ @interface CHIPXPCProtocolTests<NSXPCListenerDelegate, CHIPRemoteDeviceProtocol>
     NSNumber * _Nullable clusterId, NSNumber * _Nullable attributeId, NSNumber * minInterval, NSNumber * maxInterval,
     CHIPSubscribeParams * _Nullable params, void (^establishedHandler)(void));
 @property (readwrite, strong) void (^handleStopReports)(id controller, uint64_t nodeId, void (^completion)(void));
-@property (readwrite, strong) void (^handleSubscribeAttributeCache)
-    (id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error));
+@property (readwrite, strong) void (^handleSubscribeAll)(id controller, uint64_t nodeId, NSNumber * minInterval,
+    NSNumber * maxInterval, CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error))
+    ;
 @property (readwrite, strong) void (^handleReadAttributeCache)
     (id controller, uint64_t nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
         NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error));
@@ -221,14 +277,18 @@ - (void)stopReportsWithController:(id)controller nodeId:(uint64_t)nodeId complet
     });
 }
 
-- (void)subscribeAttributeCacheWithController:(id _Nullable)controller
-                                       nodeId:(uint64_t)nodeId
-                                       params:(NSDictionary<NSString *, id> * _Nullable)params
-                                   completion:(void (^)(NSError * _Nullable error))completion
+- (void)subscribeWithController:(id _Nullable)controller
+                         nodeId:(uint64_t)nodeId
+                    minInterval:(NSNumber *)minInterval
+                    maxInterval:(NSNumber *)maxInterval
+                         params:(NSDictionary<NSString *, id> * _Nullable)params
+                    shouldCache:(BOOL)shouldCache
+                     completion:(void (^)(NSError * _Nullable error))completion
 {
     dispatch_async(dispatch_get_main_queue(), ^{
-        XCTAssertNotNil(self.handleSubscribeAttributeCache);
-        self.handleSubscribeAttributeCache(controller, nodeId, [CHIPDeviceController decodeXPCSubscribeParams:params], completion);
+        XCTAssertNotNil(self.handleSubscribeAll);
+        self.handleSubscribeAll(controller, nodeId, minInterval, maxInterval,
+            [CHIPDeviceController decodeXPCSubscribeParams:params], shouldCache, completion);
     });
 }
 
@@ -2231,15 +2291,15 @@ - (void)testSubscribeAttributeCacheSuccess
 
     __auto_type uuid = self.controllerUUID;
     __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init];
-    _handleSubscribeAttributeCache
-        = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) {
-              NSLog(@"Subscribe attribute cache called");
-              XCTAssertTrue([controller isEqualToString:uuid]);
-              XCTAssertEqual(nodeId, myNodeId);
-              XCTAssertNil(params);
-              [callExpectation fulfill];
-              completion(nil);
-          };
+    _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval,
+        CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) {
+        NSLog(@"Subscribe called");
+        XCTAssertTrue([controller isEqualToString:uuid]);
+        XCTAssertEqual(nodeId, myNodeId);
+        XCTAssertNil(params);
+        [callExpectation fulfill];
+        completion(nil);
+    };
 
     _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
     [attributeCacheContainer subscribeWithDeviceController:_remoteDeviceController
@@ -2267,17 +2327,17 @@ - (void)testSubscribeAttributeCacheWithParamsSuccess
 
     __auto_type uuid = self.controllerUUID;
     __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init];
-    _handleSubscribeAttributeCache
-        = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) {
-              NSLog(@"Subscribe attribute cache called");
-              XCTAssertTrue([controller isEqualToString:uuid]);
-              XCTAssertEqual(nodeId, myNodeId);
-              XCTAssertNotNil(params);
-              XCTAssertEqual([params.fabricFiltered boolValue], [myParams.fabricFiltered boolValue]);
-              XCTAssertEqual([params.keepPreviousSubscriptions boolValue], [myParams.keepPreviousSubscriptions boolValue]);
-              [callExpectation fulfill];
-              completion(nil);
-          };
+    _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval,
+        CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) {
+        NSLog(@"Subscribe attribute cache called");
+        XCTAssertTrue([controller isEqualToString:uuid]);
+        XCTAssertEqual(nodeId, myNodeId);
+        XCTAssertNotNil(params);
+        XCTAssertEqual([params.fabricFiltered boolValue], [myParams.fabricFiltered boolValue]);
+        XCTAssertEqual([params.keepPreviousSubscriptions boolValue], [myParams.keepPreviousSubscriptions boolValue]);
+        [callExpectation fulfill];
+        completion(nil);
+    };
 
     _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
     [attributeCacheContainer subscribeWithDeviceController:_remoteDeviceController
@@ -2303,15 +2363,15 @@ - (void)testSubscribeAttributeCacheFailure
 
     __auto_type uuid = self.controllerUUID;
     __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init];
-    _handleSubscribeAttributeCache
-        = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) {
-              NSLog(@"Subscribe attribute cache called");
-              XCTAssertTrue([controller isEqualToString:uuid]);
-              XCTAssertEqual(nodeId, myNodeId);
-              XCTAssertNil(params);
-              [callExpectation fulfill];
-              completion(myError);
-          };
+    _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval,
+        CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) {
+        NSLog(@"Subscribe attribute cache called");
+        XCTAssertTrue([controller isEqualToString:uuid]);
+        XCTAssertEqual(nodeId, myNodeId);
+        XCTAssertNil(params);
+        [callExpectation fulfill];
+        completion(myError);
+    };
 
     _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
     [attributeCacheContainer subscribeWithDeviceController:_remoteDeviceController
@@ -2347,14 +2407,14 @@ - (void)testReadAttributeCacheSuccess
 
     __auto_type uuid = self.controllerUUID;
     __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init];
-    _handleSubscribeAttributeCache
-        = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) {
-              NSLog(@"Subscribe attribute cache called");
-              XCTAssertTrue([controller isEqualToString:uuid]);
-              XCTAssertEqual(nodeId, myNodeId);
-              XCTAssertNil(params);
-              completion(nil);
-          };
+    _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval,
+        CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) {
+        NSLog(@"Subscribe attribute cache called");
+        XCTAssertTrue([controller isEqualToString:uuid]);
+        XCTAssertEqual(nodeId, myNodeId);
+        XCTAssertNil(params);
+        completion(nil);
+    };
 
     _handleReadAttributeCache = ^(id controller, uint64_t nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
         NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error)) {
@@ -2409,14 +2469,14 @@ - (void)testReadAttributeCacheFailure
 
     __auto_type uuid = self.controllerUUID;
     __auto_type attributeCacheContainer = [[CHIPAttributeCacheContainer alloc] init];
-    _handleSubscribeAttributeCache
-        = ^(id controller, uint64_t nodeId, CHIPSubscribeParams * _Nullable params, void (^completion)(NSError * _Nullable error)) {
-              NSLog(@"Subscribe attribute cache called");
-              XCTAssertTrue([controller isEqualToString:uuid]);
-              XCTAssertEqual(nodeId, myNodeId);
-              XCTAssertNil(params);
-              completion(nil);
-          };
+    _handleSubscribeAll = ^(id controller, uint64_t nodeId, NSNumber * minInterval, NSNumber * maxInterval,
+        CHIPSubscribeParams * _Nullable params, BOOL shouldCache, void (^completion)(NSError * _Nullable error)) {
+        NSLog(@"Subscribe attribute cache called");
+        XCTAssertTrue([controller isEqualToString:uuid]);
+        XCTAssertEqual(nodeId, myNodeId);
+        XCTAssertNil(params);
+        completion(nil);
+    };
 
     _handleReadAttributeCache = ^(id controller, uint64_t nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
         NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error)) {