Lightning ⚡
Lightning provides components to make Swift development easier.
If you're looking to migrate from an old version, see releases.
Components
Property Wrapper: @Stored 📦
Stored is a property wrapper that read/write values using an internal key-value store.
Unlike widely used
@UserDefaultproperty wrapper,@Storedcan internally use any key-value store that conforms toKeyValueStoreProtocol. There are two pre-defined key-value stores in Lightning:
UserDefaultsInMemoryKeyValueStore: A key-value store that uses an internal dictionary to store data. Useful when unit-testing.
Definition:
final class UserPreferences {
@Stored var temperatureUnit: TemperatureUnit?
@Stored var weightUnit: WeightUnit
// Inject store into wrappers here:
init<S: KeyValueStoreProtocol>(store: S) where S.Key == String {
_temperatureUnit = Stored(key: "temperature-unit", store: store)
_weightUnit = Stored(key: "weight-unit", defaultValue: .grams, store: store)
}Usage in app target:
let store = UserDefaults.standard
let preferences = UserPreferences(store: store)
preferences.temperatureUnit = .celsiusUsage in test target:
let store = InMemoryKeyValueStore<String>() // Internally a simple [String: Any] dictionary.
let preferences = UserPreferences(store: store)
preferences.temperatureUnit = .celsiusSupported types:
- Primitives:
Data,String,Date,NSNumber,Int,UInt,Double,Float,Bool,URL - Any
RawRepresentable: Conform toStorableon anyRawRepresentabletype. No extra implementation needed. - Any
Codable: Conform toStorableCodableon anyCodabletype. No extra implementation needed. - Any
Storable: Conform toStorableprotocol and provide custom implementation for your type.
Channel 🗼
Channel is now a part of Rasat!
StringFormatter 🖊️
// - Perform 05308808080 -> 0 (530) 880 80 80
let phoneFormatter = StringFormatter(pattern: "# (###) ### ## ##")
let formattedNumber = phoneFormatter.format("05308808080") // Returns "0 (530) 880 80 80"StringMask 🙈
// - Perform 1111222233334444 -> ********33334444
let cardMask = StringMask(ranges: [NSRange(location: 0, length: 8)])
let cardMaskStorage = StringMaskStorage(mask: mask)
// 1. Pass it into the storage:
cardMaskStorage.original = "1111222233334444"
// 2. Read masked & unmasked value back:
let cardNo = cardMaskStorage.original // "1111222233334444"
let maskedCardNo = cardMaskStorage.masked // "********33334444"Atomic ⚛️
Atomic is a thread safe container for values.
var list = Atomic(["item1"])
// Get value:
let items = list.value
// Set value:
list.value = ["item1", "item2"]
// Read block:
list.read { items in
print(items)
}
// Write block:
list.write { items in
items.append(...)
}TimerController ⏱️
TimerController is a wrapper around Timer, which makes it easy to implement countdowns.
let ticker = Ticker() // or MockTicker()
let timerController = TimerController(total: 60, interval: 1, ticker: ticker)
timerController.startTimer { state in
timerLabel.text = "\(state.remaining) seconds remaining..."
}Weak & WeakArray 🗃️
Weakis a wrapper to reference an object weakly.WeakArrayis anArraythat references its elements weakly. (Similar toNSPointerArray.)
Following example shows how it can be used for request cancelling.
var liveRequests = WeakArray<URLSessionTask>()
func viewDidLoad() {
super.viewDidLoad()
// Following async requests will be live until we get a response from server.
// Keep a weak reference to each to be able to cancel when necessary.
let offersRequest = viewModel.getOffers { ... }
liveRequests.appendWeak(offersRequest)
let favoritesRequest = viewModel.getFavorites { ... }
liveRequests.appendWeak(favoritesRequest)
}
func viewWillDisappear() {
super.viewWillDisappear()
liveRequests.elements.forEach { $0.cancel() }
liveRequests.removeAll()
}ActivityState ⌛
Component to track live activities. Mostly used to show/hide loading view as in the following example.
var activityState = ActivityState() {
didSet {
guard activityState.isToggled else { return }
if activityState.isActive {
// Show loading view.
} else {
// Hide loading view.
}
}
}
func someProcess() {
activityState.add()
asyncCall1() {
// ...
activityState.add()
asyncCall2() {
// ...
activityState.remove()
}
activityState.remove()
}
}CollectionChange 📱 📲
public enum CollectionChange {
case reload
case update(IndexPathSetConvertible)
case insertion(IndexPathSetConvertible)
case deletion(IndexPathSetConvertible)
case move(from: IndexPathConvertible, to: IndexPathConvertible)
}Enum to encapsulate change in any collection. Can be used to model UITableView/UICollectionView or any CollectionType changes.
func addCustomer(_ customer: Customer) -> CollectionChange {
customers.insert(customer, at: 0)
return .insertion(0)
}Extensions
Lightning provides extensions on known types with zap
String+Helpers
let string = "Welcome"
// Int -> String.Index conversion:
let index1 = string.zap_index(1)
let eChar = string[index1] // "e"
let eChar = string.zap_character(at: 1) // "e"
// NSRange -> Range<String.Index> conversion:
let nsRange = NSRange(location: 0, length: 3)
let substring = string.zap_substring(with: nsRange) // "Wel"
let stringRange = string.zap_range(from: nsRange)
let substring = string.substring(with: stringRange) // "Wel"
// Range validation for NSRange -> Range<String.Index>:
let shortString = "Go"
let intersectedRange = shortString.zap_rangeIntersection(with: nsRange)
// `nsRange` [0, 2] is out of bounds for "Go". Intersection is [0, 1].Dictionary+Helpers
Introduces + and += operators.
let dict1 = ["k1": "v1", "k2": "v2"]
let dict2 = ["k3": "v3"]
var dict3 = dict1 + dict2 // [(k1: v1), (k2: v2), (k3: v3)]
dict3 += ["k4": "v4"] // [(k1: v1), (k2: v2), (k3: v3), (k4: v4)]
dict3 += ["k4": "xx"] // [(k1: v1), (k2: v2), (k3: v3), (k4: xx)]Bundle+Helpers
Provides version string helpers.
// Version field:
bundle.zap_shortVersionString // 1.2.1
// Build field:
bundle.zap_versionString // 345Installation
Using CocoaPods
Add the following line to your Podfile:
pod 'Lightning'
Using Carthage
Add the following line to your Cartfile:
github "gokselkoksal/Lightning"
Manually
Drag and drop Sources folder to your project.
It's highly recommended to use a dependency manager like CocoaPods or Carthage.
License
Lightning is available under the MIT license.