{"id":32148877,"url":"https://github.com/k-kohey/parchment-swift","last_synced_at":"2026-02-19T07:01:50.723Z","repository":{"id":46151004,"uuid":"415758326","full_name":"k-kohey/Parchment-swift","owner":"k-kohey","description":"Logger implementation using Swift Concurrency","archived":false,"fork":false,"pushed_at":"2023-06-03T11:27:14.000Z","size":824,"stargazers_count":27,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-26T12:48:07.835Z","etag":null,"topics":["ios","macos","swift","swift-library"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/k-kohey.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":"2021-10-11T02:50:11.000Z","updated_at":"2025-02-06T07:45:01.000Z","dependencies_parsed_at":"2023-02-12T16:31:14.950Z","dependency_job_id":null,"html_url":"https://github.com/k-kohey/Parchment-swift","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/k-kohey/Parchment-swift","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-kohey%2FParchment-swift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-kohey%2FParchment-swift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-kohey%2FParchment-swift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-kohey%2FParchment-swift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/k-kohey","download_url":"https://codeload.github.com/k-kohey/Parchment-swift/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-kohey%2FParchment-swift/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29605799,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T06:47:36.664Z","status":"ssl_error","status_checked_at":"2026-02-19T06:45:47.551Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","macos","swift","swift-library"],"created_at":"2025-10-21T09:18:18.449Z","updated_at":"2026-02-19T07:01:50.718Z","avatar_url":"https://github.com/k-kohey.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Parchment-ios\n\nThis project provides an implementation of a logger that tracks user behavior and system behavior.\nUsing this implementation, many logging-related processes can be standardized and hidden.\n\nThis is especially useful in the following cases\n\n- There are multiple backends sending event logs, and the user wants to control which event logs are sent to which backend.\n- Buffering event logs in local storage to prevent missing event logs\n- To centrally manage parameters that are common to many event logs.\n\n## Installation\n\nIf you are using Xcode Project, you can add a dependency for this Package by specifying this repository from Xcode.\n\nIf you are using the Swift Package Project, you can add a dependency for this Package by adding the following description to Package.swift.\n\n```swift\ndependencies: [\n    .product(name: \"ParchmentCore\", package: \"Parchment\"),\n    // The following statements are optional\n    .product(name: \"Parchment\", package: \"Parchment\"),\n]\n```\n\n## Project Overview\n\n### ParchmentCore\n\nIt contains the main logic and definitions for logging processing and event logging definitions.\n\n### Parchment\n\nProvides a stander implementation compliant with the Protocol provided by ParchmentCore. If you implement your own buffer and scheduler, you do not need to add any dependencies.\n\nSee the [Customization](#customization) section for more details.\n\n### eventgen\n\nThis is an experimental API that generates Swift code from event log specifications written in natural language.\n\nSee the [document](EventGen/README.md) section for more details.\n\n## Usage\n\nThis section describes the basic usage of this project.\n\n### Definision logging event\n\n```swift\n\n// with struct\nstruct Event: Loggable {\n    public let eventName: String\n    public let parameters: [String : Any]\n}\n\n// with enum\nenum Event: Loggable {\n  case impletion(screen: String)\n\n  var eventName: String {\n    ...\n  }\n\n  var parameters: [String : Any] {\n    ...\n  }\n}\n\n```\n\nAlternatively, there are two ways to do this without definision logging event.\n\n- Use type `TrackingEvent`\n- Use Dictionary. Dictionary is conformed Loggable.\n\n### Wrap logging service\n\nWrap existing logger implemention such as such as FirebaseAnalytics and endpoints with LoggerComponent.\n\n```swift\n\nextension LoggerComponentID {\n    static let analytics = LoggerComponentID(\"Analytics\")\n}\n\nstruct Analytics: LoggerComponent {\n    static let id: LoggerComponentID = .analytics\n\n    func send(_ event: Loggable) async -\u003e Bool {\n        let url = URL(string: \"https://your-endpoint/...\")!\n        request.httpBody = convertBody(from: event)\n\n        return await withCheckedContinuation { continuation in\n            let task = URLSession.shared.dataTask(with: request) { data, response, error in\n\n                if let error = error {\n                    print(error)\n                    continuation.resume(returning: false)\n                    return\n                }\n\n                guard\n                    let response = response as? HTTPURLResponse,\n                    (200..\u003c300).contains(response.statusCode)\n                else {\n                    continuation.resume(returning: false)\n                    return\n                }\n\n                continuation.resume(returning: true)\n            }\n            task.resume()\n        }\n    }\n}\n\n```\n\n### Send event\n\nInitialize `LoggerBundler` and send log using it.\n\n```swift\n\nlet analytics = Analytics()\nlet logger = LoggerBundler(components: [analytics])\n\nawait logger.send(\n    TrackingEvent(eventName: \"hoge\", parameters: [:]),\n    with: .init(policy: .immediately)\n)\n\nawait logger.send(.impletion(screen: \"Home\"))\n\nawait logger.send([\\.eventName: \"tapButton\", \\.parameters: [\"ButtonID\": 1]])\n\n```\n\n### More Information\n\nPlease see the API documentation below（WIP）.\n\n- https://k-kohey.github.io/Parchment-swift/ParchmentCore/documentation/parchmentcore/\n- https://k-kohey.github.io/Parchment-swift/Parchment/documentation/Parchment/\n\n## Customization\n\nThis section describes how to customize the behavior of the logger.\n\n### Create a type that conforms to Mutation\n\nMutation converts one log into another.\n\nThis is useful if you have parameters that you want to add to all the logs.\n\nTo create the type and set it to logger, write as follows.\n\n```swift\n\n// An implementation similar to this can be found in Parchment\n\nstruct DeviceDataMutation: Mutation {\n    private let deviceParams = [\n        \"Model\": UIDevice.current.name,\n        \"OS\": UIDevice.current.systemName,\n        \"OS Version\": UIDevice.current.systemVersion\n    ]\n\n    public func transform(_ event: Loggable, id: LoggerComponentID) -\u003e Loggable {\n        let log: LoggableDictonary = [\n            \\.eventName: event.eventName,\n            \\.parameters: event.parameters.merging(deviceParams) { left, _ in left }\n        ]\n        return log\n    }\n}\n\nlogger.mutations.append(DeviceDataMutation())\n\n```\n\n### Extend LoggerComponentID\n\nLoggerComponentID is an ID that uniquely recognizes a logger.\n\nBy extending LoggerComponentID, the destination of the log can be controlled as shown below.\n\n```swift\n\nextension LoggerComponentID {\n    static let firebase: Self = .init(\"firebase\")\n    static let myBadkend: Self = .init(\"myBadkend\")\n}\n\nawait logger.send(.tap, with: .init(scope: .exclude([.firebase, .myBadkend])))\n\nawait logger.send(.tap, with: .init(scope: .only([.myBadkend])))\n\n```\n\n### Create a type that conforms to BufferedEventFlushScheduler\n\nBufferedEventFlushScheduler determines the timing of fetching the log data in the buffer.\nTo create the type and set it to logger, write as follows.\n\n```swift\n\n// An implementation similar to this can be found in Parchment\nfinal class RegularlyPollingScheduler: BufferedEventFlushScheduler {\n    public static let `default` = RegularlyPollingScheduler(timeInterval: 60)\n\n    let timeInterval: TimeInterval\n\n    var lastFlushedDate: Date = Date()\n\n    private weak var timer: Timer?\n\n    public init(\n        timeInterval: TimeInterval,\n    ) {\n        self.timeInterval = timeInterval\n    }\n\n    public func schedule(with buffer: TrackingEventBufferAdapter) async -\u003e AsyncThrowingStream\u003c[BufferRecord], Error\u003e {\n        return AsyncThrowingStream { continuation in\n            let timer = Timer(fire: .init(), interval: 1, repeats: true) { _ in\n                Task { [weak self] in\n                    await self?.tick(with: buffer) {\n                        continuation.yield($0)\n                    }\n                }\n            }\n            RunLoop.main.add(timer, forMode: .common)\n            self.timer = timer\n        }\n    }\n\n    public func cancel() {\n        timer?.invalidate()\n    }\n\n    private func tick(with buffer: TrackingEventBufferAdapter, didFlush: @escaping ([BufferRecord])-\u003e()) async {\n        guard await buffer.count() \u003e 0 else { return }\n\n        let flush = {\n            let records = await buffer.load()\n            didFlush(records)\n        }\n\n        let timeSinceLastFlush = abs(self.lastFlushedDate.timeIntervalSinceNow)\n        if self.timeInterval \u003c timeSinceLastFlush {\n            await flush()\n            self.lastFlushedDate = Date()\n            return\n        }\n    }\n}\n\nlet logger = LoggerBundler(\n    components: [...],\n    buffer: TrackingEventBuffer = ...,\n    loggingStrategy: BufferedEventFlushScheduler = RegularlyPollingScheduler.default\n)\n\n```\n\n### Create a type that conforms to TrackingEventBuffer\n\nTrackingEventBuffer is a buffer that saves the log.\n\nParchment defines a class SQLiteBuffer that uses SQLite to store logs.\n\nThis implementation can be replaced by a class that is compatible with TrackingEventBuffer.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk-kohey%2Fparchment-swift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fk-kohey%2Fparchment-swift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk-kohey%2Fparchment-swift/lists"}