{"id":"69cd4f77-b07a-4bee-9a5c-3fc17b27ddca","shortId":"Yx6hJ2","kind":"skill","title":"cometchat-ios-testing","tagline":"Testing patterns for CometChat iOS UI Kit v5 in Swift / SwiftUI / UIKit projects. Covers XCTest setup, mocking CometChatSDK + CometChatUIKitSwift via protocols, snapshot testing with iOSSnapshotTestCase, async/await test helpers, UI testing for full chat flows on simulators, and ","description":"## Purpose\n\nTesting patterns for iOS CometChat integrations. XCTest (built-in) for unit + integration; SnapshotTesting (Pointfree) for visual regression; XCUITest for full-flow UI tests on the simulator.\n\n**Read these other skills first:**\n- `cometchat-ios-core` — init, login, Secrets pattern\n- `cometchat-ios-components` — what each VC does (tests assert against this)\n- `cometchat-ios-calls/references/callkit-and-pushkit.md` — CallKit testing strategy (different from chat)\n\n**Ground truth:**\n- XCTest — https://developer.apple.com/documentation/xctest\n- SnapshotTesting — https://github.com/pointfreeco/swift-snapshot-testing\n\n---\n\n## 1. Test target structure\n\nA typical iOS project has three targets:\n\n| Target | When |\n|---|---|\n| `YourApp` | Production code |\n| `YourAppTests` | Unit + integration tests (XCTest) |\n| `YourAppUITests` | UI tests (XCUITest, runs on simulator) |\n\nThe skill writes to `YourAppTests` for most assertions; `YourAppUITests` for full flows.\n\n---\n\n## 2. Mocking the SDK via protocols\n\nThe CometChat SDKs are class-based with static methods (`CometChat.init`, `CometChat.login`). Hard to mock directly. **Wrap them in protocols you own:**\n\n```swift\n// Protocols/CometChatService.swift\nimport CometChatSDK\n\nprotocol CometChatServiceProtocol {\n  func initialize(appId: String, region: String) async throws\n  func login(uid: String, authKey: String) async throws -> User\n  func logout() async throws\n  func getLoggedInUser() -> User?\n  func sendMessage(_ message: TextMessage) async throws -> TextMessage\n}\n\n// Production implementation\nfinal class CometChatService: CometChatServiceProtocol {\n  func initialize(appId: String, region: String) async throws {\n    try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in\n      let settings = AppSettings.AppSettingsBuilder()\n        .subscribePresenceForAllUsers()\n        .setRegion(region: region)\n        .build()\n      CometChat.init(appId: appId, appSettings: settings) { isInitialized, error in\n        if let error = error { cont.resume(throwing: error) } else { cont.resume() }\n      }\n    }\n  }\n\n  func login(uid: String, authKey: String) async throws -> User {\n    try await withCheckedThrowingContinuation { (cont: CheckedContinuation<User, Error>) in\n      CometChat.login(UID: uid, apiKey: authKey, onSuccess: { cont.resume(returning: $0) },\n                      onError: { cont.resume(throwing: $0) })\n    }\n  }\n\n  // ... etc\n}\n```\n\nInject the protocol via DI:\n\n```swift\nfinal class ChatViewController: UIViewController {\n  private let service: CometChatServiceProtocol\n\n  init(service: CometChatServiceProtocol = CometChatService()) {\n    self.service = service\n    super.init(nibName: nil, bundle: nil)\n  }\n  required init?(coder: NSCoder) { fatalError() }\n}\n```\n\nTest with a mock:\n\n```swift\n// Tests/CometChatServiceMock.swift\nfinal class CometChatServiceMock: CometChatServiceProtocol {\n  var initializeCallCount = 0\n  var initializeError: Error?\n  var loginUser: User?\n  var loginError: Error?\n\n  func initialize(appId: String, region: String) async throws {\n    initializeCallCount += 1\n    if let error = initializeError { throw error }\n  }\n\n  func login(uid: String, authKey: String) async throws -> User {\n    if let error = loginError { throw error }\n    return loginUser ?? User(uid: uid, name: \"Test User\")\n  }\n\n  func logout() async throws {}\n  func getLoggedInUser() -> User? { loginUser }\n  func sendMessage(_ message: TextMessage) async throws -> TextMessage { message }\n}\n```\n\n---\n\n## 3. XCTest patterns\n\n### Async/await tests\n\n```swift\nimport XCTest\n@testable import YourApp\n\nfinal class ChatViewControllerTests: XCTestCase {\n  func testInitializeRunsBeforeLogin() async throws {\n    let mock = CometChatServiceMock()\n    let vc = ChatViewController(service: mock)\n\n    await vc.bootstrapCometChat()\n\n    XCTAssertEqual(mock.initializeCallCount, 1)\n    XCTAssertNotNil(vc.currentUser)\n  }\n\n  func testLoginFailureSurfacesError() async throws {\n    let mock = CometChatServiceMock()\n    mock.loginError = NSError(domain: \"Test\", code: 401, userInfo: [NSLocalizedDescriptionKey: \"Unauthorized\"])\n\n    let vc = ChatViewController(service: mock)\n    await vc.bootstrapCometChat()\n\n    XCTAssertEqual(vc.errorMessage, \"Unauthorized\")\n    XCTAssertEqual(vc.errorLabel?.textColor, .red)            // skill rule: error UI must be visible\n  }\n}\n```\n\n### Expectation-based tests (legacy / non-async code)\n\n```swift\nfunc testMessageSendFiresCallback() {\n  let expectation = expectation(description: \"message sent\")\n\n  let mock = CometChatServiceMock()\n  let vc = ChatViewController(service: mock)\n  vc.send(message: \"Hello\") { _ in\n    expectation.fulfill()\n  }\n\n  wait(for: [expectation], timeout: 1.0)\n}\n```\n\nFor SDK callback-based code, prefer wrapping in protocol + async/await per Section 2; this pattern is for legacy code only.\n\n---\n\n## 4. SwiftUI view tests\n\n```swift\nimport SwiftUI\nimport XCTest\n@testable import YourApp\n\n@MainActor\nfinal class ProfileViewTests: XCTestCase {\n  func testCallButtonInvokesService() throws {\n    let mock = CallServiceMock()\n    let view = ProfileView(user: testUser(), callService: mock)\n\n    let host = UIHostingController(rootView: view)\n    host.loadViewIfNeeded()\n\n    // Hard to interact with SwiftUI views from XCTest directly — use ViewInspector or UI tests\n    // For most behavioral tests, prefer XCUITest (Section 6)\n  }\n}\n```\n\nPure SwiftUI testing is hard; pragmatic choice is to keep most tests at the service / view-model layer and put view assertions in UI tests.\n\n### ViewInspector (third-party)\n\nFor SwiftUI views you do want to inspect from XCTest, install `ViewInspector`:\n\n```swift\nimport ViewInspector\n\nfunc testProfileShowsUserName() throws {\n  let user = testUser()\n  let view = ProfileView(user: user)\n  let text = try view.inspect().find(text: user.name)\n  XCTAssertNotNil(text)\n}\n```\n\nAdds a runtime dep. Not strictly necessary if you have UI tests.\n\n---\n\n## 5. Snapshot testing\n\n```bash\n# Package.swift dependency\n.package(url: \"https://github.com/pointfreeco/swift-snapshot-testing.git\", from: \"1.15.0\")\n```\n\n```swift\nimport SnapshotTesting\nimport XCTest\n@testable import YourApp\n\nfinal class ChatViewSnapshotTests: XCTestCase {\n  func testEmptyChatView() {\n    let view = ChatView(messages: [])\n    let host = UIHostingController(rootView: view)\n    assertSnapshot(matching: host, as: .image(on: .iPhone13))\n  }\n\n  func testChatViewWithMessages() {\n    let messages = [\n      TextMessage(text: \"Hi\", senderUid: \"alice\"),\n      TextMessage(text: \"Hey\", senderUid: \"bob\"),\n    ]\n    let view = ChatView(messages: messages)\n    let host = UIHostingController(rootView: view)\n    assertSnapshot(matching: host, as: .image(on: .iPhone13))\n  }\n}\n```\n\nFirst run creates the reference image; subsequent runs diff against it. Commit the `__Snapshots__` directory.\n\n**Gotcha:** snapshot tests are sensitive to font rendering — if CI uses a different macOS version than dev, snapshots won't match. Pin runners to a known version.\n\n---\n\n## 6. UI tests (XCUITest)\n\n```swift\nimport XCTest\n\nfinal class ChatUITests: XCTestCase {\n  override func setUp() {\n    continueAfterFailure = false\n    let app = XCUIApplication()\n    app.launchEnvironment[\"UI_TEST_MODE\"] = \"1\"\n    app.launchEnvironment[\"TEST_UID\"] = \"cometchat-uid-1\"\n    app.launch()\n  }\n\n  func testLoginAndSeeConversations() {\n    let app = XCUIApplication()\n    XCTAssertTrue(app.staticTexts[\"Welcome\"].waitForExistence(timeout: 10))\n    app.buttons[\"Messages\"].tap()\n    XCTAssertTrue(app.staticTexts[\"Conversations\"].waitForExistence(timeout: 10))\n  }\n\n  func testSendMessage() {\n    let app = XCUIApplication()\n    app.buttons[\"Messages\"].tap()\n    app.buttons[\"cometchat-uid-2\"].tap()\n\n    let composer = app.textFields[\"Type a message\"]\n    composer.tap()\n    composer.typeText(\"Hello from UI test\")\n    app.buttons[\"Send\"].tap()\n\n    XCTAssertTrue(app.staticTexts[\"Hello from UI test\"].waitForExistence(timeout: 5))\n  }\n}\n```\n\n**`UI_TEST_MODE` flag:** in production code, check this env var and skip animations / use shorter timeouts:\n\n```swift\nif ProcessInfo.processInfo.environment[\"UI_TEST_MODE\"] == \"1\" {\n  UIView.setAnimationsEnabled(false)\n}\n```\n\n**Test users:** UI tests need real login. Use the dev `cometchat-uid-1` through `cometchat-uid-5`; never use production credentials.\n\n---\n\n## 7. CI configuration\n\n### GitHub Actions\n\n```yaml\nname: tests\non: [push, pull_request]\n\njobs:\n  unit:\n    runs-on: macos-13                              # pin macOS version for snapshot stability\n    steps:\n      - uses: actions/checkout@v4\n      - run: |\n          xcodebuild test \\\n            -scheme YourApp \\\n            -destination \"platform=iOS Simulator,name=iPhone 15,OS=17.0\" \\\n            -only-testing:YourAppTests \\\n            COMETCHAT_TEST_APP_ID=\"${{ secrets.TEST_COMETCHAT_APP_ID }}\" \\\n            COMETCHAT_TEST_REGION=\"${{ secrets.TEST_COMETCHAT_REGION }}\" \\\n            COMETCHAT_TEST_AUTH_KEY=\"${{ secrets.TEST_COMETCHAT_AUTH_KEY }}\"\n\n  ui:\n    runs-on: macos-13\n    needs: unit\n    steps:\n      - uses: actions/checkout@v4\n      - run: |\n          xcodebuild test \\\n            -scheme YourApp \\\n            -destination \"platform=iOS Simulator,name=iPhone 15,OS=17.0\" \\\n            -only-testing:YourAppUITests \\\n            COMETCHAT_TEST_APP_ID=\"${{ secrets.TEST_COMETCHAT_APP_ID }}\"\n```\n\n### Xcode Cloud\n\nConfigure in App Store Connect → Xcode Cloud. Use the same env var convention; Xcode Cloud injects them into `xcodebuild` automatically.\n\n---\n\n## 8. Anti-patterns\n\n1. **Mocking CometChat directly** (without the protocol wrapper). The static methods aren't swappable; you'd need OCMock and method swizzling. The protocol wrapper is cleaner.\n2. **Using real WebSocket connections** in unit tests. They're flaky on CI. Mock the service layer.\n3. **Snapshot tests on a Mac with system font preference different from CI.** Pin macOS + Xcode versions.\n4. **Hardcoding `cometchat-uid-1` strings** across many tests. Centralize via a `TestUsers` enum so renames are one-edit.\n5. **Skipping `continueAfterFailure = false`** in UI tests. Default UI tests continue running after a failure, hiding root causes in cascade failures.\n6. **Storing test credentials in `Secrets.swift`.** Use env vars passed via `xcodebuild` and read via `ProcessInfo.processInfo.environment[\"...\"]` in test bundles only.\n\n## 9. Verification checklist\n\n- [ ] `CometChatServiceProtocol` (or similar) wrapping the SDK; production VCs use it via DI\n- [ ] Mock implementation in `Tests/Mocks/`\n- [ ] At least one test for \"init resolves before VC is functional\"\n- [ ] At least one test for \"error UI shows on init/login failure\"\n- [ ] Snapshot tests for at least the empty + populated states of chat surfaces\n- [ ] At least one UI test for login + see conversations\n- [ ] Test target uses dedicated CometChat test App ID via env vars (not Secrets.swift)\n- [ ] CI pinned to macOS + Xcode versions\n- [ ] No `continueAfterFailure = true` in UI tests\n\n## 10. Pointers\n\n- `cometchat-ios-calls/references/callkit-and-pushkit.md` — calls + CallKit testing\n- `cometchat-ios-core` — init, login, Secrets patterns the tests assert against\n- `cometchat-ios-troubleshooting` — when tests pass but production breaks","tags":["cometchat","ios","testing","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react"],"capabilities":["skill","source-cometchat","skill-cometchat-ios-testing","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-ios-testing","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (11,244 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:53.548Z","embedding":null,"createdAt":"2026-05-18T07:04:26.788Z","updatedAt":"2026-05-18T19:04:53.548Z","lastSeenAt":"2026-05-18T19:04:53.548Z","tsv":"'-13':943,999 '/documentation/xctest':112 '/pointfreeco/swift-snapshot-testing':116 '/pointfreeco/swift-snapshot-testing.git':680 '/references/callkit-and-pushkit.md':100,1273 '0':294,298,342 '1':117,361,438,809,816,899,915,1058,1123 '1.0':513 '1.15.0':682 '10':828,837,1267 '15':965,1017 '17.0':967,1019 '2':157,527,850,1084 '3':407,1101 '4':535,1118 '401':453 '5':670,875,920,1139 '6':592,786,1160 '7':925 '8':1054 '9':1180 'across':1125 'action':929 'actions/checkout':952,1004 'add':658 'alic':721 'anim':889 'anti':1056 'anti-pattern':1055 'apikey':289 'app':803,821,841,974,978,1026,1030,1036,1248 'app.buttons':829,843,846,864 'app.launch':817 'app.launchenvironment':805,810 'app.statictexts':824,833,868 'app.textfields':854 'appid':193,230,253,254,354 'appset':255 'appsettings.appsettingsbuilder':246 'aren':1069 'assert':93,152,615,1287 'assertsnapshot':706,737 'async':197,205,210,219,234,275,358,374,393,403,424,443,485 'async/await':30,410,524 'auth':988,992 'authkey':203,273,290,372 'automat':1053 'await':237,279,434,462 'base':169,480,518 'bash':673 'behavior':587 'bob':726 'break':1298 'build':251 'built':51 'built-in':50 'bundl':323,1178 'call':99,1272,1274 'callback':517 'callback-bas':516 'callkit':101,1275 'callservic':563 'callservicemock':557 'cascad':1158 'caus':1156 'central':1128 'chat':37,106,1231 'chatuitest':795 'chatview':699,729 'chatviewcontrol':308,431,459,501 'chatviewcontrollertest':420 'chatviewsnapshottest':693 'check':883 'checkedcontinu':240,282 'checklist':1182 'choic':599 'ci':768,926,1096,1113,1255 'class':168,225,307,337,419,549,692,794 'class-bas':167 'cleaner':1083 'cloud':1033,1040,1048 'code':132,452,486,519,533,882 'coder':327 'cometchat':2,8,47,77,85,97,164,814,848,913,918,972,977,980,984,986,991,1024,1029,1060,1121,1246,1270,1278,1290 'cometchat-ios-cal':96,1269 'cometchat-ios-compon':84 'cometchat-ios-cor':76,1277 'cometchat-ios-test':1 'cometchat-ios-troubleshoot':1289 'cometchat-uid':813,847,912,917,1120 'cometchat.init':173,252 'cometchat.login':174,286 'cometchatsdk':22,188 'cometchatservic':226,317 'cometchatservicemock':338,428,447,498 'cometchatserviceprotocol':190,227,313,316,339,1183 'cometchatuikitswift':23 'commit':755 'compon':87 'compos':853 'composer.tap':858 'composer.typetext':859 'configur':927,1034 'connect':1038,1088 'cont':239,281 'cont.resume':264,268,292,296 'continu':1149 'continueafterfailur':800,1141,1262 'convent':1046 'convers':834,1241 'core':79,1280 'cover':18 'creat':746 'credenti':924,1163 'd':1073 'dedic':1245 'default':1146 'dep':661 'depend':675 'descript':493 'destin':959,1011 'dev':775,911 'developer.apple.com':111 'developer.apple.com/documentation/xctest':110 'di':304,1194 'diff':752 'differ':104,771,1111 'direct':178,579,1061 'directori':758 'domain':450 'edit':1138 'els':267 'empti':1227 'enum':1132 'env':885,1044,1167,1251 'error':242,258,262,263,266,284,345,351,364,367,379,382,473,1215 'etc':299 'expect':479,491,492,511 'expectation-bas':478 'expectation.fulfill':508 'failur':1153,1159,1220 'fals':801,901,1142 'fatalerror':329 'final':224,306,336,418,548,691,793 'find':653 'first':75,744 'flag':879 'flaki':1094 'flow':38,65,156 'font':765,1109 'full':36,64,155 'full-flow':63 'func':191,199,208,212,215,228,269,352,368,391,395,399,422,441,488,552,638,695,713,798,818,838 'function':1209 'getloggedinus':213,396 'github':928 'github.com':115,679 'github.com/pointfreeco/swift-snapshot-testing':114 'github.com/pointfreeco/swift-snapshot-testing.git':678 'gotcha':759 'ground':107 'hard':175,571,597 'hardcod':1119 'hello':506,860,869 'helper':32 'hey':724 'hi':719 'hide':1154 'host':566,702,708,733,739 'host.loadviewifneeded':570 'id':975,979,1027,1031,1249 'imag':710,741,749 'implement':223,1196 'import':187,413,416,540,542,545,636,684,686,689,791 'init':80,314,326,1204,1281 'init/login':1219 'initi':192,229,353 'initializecallcount':341,360 'initializeerror':344,365 'inject':300,1049 'inspect':630 'instal':633 'integr':48,55,135 'interact':573 'io':3,9,46,78,86,98,123,961,1013,1271,1279,1291 'iossnapshottestcas':29 'iphon':964,1016 'iphone13':712,743 'isiniti':257 'job':937 'keep':602 'key':989,993 'kit':11 'known':784 'layer':611,1100 'least':1200,1211,1225,1234 'legaci':482,532 'let':244,261,311,363,378,426,429,445,457,490,496,499,555,558,565,641,644,649,697,701,715,727,732,802,820,840,852 'login':81,200,270,369,908,1239,1282 'loginerror':350,380 'loginus':347,384,398 'logout':209,392 'mac':1106 'maco':772,942,945,998,1115,1258 'mainactor':547 'mani':1126 'match':707,738,779 'messag':217,401,406,494,505,700,716,730,731,830,844,857 'method':172,1068,1077 'mock':21,158,177,333,427,433,446,461,497,503,556,564,1059,1097,1195 'mock.initializecallcount':437 'mock.loginerror':448 'mode':808,878,898 'model':610 'must':475 'name':388,931,963,1015 'necessari':664 'need':906,1000,1074 'never':921 'nibnam':321 'nil':322,324 'non':484 'non-async':483 'nscoder':328 'nserror':449 'nslocalizeddescriptionkey':455 'ocmock':1075 'one':1137,1201,1212,1235 'one-edit':1136 'onerror':295 'only-test':968,1020 'onsuccess':291 'os':966,1018 'overrid':797 'packag':676 'package.swift':674 'parti':622 'pass':1169,1295 'pattern':6,44,83,409,529,1057,1284 'per':525 'pin':780,944,1114,1256 'platform':960,1012 'pointer':1268 'pointfre':57 'popul':1228 'pragmat':598 'prefer':520,589,1110 'privat':310 'processinfo.processinfo.environment':895,1175 'product':131,222,881,923,1189,1297 'profileview':560,646 'profileviewtest':550 'project':17,124 'protocol':25,162,182,189,302,523,1064,1080 'protocols/cometchatservice.swift':186 'pull':935 'pure':593 'purpos':42 'push':934 'put':613 're':1093 'read':71,1173 'real':907,1086 'red':470 'refer':748 'region':195,232,249,250,356,982,985 'regress':60 'renam':1134 'render':766 'request':936 'requir':325 'resolv':1205 'return':293,383 'root':1155 'rootview':568,704,735 'rule':472 'run':142,745,751,940,954,996,1006,1150 'runner':781 'runs-on':939,995 'runtim':660 'scheme':957,1009 'sdk':160,515,1188 'sdks':165 'secret':82,1283 'secrets.swift':1165,1254 'secrets.test':976,983,990,1028 'section':526,591 'see':1240 'self.service':318 'send':865 'senderuid':720,725 'sendmessag':216,400 'sensit':763 'sent':495 'servic':312,315,319,432,460,502,607,1099 'set':245,256 'setregion':248 'setup':20,799 'shorter':891 'show':1217 'similar':1185 'simul':40,70,144,962,1014 'skill':74,146,471 'skill-cometchat-ios-testing' 'skip':888,1140 'snapshot':26,671,757,760,776,948,1102,1221 'snapshottest':56,113,685 'source-cometchat' 'stabil':949 'state':1229 'static':171,1067 'step':950,1002 'store':1037,1161 'strategi':103 'strict':663 'string':194,196,202,204,231,233,272,274,355,357,371,373,1124 'structur':120 'subscribepresenceforallus':247 'subsequ':750 'super.init':320 'surfac':1232 'swappabl':1071 'swift':14,185,305,334,412,487,539,635,683,790,893 'swiftui':15,536,541,575,594,624 'swizzl':1078 'system':1108 'tap':831,845,851,866 'target':119,127,128,1243 'test':4,5,27,31,34,43,67,92,102,118,136,140,330,389,411,451,481,538,584,588,595,604,618,669,672,761,788,807,811,863,872,877,897,902,905,932,956,970,973,981,987,1008,1022,1025,1091,1103,1127,1145,1148,1162,1177,1202,1213,1222,1237,1242,1247,1266,1276,1286,1294 'testabl':415,544,688 'testcallbuttoninvokesservic':553 'testchatviewwithmessag':714 'testemptychatview':696 'testinitializerunsbeforelogin':423 'testloginandseeconvers':819 'testloginfailuresurfaceserror':442 'testmessagesendfirescallback':489 'testprofileshowsusernam':639 'tests/cometchatservicemock.swift':335 'tests/mocks':1198 'testsendmessag':839 'testus':562,643,1131 'text':650,654,657,718,723 'textcolor':469 'textmessag':218,221,402,405,717,722 'third':621 'third-parti':620 'three':126 'throw':198,206,211,220,235,265,276,297,359,366,375,381,394,404,425,444,554,640 'timeout':512,827,836,874,892 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'tri':236,278,651 'troubleshoot':1292 'true':1263 'truth':108 'type':855 'typic':122 'ui':10,33,66,139,474,583,617,668,787,806,862,871,876,896,904,994,1144,1147,1216,1236,1265 'uid':201,271,287,288,370,386,387,812,815,849,914,919,1122 'uihostingcontrol':567,703,734 'uikit':16 'uiview.setanimationsenabled':900 'uiviewcontrol':309 'unauthor':456,466 'unit':54,134,938,1001,1090 'url':677 'use':580,769,890,909,922,951,1003,1041,1085,1166,1191,1244 'user':207,214,277,283,348,376,385,390,397,561,642,647,648,903 'user.name':655 'userinfo':454 'v4':953,1005 'v5':12 'var':340,343,346,349,886,1045,1168,1252 'vc':90,430,458,500,1207 'vc.bootstrapcometchat':435,463 'vc.currentuser':440 'vc.errorlabel':468 'vc.errormessage':465 'vc.send':504 'vcs':1190 'verif':1181 'version':773,785,946,1117,1260 'via':24,161,303,1129,1170,1174,1193,1250 'view':537,559,569,576,609,614,625,645,698,705,728,736 'view-model':608 'view.inspect':652 'viewinspector':581,619,634,637 'visibl':477 'visual':59 'void':241 'wait':509 'waitforexist':826,835,873 'want':628 'websocket':1087 'welcom':825 'withcheckedthrowingcontinu':238,280 'without':1062 'won':777 'wrap':179,521,1186 'wrapper':1065,1081 'write':147 'xcode':1032,1039,1047,1116,1259 'xcodebuild':955,1007,1052,1171 'xctassertequ':436,464,467 'xctassertnotnil':439,656 'xctasserttru':823,832,867 'xctest':19,49,109,137,408,414,543,578,632,687,792 'xctestcas':421,551,694,796 'xcuiapplic':804,822,842 'xcuitest':61,141,590,789 'yaml':930 'yourapp':130,417,546,690,958,1010 'yourapptest':133,149,971 'yourappuitest':138,153,1023","prices":[{"id":"c935068f-2909-4ecb-a977-e3efdb112112","listingId":"69cd4f77-b07a-4bee-9a5c-3fc17b27ddca","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T07:04:26.788Z"}],"sources":[{"listingId":"69cd4f77-b07a-4bee-9a5c-3fc17b27ddca","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-ios-testing","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-ios-testing","isPrimary":false,"firstSeenAt":"2026-05-18T07:04:26.788Z","lastSeenAt":"2026-05-18T19:04:53.548Z"}],"details":{"listingId":"69cd4f77-b07a-4bee-9a5c-3fc17b27ddca","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-ios-testing","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"87ee6d9a36e96b93090cdaf814778dd912537954","skill_md_path":"skills/cometchat-ios-testing/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-ios-testing"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-ios-testing","license":"MIT","description":"Testing patterns for CometChat iOS UI Kit v5 in Swift / SwiftUI / UIKit projects. Covers XCTest setup, mocking CometChatSDK + CometChatUIKitSwift via protocols, snapshot testing with iOSSnapshotTestCase, async/await test helpers, UI testing for full chat flows on simulators, and CI on Xcode Cloud / GitHub Actions.","compatibility":"Xcode 15+, Swift 5.9+, iOS 13+ deployment target; XCTest (built-in); SnapshotTesting >= 1.15 (Pointfree); CometChatUIKitSwift ~> 5.1"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-ios-testing"},"updatedAt":"2026-05-18T19:04:53.548Z"}}