Revert "Refactor iOS integration_test API to support Swift, dynamically add native tests (#88013)" (#88377)

This reverts commit 9e3de9a3289fbcdd88ff73c808333a58146be87d.
diff --git a/packages/integration_test/README.md b/packages/integration_test/README.md
index f9d01ef..0837112 100644
--- a/packages/integration_test/README.md
+++ b/packages/integration_test/README.md
@@ -281,27 +281,14 @@
 flutter build ios --config-only integration_test/foo_test.dart
 ```
 
-In Xcode, add a test file called `RunnerTests.m` or `RunnerTests.swift` (or any name of your choice) to the new target and
+In Xcode, add a test file called `RunnerTests.m` (or any name of your choice) to the new target and
 replace the file:
 
 ```objective-c
 @import XCTest;
 @import integration_test;
 
-@interface RunnerTests : FLTIntegrationTestCase
-@end
-
-@implementation RunnerTests
-@end
-```
-or in Swift:
-````swift
-import integration_test
-import XCTest
-
-class RunnerSwiftTests: FLTIntegrationTestCase {
-}
-
+INTEGRATION_TEST_IOS_RUNNER(RunnerTests)
 ```
 
 Run `Product > Test` to run the integration tests on your selected device.
diff --git a/packages/integration_test/example/integration_test/_extended_test_io.dart b/packages/integration_test/example/integration_test/_extended_test_io.dart
index 8c2456a..377aa42 100644
--- a/packages/integration_test/example/integration_test/_extended_test_io.dart
+++ b/packages/integration_test/example/integration_test/_extended_test_io.dart
@@ -25,24 +25,6 @@
     // Build our app.
     app.main();
 
-    // Pump a frame.
-    await tester.pumpAndSettle();
-
-    // Verify that platform version is retrieved.
-    expect(
-      find.byWidgetPredicate(
-        (Widget widget) =>
-            widget is Text &&
-            widget.data!.startsWith('Platform: ${Platform.operatingSystem}'),
-      ),
-      findsOneWidget,
-    );
-  });
-
-  testWidgets('verify screenshot', (WidgetTester tester) async {
-    // Build our app.
-    app.main();
-
     // On Android, this is required prior to taking the screenshot.
     await binding.convertFlutterSurfaceToImage();
 
@@ -57,5 +39,15 @@
     expect(secondPng.isNotEmpty, isTrue);
 
     expect(listEquals(firstPng, secondPng), isTrue);
+
+    // Verify that platform version is retrieved.
+    expect(
+      find.byWidgetPredicate(
+        (Widget widget) =>
+            widget is Text &&
+            widget.data!.startsWith('Platform: ${Platform.operatingSystem}'),
+      ),
+      findsOneWidget,
+    );
   });
 }
diff --git a/packages/integration_test/example/ios/Runner.xcodeproj/project.pbxproj b/packages/integration_test/example/ios/Runner.xcodeproj/project.pbxproj
index d3b7c24..a519162 100644
--- a/packages/integration_test/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/integration_test/example/ios/Runner.xcodeproj/project.pbxproj
@@ -10,14 +10,13 @@
 		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
 		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
 		4DB404AC7CF2C89658A01173 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 81BF64028CE7AE2E6196250D /* libPods-RunnerTests.a */; };
-		769541CB23A0351900E5C350 /* RunnerObjCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 769541CA23A0351900E5C350 /* RunnerObjCTests.m */; };
+		769541CB23A0351900E5C350 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 769541CA23A0351900E5C350 /* RunnerTests.m */; };
 		978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
 		97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
 		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
 		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
 		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
 		C2A5EDF11F4FDBF3ABFD7006 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 625A5A90428602E25C0DE2F6 /* libPods-Runner.a */; };
