{"id":20844611,"url":"https://github.com/keepsafe/tracer-ios","last_synced_at":"2025-05-09T01:50:09.610Z","repository":{"id":54812339,"uuid":"131178624","full_name":"KeepSafe/Tracer-iOS","owner":"KeepSafe","description":"Use traces to validate UX flows, analytics, or event buses","archived":false,"fork":false,"pushed_at":"2021-02-08T14:20:45.000Z","size":256,"stargazers_count":8,"open_issues_count":0,"forks_count":4,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-05-09T01:50:00.787Z","etag":null,"topics":["ios","keepsafe","macos","osx","swift","trace","tracer"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KeepSafe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-26T15:51:29.000Z","updated_at":"2022-05-21T05:15:48.000Z","dependencies_parsed_at":"2022-08-14T03:40:11.948Z","dependency_job_id":null,"html_url":"https://github.com/KeepSafe/Tracer-iOS","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KeepSafe%2FTracer-iOS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KeepSafe%2FTracer-iOS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KeepSafe%2FTracer-iOS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KeepSafe%2FTracer-iOS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KeepSafe","download_url":"https://codeload.github.com/KeepSafe/Tracer-iOS/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253176437,"owners_count":21866142,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ios","keepsafe","macos","osx","swift","trace","tracer"],"created_at":"2024-11-18T02:10:13.205Z","updated_at":"2025-05-09T01:50:09.573Z","avatar_url":"https://github.com/KeepSafe.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tracer\n\n[![CircleCI](https://circleci.com/gh/KeepSafe/Tracer-iOS/tree/master.svg?style=svg\u0026circle-token=a7ce3aaa52c666dceb1ab120472e37cc2065836b)](https://circleci.com/gh/KeepSafe/Tracer-iOS/tree/master)\n[![Apache 2.0 licensed](https://img.shields.io/badge/license-Apache2-blue.svg)](https://github.com/KeepSafe/Tracer-iOS/blob/master/LICENSE)\n[![CocoaPods](https://img.shields.io/cocoapods/v/Tracer.svg?maxAge=10800)]()\n[![Swift 4+](https://img.shields.io/badge/language-Swift-blue.svg)](https://swift.org)\n[![iOS](https://img.shields.io/badge/OS-iOS-orange.svg)](https://developer.apple.com/ios/)\n\n## What it does\n\nTracer acts as an in-memory log which can run time-scoped traces to validate UX flows, analytic events, or any other kind of serial event bus.\n\nFor example, if you're expecting `EventOne`, `EventTwo` and `EventThree` to be fired during a sign-up flow, Tracer can help your development or QA team validate those analytics don't break at some point. \n\nAnything that can be represented as `Equatable` can be logged and validated during a trace. Common examples of this would be strings, arrays or dictionaries (especially with [conditional conformance in Swift 4.1](https://swift.org/blog/conditional-conformance)) but you can also pass in your own custom structs or classes as long as they are `Equatable`.\n\nYou can either run the traces manually, using the built-in UI that floats over top of your app, or have important traces run automatically during unit or UI tests.\n\n### Example UI Usage\n\nSee [example gifs](#examples) below or see the [Examples folder](https://github.com/KeepSafe/Tracer-iOS/tree/master/Examples) for demonstrations of using it. \n\n### API Usage\n\nOr jump into the [API section](#api) below if you're ready to implement.\n\n## Installation\n\nQuickly install using [CocoaPods](https://cocoapods.org): \n\n```ruby\npod 'Tracer/UI'\n\n# or optionally don't include the UI component\npod 'Tracer/Core'\n```\n\nOr [Carthage](https://github.com/Carthage/Carthage):\n\n```\ngithub \"KeepSafe/Tracer-iOS\"\n```\n\nOr [manually install it](#manual-installation)\n\n## Examples\n\n### Running a trace\n\nAfter you tell the trace tool about your traces, it will list them all and enable you or your QA team to start one manually.\n\nHere's a simple example of a trace where order matters:\n\n![passinginorder](https://user-images.githubusercontent.com/30269720/40362283-169dca86-5d9a-11e8-8995-7bf89d714cf4.gif)\n\nAnd here's the same trace failing when an event is fired out of order (in this case, it fails all events before it that haven't already been matched since they would also be considered out-of-order).\n\n![failingoutoforder](https://user-images.githubusercontent.com/30269720/40362347-411b9496-5d9a-11e8-9e95-a1ff75341fcc.gif)\n\n### Move it around\n\nYou can expand or collapse the UI at any time, or even move it around the screen or drag the top of it to resize how much screen real estate it covers when expanded.\n\n![moveitaround](https://user-images.githubusercontent.com/30269720/40361667-1bcb4bac-5d98-11e8-817d-f4d480d155b4.gif)\n\n\n### Logger\n\nLogging uses the `debugDescription` of each item to display it on screen, so be sure your more complex data structures implement this for how you'd like to show it.\n\n![tracetool](https://user-images.githubusercontent.com/30269720/40361723-500a802c-5d98-11e8-80f0-ca5c409d3b05.png)\n\n### Traces\n\n[Traces](https://github.com/KeepSafe/Tracer-iOS/blob/master/Source/Traces/Trace.swift) have multiple options like `enforceOrder`, `allowDuplicates` or `assertOnFailure` that you can configure for specific scenarios. You can even perform programmatic setup steps (like resetting your app to a certain configuration) before each trace is started or list optional setup steps for someone to do so manually.\n\n#### Example 1: Order matters, duplicates don't\n\n![configone](https://user-images.githubusercontent.com/30269720/40362198-d44b552c-5d99-11e8-9eca-8ac659cfb3d3.png)\n\n(passing/failing this trace was shown in the first example section above)\n\n#### Example 2: No duplicates, order doesn't matter\n\n![configtwo](https://user-images.githubusercontent.com/30269720/40362231-e8814a56-5d99-11e8-974c-82f2d032907e.png)\n\nOrder doesn't matter for this trace, so passing it would look like:\n\n![passingoutoforder](https://user-images.githubusercontent.com/30269720/40362419-7f3da3cc-5d9a-11e8-9545-f40b4b664eb3.gif)\n\n**But**, duplicates do matter so if we fired any during the trace it would fail:\n\n![failingduplicate](https://user-images.githubusercontent.com/30269720/40362470-a16644a4-5d9a-11e8-8fe4-57e8b2c51469.gif)\n\n### Custom Settings\n\nOptionally, you can also add your own custom settings to the trace tool (or even show custom emojis for a logged item):\n\n![customsettings](https://user-images.githubusercontent.com/30269720/40362532-c42227a6-5d9a-11e8-8bed-a7358bbef5ea.gif)\n\n## API\n\n### UI\n\nIf you're using the UI frontend, you'll be interacting directly through [TraceUI.swift](https://github.com/KeepSafe/Tracer-iOS/blob/master/UI/TraceUI.swift). See the [TraceUIExample](https://github.com/KeepSafe/Tracer-iOS/tree/master/Examples/TraceUIExample).\n\n```swift\nlet traceUI = TraceUI()\ntraceUI.show()\n```\n\nYou can also toggle this to `.hide()` via a debug setting if you'd like.\n\n\u003e **Note**: If you're starting this tool in your `AppDelegate`, you should lazily load it after your app window loads to ensure the tool starts properly. The best thing to do is listen for `.UIWindowDidBecomeKey` and only show it after that happens.\n\n#### Configuration\n\nLoad traces into the UI using:\n\n```swift\ntraceUI.add(traces: [MyTraces.traceOne, MyTraces.traceTwo])\n```\n\n#### Logging \u0026 Validation\n\nIf you're logging an item specifically to be validated against for a trace, use:\n\n```swift\ntraceUI.log(traceItem: Event.three.toTraceItem)\n```\n\nHere we're just creating an `Event` enum with the events we're firing within the app and then using the `.toTraceItem` property to transform it:\n\n```swift\nenum Event: String {\n    case one\n    case two\n    case three\n    \n    var uxFlowHint: String {\n        switch self {\n        case .one: return \"Press the 'Fire event 1' button\"\n        case .two: return \"Press the 'Fire event 2' button\"\n        case .three: return \"Press the 'Fire event 3' button\"\n        }\n    }\n    \n    var toTraceItem: TraceItem {\n        return TraceItem(type: \"event\", itemToMatch: AnyTraceEquatable(self), uxFlowHint: uxFlowHint)\n    }\n}\n````\n\nOtherwise, you can also generically log (without it validating against an ongoing trace) by using:\n\n```swift\ntraceUI.log(genericItem: AnyTraceEquatable(\"Moooooooooo\"), emojiToPrepend: \"🐄\")\n```\n\n#### All Logging\n\nYou can see all logging functions and their documentation in [TraceUI.swift](https://github.com/KeepSafe/Tracer-iOS/blob/master/UI/TraceUI.swift):\n\n```swift\npublic func log(traceItem: TraceItem, verboseLog: AnyTraceEquatable? = nil, emojiToPrepend: String? = \"⚡️\") {}\n\npublic func logVerbose(traceItem: TraceItem, emojiToPrepend: String? = \"⚡️\") { }\n\npublic func log(genericItem: AnyTraceEquatable, properties: LoggedItemProperties? = nil, emojiToPrepend: String? = \"⚡️\") { }\n```\n\n### Programmatic API\n\nIf you'd rather not use the built-in UI frontend, you can set up your traces to run manually, such as during unit or UI tests. See the [AnalyticsTraceExample](https://github.com/KeepSafe/Tracer-iOS/tree/master/Examples/AnalyticsTraceExample).\n\nFeel free to have a look through the [unit tests](https://github.com/KeepSafe/Tracer-iOS/tree/master/TracerTests) for examples.\n\n#### Creating and starting a trace\n\n```swift\n// This is an individual item to match and could be in multiple traces\nlet answerTraceItem = TraceItem(type: \"The answer to the universe\", itemToMatch: AnyTraceEquatable(42))\n// This is a trace with an array of items it needs to match in order to pass\nlet trace = Trace(name: \"Find the answer\", itemsToMatch: [answerTraceItem])\n// And this is the time-scoped tracer that handles logging and creating pass/fail results\nlet tracer = Tracer(trace: trace)\n\n// Starting a trace returns a tuple with the current state and two signals to listen to\nlet (currentState, stateChangedSignal, itemLoggedSignal) = tracer.start()\n\nprint(\"\\n\\n---\u003e TRACE STARTED: \\(analyticsTrace.name)\")\nprint(\"---\u003e Current trace state: \\(currentState)\")\n\n// Optionally, listen to changes in this trace (and you can remove the listener at any point)\nitemLoggedListener = itemLoggedSignal.listen { traceItem in\n    print(\"---\u003e Trace item logged: \\(traceItem)\")\n}\nstateChangedListener = stateChangedSignal.listen { traceState in\n    print(\"---\u003e Trace state updated to: \\(traceState.rawValue)\")\n    print(\"---\u003e Trace state description: \\(traceState)\")\n}\n```\n\n#### Logging\n\nLogging during a trace is simple:\n\n```swift\ntracer.log(item: answerTraceItem)\n\n// After an item is logged, your trace will immediately either be passing or failing.\n// Optionally, you can set `assertOnFailure` to `true` on your `Trace` instance to stop \n// app execution as soon as a trace fails so you can debug.\n```\n\nOr you can even hook it into your `Analytics` struct:\n\n```swift\nAnalytics.log(event: .thirdViewSeen)\n\n/// See example app for this setup\nstruct Analytics {\n    \n    static func log(event: AnalyticsEvent) {\n        print(\"\\n\\nANALYTICS: \\(event.rawValue) logged\")\n        \n        Tracers.analytics.activeTracer?.log(item: event.toTraceItem)\n    }\n    \n}\n```\n\n#### Stopping \u0026 Reporting\n\nStopping a trace returns a `TraceReport` with raw or summary versions of the results\n\n```swift\n// FYI: signal listeners are automatically removed when stopped\nlet report = tracer.stop()\nprint(report.summary)\nprint(report.rawLog)\n\n// Or manually parse the results yourself\nlet results = report.result\n```\n\n## Manual Installation\n\n1. Clone this repository and drag the `Tracer.xcodeproj` into the Project Navigator of your application's Xcode project.\n  - It should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter.\n2. Select the `Tracer.xcodeproj` in the Project Navigator and verify the deployment target matches that of your application target.\n3. Select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the `Targets` heading in the sidebar.\n4. In the tab bar at the top of that window, open the `General` panel.\n5. Click on the `+` button under the `Embedded Binaries` section.\n6. Search for and select the top `Tracer.framework`.\n\nAnd that's it!\n\nThe `Tracer.framework` is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device.\n\n## Issues \u0026 Bugs\n\nPlease use the [Github issue tracker](https://github.com/KeepSafe/Tracer-iOS/issues) to let us know about any issues you may be experiencing.\n\n## License\n\nTracer for iOS is licensed under the [Apache Software License, 2.0 (\"Apache 2.0\")](https://github.com/KeepSafe/Tracer-iOS/blob/master/LICENSE)\n\n## Authors\n\nTracer for iOS is brought to you by [Rob Phillips](https://github.com/iwasrobbed) and the rest of the [Keepsafe team](https://www.getkeepsafe.com/about.html). We'd love to have you contribute or [join us](https://www.getkeepsafe.com/careers.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeepsafe%2Ftracer-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkeepsafe%2Ftracer-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeepsafe%2Ftracer-ios/lists"}