{"id":13907368,"url":"https://github.com/gokselkoksal/Lightning","last_synced_at":"2025-07-18T05:31:21.671Z","repository":{"id":56919783,"uuid":"74213467","full_name":"gokselkoksal/Lightning","owner":"gokselkoksal","description":"Lightning provides components to make Swift development easier.","archived":false,"fork":false,"pushed_at":"2023-09-18T21:10:50.000Z","size":173,"stargazers_count":96,"open_issues_count":2,"forks_count":4,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-06-19T16:16:45.462Z","etag":null,"topics":["helper","library","swift","utility"],"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/gokselkoksal.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,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-11-19T14:29:15.000Z","updated_at":"2025-02-04T23:00:13.000Z","dependencies_parsed_at":"2024-01-19T10:21:13.850Z","dependency_job_id":"1cf36340-a6d1-4e79-88e9-94f328bf351f","html_url":"https://github.com/gokselkoksal/Lightning","commit_stats":{"total_commits":153,"total_committers":4,"mean_commits":38.25,"dds":0.05228758169934644,"last_synced_commit":"47c1ed7d8453bb7beeeea4d336d39ea727037c23"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/gokselkoksal/Lightning","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gokselkoksal%2FLightning","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gokselkoksal%2FLightning/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gokselkoksal%2FLightning/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gokselkoksal%2FLightning/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gokselkoksal","download_url":"https://codeload.github.com/gokselkoksal/Lightning/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gokselkoksal%2FLightning/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265705343,"owners_count":23814430,"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":["helper","library","swift","utility"],"created_at":"2024-08-06T23:01:54.497Z","updated_at":"2025-07-18T05:31:21.348Z","avatar_url":"https://github.com/gokselkoksal.png","language":"Swift","funding_links":[],"categories":["HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"# Lightning :zap:\n\n[![Carthage](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![CocoaPods](https://img.shields.io/cocoapods/v/Lightning.svg?style=flat)](http://cocoapods.org/pods/Lightning)\n[![CI Status](http://img.shields.io/travis/gokselkoksal/Lightning.svg?style=flat)](https://travis-ci.org/gokselkoksal/Lightning)\n[![Platform](https://img.shields.io/cocoapods/p/Lightning.svg?style=flat)](http://cocoadocs.org/docsets/Lightning)\n[![Language](https://img.shields.io/badge/swift-5.0-orange.svg)](http://swift.org)\n[![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/gokselkoksal/Lightning/blob/master/LICENSE)\n\nLightning provides components to make Swift development easier.\n\n\u003e If you're looking to migrate from an old version, [see releases](https://github.com/gokselkoksal/Lightning/releases).\n\n## Components\n\n### Property Wrapper: @Stored :package:\n\n`Stored` is a property wrapper that read/write values using an internal key-value store.\n\n\u003e Unlike widely used `@UserDefault` property wrapper, `@Stored` can internally use any key-value store that conforms to `KeyValueStoreProtocol`. There are two pre-defined key-value stores in Lightning:\n\u003e \n\u003e * `UserDefaults`\n\u003e * `InMemoryKeyValueStore`: A key-value store that uses an internal dictionary to store data. Useful when unit-testing.\n\n**Definition:**\n\n```swift\nfinal class UserPreferences {\n\n  @Stored var temperatureUnit: TemperatureUnit?\n  @Stored var weightUnit: WeightUnit\n  \n  // Inject store into wrappers here:\n  init\u003cS: KeyValueStoreProtocol\u003e(store: S) where S.Key == String {\n    _temperatureUnit = Stored(key: \"temperature-unit\", store: store)\n    _weightUnit = Stored(key: \"weight-unit\", defaultValue: .grams, store: store)\n}\n```\n\n**Usage in app target:**\n\n```swift\nlet store = UserDefaults.standard\nlet preferences = UserPreferences(store: store)\npreferences.temperatureUnit = .celsius\n```\n\n**Usage in test target:**\n\n```swift\nlet store = InMemoryKeyValueStore\u003cString\u003e() // Internally a simple [String: Any] dictionary.\nlet preferences = UserPreferences(store: store)\npreferences.temperatureUnit = .celsius\n```\n\n**Supported types:**\n\n* **Primitives**: `Data`, `String`, `Date`, `NSNumber`, `Int`, `UInt`, `Double`, `Float`, `Bool`, `URL`\n* **Any `RawRepresentable`**: Conform to `Storable` on any `RawRepresentable` type. No extra implementation needed.\n* **Any `Codable`**: Conform to `StorableCodable` on any `Codable` type. No extra implementation needed.\n* **Any `Storable`**: Conform to `Storable` protocol and provide custom implementation for your type.\n\n### Channel :tokyo_tower:\nChannel is now a part of [Rasat](https://github.com/gokselkoksal/Rasat)!\n\n### StringFormatter :pen:\n```swift\n// - Perform 05308808080 -\u003e 0 (530) 880 80 80\n\nlet phoneFormatter = StringFormatter(pattern: \"# (###) ### ## ##\")\nlet formattedNumber = phoneFormatter.format(\"05308808080\") // Returns \"0 (530) 880 80 80\"\n```\n\n### StringMask :see_no_evil:\n```swift\n// - Perform 1111222233334444 -\u003e ********33334444\n\nlet cardMask = StringMask(ranges: [NSRange(location: 0, length: 8)])\nlet cardMaskStorage = StringMaskStorage(mask: mask)\n\n// 1. Pass it into the storage:\ncardMaskStorage.original = \"1111222233334444\"\n// 2. Read masked \u0026 unmasked value back:\nlet cardNo = cardMaskStorage.original     // \"1111222233334444\"\nlet maskedCardNo = cardMaskStorage.masked // \"********33334444\"\n```\n\n### Atomic :atom_symbol:\n`Atomic` is a thread safe container for values.\n```swift\nvar list = Atomic([\"item1\"])\n\n// Get value:\nlet items = list.value\n\n// Set value:\nlist.value = [\"item1\", \"item2\"]\n\n// Read block:\nlist.read { items in\n  print(items)\n}\n\n// Write block:\nlist.write { items in\n  items.append(...)\n}\n```\n\n### TimerController :stopwatch:\n`TimerController` is a wrapper around `Timer`, which makes it easy to implement countdowns.\n```swift\nlet ticker = Ticker() // or MockTicker()\nlet timerController = TimerController(total: 60, interval: 1, ticker: ticker)\ntimerController.startTimer { state in\n  timerLabel.text = \"\\(state.remaining) seconds remaining...\"\n}\n```\n\n### Weak \u0026 WeakArray :card_file_box:\n- `Weak` is a wrapper to reference an object weakly.\n- `WeakArray` is an `Array` that references its elements weakly. (Similar to `NSPointerArray`.)\n\nFollowing example shows how it can be used for request cancelling.\n\n```swift\nvar liveRequests = WeakArray\u003cURLSessionTask\u003e()\n\nfunc viewDidLoad() {\n  super.viewDidLoad()\n  // Following async requests will be live until we get a response from server.\n  // Keep a weak reference to each to be able to cancel when necessary.\n  let offersRequest = viewModel.getOffers { ... }\n  liveRequests.appendWeak(offersRequest)\n  let favoritesRequest = viewModel.getFavorites { ... }\n  liveRequests.appendWeak(favoritesRequest)\n}\n\nfunc viewWillDisappear() {\n  super.viewWillDisappear()\n  liveRequests.elements.forEach { $0.cancel() }\n  liveRequests.removeAll()\n}\n```\n\n### ActivityState :hourglass:\nComponent to track live activities. Mostly used to show/hide loading view as in the following example.\n\n```swift\nvar activityState = ActivityState() {\n  didSet {\n    guard activityState.isToggled else { return }\n    if activityState.isActive {\n      // Show loading view.\n    } else {\n      // Hide loading view.\n    }\n  }\n}\n\nfunc someProcess() {\n  activityState.add()\n  asyncCall1() {\n    // ...\n    activityState.add()\n    asyncCall2() {\n      // ...\n      activityState.remove()\n    }\n    activityState.remove()\n  }\n}\n```\n\n### CollectionChange :iphone::calling:\n```swift\npublic enum CollectionChange {\n  case reload\n  case update(IndexPathSetConvertible)\n  case insertion(IndexPathSetConvertible)\n  case deletion(IndexPathSetConvertible)\n  case move(from: IndexPathConvertible, to: IndexPathConvertible)\n}\n```\nEnum to encapsulate change in any collection. Can be used to model `UITableView`/`UICollectionView` or any `CollectionType` changes.\n\n```swift\nfunc addCustomer(_ customer: Customer) -\u003e CollectionChange {\n  customers.insert(customer, at: 0)\n  return .insertion(0)\n}\n```\n\n## Extensions\nLightning provides extensions on known types with `zap` :zap: prefix.\n\n### String+Helpers\n```swift\nlet string = \"Welcome\"\n\n// Int -\u003e String.Index conversion:\nlet index1 = string.zap_index(1)\nlet eChar = string[index1] // \"e\"\nlet eChar = string.zap_character(at: 1) // \"e\"\n\n// NSRange -\u003e Range\u003cString.Index\u003e conversion:\nlet nsRange = NSRange(location: 0, length: 3)\nlet substring = string.zap_substring(with: nsRange) // \"Wel\"\nlet stringRange = string.zap_range(from: nsRange)\nlet substring = string.substring(with: stringRange) // \"Wel\"\n\n// Range validation for NSRange -\u003e Range\u003cString.Index\u003e:\nlet shortString = \"Go\"\nlet intersectedRange = shortString.zap_rangeIntersection(with: nsRange)\n// `nsRange` [0, 2] is out of bounds for \"Go\". Intersection is [0, 1].\n```\n\n### Dictionary+Helpers\nIntroduces `+` and `+=` operators.\n```swift\nlet dict1 = [\"k1\": \"v1\", \"k2\": \"v2\"]\nlet dict2 = [\"k3\": \"v3\"]\nvar dict3 = dict1 + dict2 // [(k1: v1), (k2: v2), (k3: v3)]\ndict3 += [\"k4\": \"v4\"]     // [(k1: v1), (k2: v2), (k3: v3), (k4: v4)]\ndict3 += [\"k4\": \"xx\"]     // [(k1: v1), (k2: v2), (k3: v3), (k4: xx)]\n```\n\n### Bundle+Helpers\nProvides version string helpers.\n```swift\n// Version field:\nbundle.zap_shortVersionString // 1.2.1\n\n// Build field:\nbundle.zap_versionString      // 345\n```\n\n## Installation\n\n### Using [CocoaPods](https://github.com/CocoaPods/CocoaPods)\nAdd the following line to your `Podfile`:\n```\npod 'Lightning'\n```\n\n### Using [Carthage](https://github.com/Carthage/Carthage)\nAdd the following line to your `Cartfile`:\n```\ngithub \"gokselkoksal/Lightning\"\n```\n\n### Manually\nDrag and drop `Sources` folder to your project. \n\n*It's highly recommended to use a dependency manager like `CocoaPods` or `Carthage`.*\n\n## License\nLightning is available under the [MIT license](https://github.com/gokselkoksal/Lightning/blob/master/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgokselkoksal%2FLightning","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgokselkoksal%2FLightning","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgokselkoksal%2FLightning/lists"}