-		F77B951926C3504400F785B3 /* RunnerSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77B951826C3504400F785B3 /* RunnerSwiftTests.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -53,7 +52,7 @@
 		750225973AAB5D7832AFA60C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
 		769541BF23A0337200E5C350 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
 		769541C823A0351900E5C350 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
-		769541CA23A0351900E5C350 /* RunnerObjCTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerObjCTests.m; sourceTree = "<group>"; };
+		769541CA23A0351900E5C350 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = "<group>"; };
 		769541CC23A0351900E5C350 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
 		7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
@@ -69,7 +68,6 @@
 		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		D69CCAD5F82E76E2E22BFA96 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
 		E23EF4D45DAE46B9DDB9B445 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
-		F77B951826C3504400F785B3 /* RunnerSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerSwiftTests.swift; sourceTree = "<group>"; };
 		FCE3953801588FC13ED9E898 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -106,8 +104,7 @@
 		769541C923A0351900E5C350 /* RunnerTests */ = {
 			isa = PBXGroup;
 			children = (
-				769541CA23A0351900E5C350 /* RunnerObjCTests.m */,
-				F77B951826C3504400F785B3 /* RunnerSwiftTests.swift */,
+				769541CA23A0351900E5C350 /* RunnerTests.m */,
 				769541CC23A0351900E5C350 /* Info.plist */,
 			);
 			path = RunnerTests;
@@ -236,7 +233,6 @@
 				TargetAttributes = {
 					769541C723A0351900E5C350 = {
 						CreatedOnToolsVersion = 11.0;
-						LastSwiftMigration = 1300;
 						ProvisioningStyle = Automatic;
 						TestTargetID = 97C146ED1CF9000F007C117D;
 					};
@@ -365,8 +361,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				769541CB23A0351900E5C350 /* RunnerObjCTests.m in Sources */,
-				F77B951926C3504400F785B3 /* RunnerSwiftTests.swift in Sources */,
+				769541CB23A0351900E5C350 /* RunnerTests.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -480,14 +475,11 @@
 			baseConfigurationReference = 09505407E99803EF7AA92DE7 /* Pods-RunnerTests.debug.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
-				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				INFOPLIST_FILE = RunnerTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.example.instrumentationAdapterExample.RunnerTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 5.0;
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
 			};
 			name = Debug;
@@ -497,13 +489,11 @@
 			baseConfigurationReference = FCE3953801588FC13ED9E898 /* Pods-RunnerTests.release.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
-				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				INFOPLIST_FILE = RunnerTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.example.instrumentationAdapterExample.RunnerTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_VERSION = 5.0;
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
 			};
 			name = Release;
@@ -513,13 +503,11 @@
 			baseConfigurationReference = 750225973AAB5D7832AFA60C /* Pods-RunnerTests.profile.xcconfig */;
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
-				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				INFOPLIST_FILE = RunnerTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.example.instrumentationAdapterExample.RunnerTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_VERSION = 5.0;
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
 			};
 			name = Profile;
diff --git a/packages/integration_test/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/integration_test/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index ea00904..72fa146 100644
--- a/packages/integration_test/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/integration_test/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -20,20 +20,6 @@
                ReferencedContainer = "container:Runner.xcodeproj">
             </BuildableReference>
          </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "NO"
-            buildForProfiling = "NO"
-            buildForArchiving = "NO"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "769541C723A0351900E5C350"
-               BuildableName = "RunnerTests.xctest"
-               BlueprintName = "RunnerTests"
-               ReferencedContainer = "container:Runner.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
       </BuildActionEntries>
    </BuildAction>
    <TestAction
diff --git a/packages/integration_test/example/ios/RunnerTests/RunnerObjCTests.m b/packages/integration_test/example/ios/RunnerTests/RunnerObjCTests.m
deleted file mode 100644
index 39dce98..0000000
--- a/packages/integration_test/example/ios/RunnerTests/RunnerObjCTests.m
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2014 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-@import integration_test;
-@import XCTest;
-
-// Test without macro.
-@interface RunnerObjCTests : FLTIntegrationTestCase
-@end
-
-@implementation RunnerObjCTests
-
-+ (NSArray<NSInvocation *> *)testInvocations {
-  // Add a test to verify the Flutter dart tests have been dynamically added to this test case.
-  SEL selector = @selector(testDynamicTestMethods);
-  NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector];
-  NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
-  invocation.selector = selector;
-
-  return [super.testInvocations arrayByAddingObject:invocation];
-}
-
-- (void)testDynamicTestMethods {
-  XCTAssertTrue([self respondsToSelector:NSSelectorFromString(@"testVerifyScreenshot")]);
-  XCTAssertTrue([self respondsToSelector:NSSelectorFromString(@"testVerifyText")]);
-  XCTAssertTrue([self respondsToSelector:NSSelectorFromString(@"screenshotPlaceholder")]);
-}
-
-@end
-
-// Test deprecated macro. Do not use.
-INTEGRATION_TEST_IOS_RUNNER(RunnerObjCMacroTests)
-
-@interface DeprecatedIntegrationTestIosTests : XCTestCase
-@end
-
-@implementation DeprecatedIntegrationTestIosTests
-
-- (void)testIntegrationTest {
-  NSString *testResult;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-  BOOL testPass = [[IntegrationTestIosTest new] testIntegrationTest:&testResult];
-#pragma clang diagnostic pop
-  XCTAssertTrue(testPass, @"%@", testResult);
-}
-
-@end
diff --git a/packages/integration_test/example/ios/RunnerTests/RunnerSwiftTests.swift b/packages/integration_test/example/ios/RunnerTests/RunnerTests.m
similarity index 64%
rename from packages/integration_test/example/ios/RunnerTests/RunnerSwiftTests.swift
rename to packages/integration_test/example/ios/RunnerTests/RunnerTests.m
index 433f828..edd7f10 100644
--- a/packages/integration_test/example/ios/RunnerTests/RunnerSwiftTests.swift
+++ b/packages/integration_test/example/ios/RunnerTests/RunnerTests.m
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import integration_test
-import XCTest
+@import XCTest;
+@import integration_test;
 
