2018-09-20

Simple Events in Swift

Events are useful when you need more than one delegate or event handler. Here is a simple class that can be used to manage events:

public class Event<T> {
// Return false if the event handler should be removed, ie: if the class instance used in the event handler is no longer valid
// { [weak self] val in
// guard let strongSelf = self else {
// return false
// }
// strongSelf.doStuff(with: val)
// return true
// }
public typealias EventHandler = ((T) -> Bool)
public init() {}
public func notify(_ event: T) {
for (uuid, eventHandler) in uiEventHandlers {
DispatchQueue.main.async { [weak self] in
if eventHandler(event) == false {
self?.uiEventHandlers[uuid] = nil
}
}
}
for (uuid, eventHandler) in eventHandlers {
if eventHandler(event) == false {
eventHandlers[uuid] = nil
}
}
}
public func subscribe(uuid: UUID, eventHandler: @escaping EventHandler) {
eventHandlers[uuid] = eventHandler
}
public func uiSubscribe(uuid: UUID, eventHandler: @escaping EventHandler) {
uiEventHandlers[uuid] = eventHandler
}
public func uiUnsubscribe(uuid: UUID) {
uiEventHandlers[uuid] = nil
}
public func unsubscribe(uuid: UUID) {
eventHandlers[uuid] = nil
}
private var eventHandlers: [UUID: EventHandler] = [:]
private var uiEventHandlers: [UUID: EventHandler] = [:]
}
view raw Event.swift hosted with ❤ by GitHub



Use the uiSubscribe and uiUnsubscribe to have the Event class automatically run the event handler on the main DispatchQueue (for GUI related stuff). Here is an example of how to use them:

class EventProducer {
var value: Int = 0 {
didSet {
valueEvent.notify(value)
}
}
let valueEvent = Event<Int>()
}
class EventConsumer {
init(producer: EventProducer) {
producersValue = producer.value
producer.valueEvent.subscribe(uuid: uuid) { [weak self] value in
guard let strongSelf = self else {
return false
}
print("Value changed: \(value)")
strongSelf.producersValue = value
return true
}
}
private var producersValue: Int
private let uuid = UUID()
}
let producer = EventProducer()
let consumer = EventConsumer(producer: producer)
producer.value = 5

2018-09-18

UIColor Extension for RGBA Hex and HSLA

Here is an extension for UIColor that I've found useful when working with Photoshop and Web designers. It allows you to specify a UIColor in RGBA hex or HSLA.

extension UIColor {
// Hex init
// UIColor(r: 0xFF, g: 0xFF, b: 0xFF)
convenience init(r: UInt8, g: UInt8, b: UInt8, a: UInt8=0xFF) {
assert(r >= 0 && r <= 255, "Invalid red component")
assert(g >= 0 && g <= 255, "Invalid green component")
assert(b >= 0 && b <= 255, "Invalid blue component")
assert(a >= 0 && a <= 255, "Invalid alpha component")
self.init(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: CGFloat(a) / 255.0)
}
// HSL init
// UIColor(h: 270.0, s: 1.0, l: 1.0)
convenience init(h: CGFloat, s: CGFloat, l: CGFloat, a: CGFloat=1.0) {
assert(h >= 0.0 && h <= 360.0, "Invalid hue component")
assert(s >= 0.0 && s <= 1.0, "Invalid saturation component")
assert(l >= 0.0 && l <= 1.0, "Invalid lightness component")
assert(a >= 0.0 && a <= 1.0, "Invalid alpha component")
let c = (1 - abs(2 * l - 1)) * s
let hPrime = h / 60
let x = c * (1 - abs(hPrime.truncatingRemainder(dividingBy: 2) - 1))
var r1, g1, b1: CGFloat
if 0.0 <= hPrime && hPrime <= 1.0 {
r1 = c; g1 = x; b1 = 0
} else if 1.0 <= hPrime && hPrime <= 2.0 {
r1 = x; g1 = c; b1 = 0
} else if 2.0 <= hPrime && hPrime <= 3.0 {
r1 = 0; g1 = c; b1 = x
} else if 3.0 <= hPrime && hPrime <= 4.0 {
r1 = 0; g1 = x; b1 = c
} else if 4.0 <= hPrime && hPrime <= 5.0 {
r1 = x; g1 = 0; b1 = c
} else {
r1 = c; g1 = 0; b1 = x
}
let m = l - 0.5 * c
self.init(red: r1 + m, green: g1 + m, blue: b1 + m, alpha: a)
}
}
view raw UIColor.swift hosted with ❤ by GitHub

PreferenceKey Quirks

PreferenceKey has a few quirks that have tripped me up. One is the reduce(value:nextValue:) implementation. The other is that a PreferenceKe...