-class RunnerSwiftTests: FLTIntegrationTestCase {
-}
+INTEGRATION_TEST_IOS_RUNNER(RunnerTests)
diff --git a/packages/integration_test/ios/Classes/FLTIntegrationTestCase.h b/packages/integration_test/ios/Classes/FLTIntegrationTestCase.h
deleted file mode 100644
index 64a6ac6..0000000
--- a/packages/integration_test/ios/Classes/FLTIntegrationTestCase.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// XCTest is weakly linked.
-#if __has_include(<XCTest/XCTest.h>)
-
-@import XCTest;
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface FLTIntegrationTestCase : XCTestCase
-@end
-
-/*!
- Deprecated. Prefer directly inheriting from @c FLTIntegrationTestCase
- */
-#define INTEGRATION_TEST_IOS_RUNNER(__test_class)                                           \
-  @interface __test_class : FLTIntegrationTestCase                                          \
-  @end                                                                                      \
-                                                                                            \
-  @implementation __test_class                                                              \
-  @end
-
-NS_ASSUME_NONNULL_END
-
-#endif
diff --git a/packages/integration_test/ios/Classes/FLTIntegrationTestCase.m b/packages/integration_test/ios/Classes/FLTIntegrationTestCase.m
deleted file mode 100644
index e75bcf8..0000000
--- a/packages/integration_test/ios/Classes/FLTIntegrationTestCase.m
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2014 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// XCTest is weakly linked.
-#if __has_include(<XCTest/XCTest.h>)
-
-#import "FLTIntegrationTestCase.h"
-
-#import "FLTIntegrationTestRunner.h"
-#import "IntegrationTestPlugin.h"
-
-@import ObjectiveC.runtime;
-@import XCTest;
-
-@implementation FLTIntegrationTestCase
-
-+ (NSArray<NSInvocation *> *)testInvocations {
-  if (self == [FLTIntegrationTestCase class]) {
-    // Do not add any tests for this base class.
-    return @[];
-  }
-  FLTIntegrationTestRunner *integrationTestRunner = [FLTIntegrationTestRunner new];
-  NSMutableArray<NSInvocation *> *testInvocations = [NSMutableArray new];
-  [integrationTestRunner testIntegrationTestWithResults:^(NSString *testName, BOOL success, NSString *failureMessage) {
-    // For every Flutter dart test, dynamically generate an Objective-C method mirroring the test results
-    // so it is reported as a native XCTest run result.
-    IMP assertImplementation = imp_implementationWithBlock(^(id _self) {
-      XCTAssertTrue(success, @"%@", failureMessage);
-    });
-
-    // Create an appropriate XCTest method name based on the dart test name.
-    // Example: dart test "verify widget" becomes "testVerifyWidget"
-    NSString *upperCamelTestName = [testName.localizedCapitalizedString stringByReplacingOccurrencesOfString:@" " withString:@""];
-    NSString *testSelectorName = [NSString stringWithFormat:@"test%@", upperCamelTestName];
-    SEL testSelector = NSSelectorFromString(testSelectorName);
-    class_addMethod(self, testSelector, assertImplementation, "v@:");
-
-    // Add the new class method as a test invocation to the XCTestCase.
-    NSMethodSignature *signature = [self instanceMethodSignatureForSelector:testSelector];
-    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
-    invocation.selector = testSelector;
-
-    [testInvocations addObject:invocation];
-  }];
-
-  NSDictionary<NSString *, UIImage *> *capturedScreenshotsByName = integrationTestRunner.capturedScreenshotsByName;
-  if (capturedScreenshotsByName.count > 0) {
-    // If the Flutter dart tests have captured screenshots, add them to the XCTest bundle.
-    IMP screenshotImplementation = imp_implementationWithBlock(^(id _self) {
-      [capturedScreenshotsByName enumerateKeysAndObjectsUsingBlock:^(NSString *name, UIImage *screenshot, BOOL *stop) {
-        XCTAttachment *attachment = [XCTAttachment attachmentWithImage:screenshot];
-        attachment.lifetime = XCTAttachmentLifetimeKeepAlways;
-        if (name != nil) {
-          attachment.name = name;
-        }
-        [_self addAttachment:attachment];
-      }];
-    });
-
-    SEL attachmentSelector = NSSelectorFromString(@"screenshotPlaceholder");
-    class_addMethod(self, attachmentSelector, screenshotImplementation, "v@:");
-
-    NSMethodSignature *attachmentSignature = [self instanceMethodSignatureForSelector:attachmentSelector];
-    NSInvocation *attachmentInvocation = [NSInvocation invocationWithMethodSignature:attachmentSignature];
-    attachmentInvocation.selector = attachmentSelector;
-
-    [testInvocations addObject:attachmentInvocation];
-  }
-  return testInvocations;
-}
-
-@end
-
-#endif
diff --git a/packages/integration_test/ios/Classes/FLTIntegrationTestRunner.h b/packages/integration_test/ios/Classes/FLTIntegrationTestRunner.h
deleted file mode 100644
index 3b0407b..0000000
--- a/packages/integration_test/ios/Classes/FLTIntegrationTestRunner.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2014 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-@import Foundation;
-
-@class UIImage;
-
-NS_ASSUME_NONNULL_BEGIN
-
-typedef void (^FLTIntegrationTestResults)(NSString *testName, BOOL success, NSString *_Nullable failureMessage);
-
-
-@interface FLTIntegrationTestRunner : NSObject
-
-/**
- * Any screenshots captured by the plugin.
- */
-@property (copy, readonly) NSDictionary<NSString *, UIImage *> *capturedScreenshotsByName;
-
-/*!
- Start dart tests and wait for results.
-
- @param testResult Will be called once per every completed dart test.
- */
-- (void)testIntegrationTestWithResults:(NS_NOESCAPE FLTIntegrationTestResults)testResult;
-
-@end
-
-DEPRECATED_MSG_ATTRIBUTE("Use FLTIntegrationTestRunner instead.")
-@interface IntegrationTestIosTest : NSObject
-
-/*!
- Initate dart tests and wait for results.
-
- @param testResult Will be set to a string describing the results.
- @returns @c YES if all tests succeeded.
- */
-- (BOOL)testIntegrationTest:(NSString *_Nullable *_Nullable)testResult;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/packages/integration_test/ios/Classes/FLTIntegrationTestRunner.m b/packages/integration_test/ios/Classes/FLTIntegrationTestRunner.m
deleted file mode 100644
index 766102f..0000000
--- a/packages/integration_test/ios/Classes/FLTIntegrationTestRunner.m
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2014 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "FLTIntegrationTestRunner.h"
-
-#import "IntegrationTestPlugin.h"
-
-@import UIKit;
-
-@interface FLTIntegrationTestRunner ()
-
-@property IntegrationTestPlugin *integrationTestPlugin;
-
-@end
-
-@implementation FLTIntegrationTestRunner
-
-- (instancetype)init {
-  self = [super init];
-  _integrationTestPlugin = [IntegrationTestPlugin instance];
-
-  return self;
-}
-
-- (void)testIntegrationTestWithResults:(NS_NOESCAPE FLTIntegrationTestResults)testResult {
-  IntegrationTestPlugin *integrationTestPlugin = self.integrationTestPlugin;
-  UIViewController *rootViewController = UIApplication.sharedApplication.delegate.window.rootViewController;
-  if (![rootViewController isKindOfClass:[FlutterViewController class]]) {
-    testResult(@"setup", NO, @"rootViewController was not expected FlutterViewController");
-  }
-  FlutterViewController *flutterViewController = (FlutterViewController *)rootViewController;
-  [integrationTestPlugin setupChannels:flutterViewController.engine.binaryMessenger];
-
-  // Spin the runloop.
-  while (!integrationTestPlugin.testResults) {
-    [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
-  }
-
-  [integrationTestPlugin.testResults enumerateKeysAndObjectsUsingBlock:^(NSString *test, NSString *result, BOOL *stop) {
-    if ([result isEqualToString:@"success"]) {
-      testResult(test, YES, nil);
-    } else {
-      testResult(test, NO, result);
-    }
-  }];
-}
-
-- (NSDictionary<NSString *,UIImage *> *)capturedScreenshotsByName {
-  return self.integrationTestPlugin.capturedScreenshotsByName;
-}
-
-@end
-
-#pragma mark - Deprecated
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-implementations"
-
-@implementation IntegrationTestIosTest
-
-- (BOOL)testIntegrationTest:(NSString **)testResult {
-  NSLog(@"==================== Test Results =====================");
-  NSMutableArray<NSString *> *failedTests = [NSMutableArray array];
-  NSMutableArray<NSString *> *testNames = [NSMutableArray array];
-  [[FLTIntegrationTestRunner new] testIntegrationTestWithResults:^(NSString *testName, BOOL success, NSString *message) {
-    [testNames addObject:testName];
-    if (success) {
-      NSLog(@"%@ passed.", testName);
-    } else {
-      NSLog(@"%@ failed: %@", testName, message);
-      [failedTests addObject:testName];
-    }
-  }];
-  NSLog(@"================== Test Results End ====================");
-  BOOL testPass = failedTests.count == 0;
-  if (!testPass && testResult != NULL) {
-    *testResult =
-        [NSString stringWithFormat:@"Detected failed integration test(s) %@ among %@",
-                                   failedTests.description, testNames.description];
-  }
-  return testPass;
-}
-
-@end
-#pragma clang diagnostic pop
diff --git a/packages/integration_test/ios/Classes/IntegrationTestIosTest.h b/packages/integration_test/ios/Classes/IntegrationTestIosTest.h
new file mode 100644
index 0000000..333b0ec
--- /dev/null
+++ b/packages/integration_test/ios/Classes/IntegrationTestIosTest.h
@@ -0,0 +1,48 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol FLTIntegrationTestScreenshotDelegate;
+
+@interface IntegrationTestIosTest : NSObject
+
+- (instancetype)initWithScreenshotDelegate:(nullable id<FLTIntegrationTestScreenshotDelegate>)delegate NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Initate dart tests and wait for results.  @c testResult will be set to a string describing the results.
+ *
+ * @return @c YES if all tests succeeded.
+ */
+- (BOOL)testIntegrationTest:(NSString *_Nullable *_Nullable)testResult;
+
+@end
+
+#define INTEGRATION_TEST_IOS_RUNNER(__test_class)                                           \
+  @interface __test_class : XCTestCase<FLTIntegrationTestScreenshotDelegate>                \
+  @end                                                                                      \
+                                                                                            \
+  @implementation __test_class                                                              \
+                                                                                            \
+  - (void)testIntegrationTest {                                                             \
+    NSString *testResult;                                                                   \
+    IntegrationTestIosTest *integrationTestIosTest = integrationTestIosTest = [[IntegrationTestIosTest alloc] initWithScreenshotDelegate:self]; \
+    BOOL testPass = [integrationTestIosTest testIntegrationTest:&testResult];               \
+    XCTAssertTrue(testPass, @"%@", testResult);                                             \
+  }                                                                                         \
+                                                                                            \
+  - (void)didTakeScreenshot:(UIImage *)screenshot attachmentName:(NSString *)name {         \
+    XCTAttachment *attachment = [XCTAttachment attachmentWithImage:screenshot];             \
+    attachment.lifetime = XCTAttachmentLifetimeKeepAlways;                                  \
+    if (name != nil) {                                                                      \
+      attachment.name = name;                                                               \
+    }                                                                                       \
+    [self addAttachment:attachment];                                                        \
+  }                                                                                         \
+                                                                                            \
+  @end
+
+NS_ASSUME_NONNULL_END
diff --git a/packages/integration_test/ios/Classes/IntegrationTestIosTest.m b/packages/integration_test/ios/Classes/IntegrationTestIosTest.m
new file mode 100644
index 0000000..6a54ed2
--- /dev/null
+++ b/packages/integration_test/ios/Classes/IntegrationTestIosTest.m
@@ -0,0 +1,63 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "IntegrationTestIosTest.h"
+#import "IntegrationTestPlugin.h"
+
+@interface IntegrationTestIosTest()
+@property (nonatomic) IntegrationTestPlugin *integrationTestPlugin;
+@end
+
+@implementation IntegrationTestIosTest
+
+- (instancetype)initWithScreenshotDelegate:(id<FLTIntegrationTestScreenshotDelegate>)delegate {
+  self = [super init];
+  _integrationTestPlugin = [IntegrationTestPlugin instance];
+  _integrationTestPlugin.screenshotDelegate = delegate;
+  return self;
+}
+
+- (instancetype)init {
+  return [self initWithScreenshotDelegate:nil];
+}
+
+- (BOOL)testIntegrationTest:(NSString **)testResult {
+  IntegrationTestPlugin *integrationTestPlugin = self.integrationTestPlugin;
+
+  UIViewController *rootViewController =
+      [[[[UIApplication sharedApplication] delegate] window] rootViewController];
+  if (![rootViewController isKindOfClass:[FlutterViewController class]]) {
+    NSLog(@"expected FlutterViewController as rootViewController.");
+    return NO;
+  }
+  FlutterViewController *flutterViewController = (FlutterViewController *)rootViewController;
+  [integrationTestPlugin setupChannels:flutterViewController.engine.binaryMessenger];
+  while (!integrationTestPlugin.testResults) {
+    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.f, NO);
+  }
+  NSDictionary<NSString *, NSString *> *testResults = integrationTestPlugin.testResults;
+  NSMutableArray<NSString *> *passedTests = [NSMutableArray array];
+  NSMutableArray<NSString *> *failedTests = [NSMutableArray array];
+  NSLog(@"==================== Test Results =====================");
+  for (NSString *test in testResults.allKeys) {
+    NSString *result = testResults[test];
+    if ([result isEqualToString:@"success"]) {
+      NSLog(@"%@ passed.", test);
+      [passedTests addObject:test];
+    } else {
+      NSLog(@"%@ failed: %@", test, result);
+      [failedTests addObject:test];
+    }
+  }
+  NSLog(@"================== Test Results End ====================");
+  BOOL testPass = failedTests.count == 0;
+  if (!testPass && testResult) {
+    *testResult =
+        [NSString stringWithFormat:@"Detected failed integration test(s) %@ among %@",
+                                   failedTests.description, testResults.allKeys.description];
+  }
+  return testPass;
+}
+
+@end
diff --git a/packages/integration_test/ios/Classes/IntegrationTestPlugin.h b/packages/integration_test/ios/Classes/IntegrationTestPlugin.h
index 4836339..9684835 100644
--- a/packages/integration_test/ios/Classes/IntegrationTestPlugin.h
+++ b/packages/integration_test/ios/Classes/IntegrationTestPlugin.h
@@ -6,6 +6,13 @@
 
 NS_ASSUME_NONNULL_BEGIN
 
+@protocol FLTIntegrationTestScreenshotDelegate
+
+/** This will be called when a dart integration test triggers a window screenshot with  @c takeScreenshot. */
+- (void)didTakeScreenshot:(UIImage *)screenshot attachmentName:(nullable NSString *)name;
+
+@end
+
 /** A Flutter plugin that's responsible for communicating the test results back
  * to iOS XCTest. */
 @interface IntegrationTestPlugin : NSObject <FlutterPlugin>
@@ -16,11 +23,6 @@
  */
 @property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSString *> *testResults;
 
-/**
- * Mapping of screenshot images by suggested names, captured by the dart tests.
- */
-@property (copy, readonly) NSDictionary<NSString *, UIImage *> *capturedScreenshotsByName;
-
 /** Fetches the singleton instance of the plugin. */
 + (IntegrationTestPlugin *)instance;
 
@@ -28,6 +30,8 @@
 
 - (instancetype)init NS_UNAVAILABLE;
 
+@property(weak, nonatomic) id<FLTIntegrationTestScreenshotDelegate> screenshotDelegate;
+
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/packages/integration_test/ios/Classes/IntegrationTestPlugin.m b/packages/integration_test/ios/Classes/IntegrationTestPlugin.m
index a8a80b6..82d2635 100644
--- a/packages/integration_test/ios/Classes/IntegrationTestPlugin.m
+++ b/packages/integration_test/ios/Classes/IntegrationTestPlugin.m
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "IntegrationTestPlugin.h"
-
 @import UIKit;
 
+#import "IntegrationTestPlugin.h"
+
 static NSString *const kIntegrationTestPluginChannel = @"plugins.flutter.io/integration_test";
 static NSString *const kMethodTestFinished = @"allTestsFinished";
 static NSString *const kMethodScreenshot = @"captureScreenshot";
@@ -16,13 +16,10 @@
 
 @property(nonatomic, readwrite) NSDictionary<NSString *, NSString *> *testResults;
 
-- (instancetype)init NS_DESIGNATED_INITIALIZER;
-
 @end
 
 @implementation IntegrationTestPlugin {
   NSDictionary<NSString *, NSString *> *_testResults;
-  NSMutableDictionary<NSString *, UIImage *> *_capturedScreenshotsByName;
 }
 
 + (IntegrationTestPlugin *)instance {
@@ -35,13 +32,7 @@
 }
 
 - (instancetype)initForRegistration {
-  return [self init];
-}
-
-- (instancetype)init {
-  self = [super init];
-  _capturedScreenshotsByName = [NSMutableDictionary new];
-  return self;
+  return [super init];
 }
 
 + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
@@ -68,7 +59,7 @@
     // If running as a native Xcode test, attach to test.
     UIImage *screenshot = [self capturePngScreenshot];
     NSString *name = call.arguments[@"name"];
-    _capturedScreenshotsByName[name] = screenshot;
+    [self.screenshotDelegate didTakeScreenshot:screenshot attachmentName:name];
 
     // Also pass back along the channel for the driver to handle.
     NSData *pngData = UIImagePNGRepresentation(screenshot);
diff --git a/packages/integration_test/ios/integration_test.podspec b/packages/integration_test/ios/integration_test.podspec
index 5255073..fb24cb0 100644
--- a/packages/integration_test/ios/integration_test.podspec
+++ b/packages/integration_test/ios/integration_test.podspec
@@ -19,14 +19,7 @@
   s.public_header_files = 'Classes/**/*.h'
   s.dependency 'Flutter'
   s.ios.framework  = 'UIKit'
-  # Weakly link for parts of API that need to be run in XCTest targets.
-  s.ios.weak_framework = 'XCTest'
 
   s.platform = :ios, '8.0'
-  s.pod_target_xcconfig = {
-    'DEFINES_MODULE' => 'YES',
-    'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
-    # Find XCTest framework.
-    'FRAMEWORK_SEARCH_PATHS' => '$(PLATFORM_DIR)/Developer/Library/Frameworks',
-  }
+  s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
 end