Skip to content

Instantly share code, notes, and snippets.

@BenziAhamed
Created February 27, 2019 12:41
Show Gist options
  • Save BenziAhamed/6bff0984182244e42e01e563d5cf6961 to your computer and use it in GitHub Desktop.
Save BenziAhamed/6bff0984182244e42e01e563d5cf6961 to your computer and use it in GitHub Desktop.
Simulates a chat room
import Cocoa
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
struct ChatMessage {
let id: String
let senderId: String
let message: String
let timestamp: Double
}
extension ChatMessage : CustomDebugStringConvertible {
var debugDescription: String {
let t = String(format: "%06.3f", timestamp)
return "[\(t)] \(senderId): \(message)"
}
}
protocol ChatRoomProxy : class {
var elapsedTime: TimeInterval { get }
var users: [String] { get }
func push(message: ChatMessage)
}
extension ChatRoomProxy {
func pushText(_ text: String) {
let m = ChatMessage.init(id: UUID().uuidString, senderId: users.first!, message: text, timestamp: elapsedTime)
push(message: m)
}
func pushHello(timeOffset: TimeInterval = 0) {
let m = ChatMessage.init(id: UUID().uuidString, senderId: users.randomElement()!, message: MessageGenerator.hello, timestamp: elapsedTime + timeOffset)
push(message: m)
}
}
class ChatRoomUpdateSource {
func update(proxy: ChatRoomProxy) { }
}
class ChatRoom : ChatRoomProxy {
let users: [String]
let tickInterval: TimeInterval
private let updateSources: [ChatRoomUpdateSource]
private var messages: [ChatMessage] = []
private let q = DispatchQueue(label: "com.gojek.chatroom")
private(set) var elapsedTime: TimeInterval = 0
var onMessageReceived: ((ChatMessage) -> Void)?
private var timer: Timer?
init(users: [String], updateSources: [ChatRoomUpdateSource], tickInterval: TimeInterval = 0.5) {
self.users = users
self.updateSources = updateSources
self.tickInterval = tickInterval
}
func start() {
timer = Timer.scheduledTimer(withTimeInterval: tickInterval, repeats: true, block: { timer in
self.updateSources.forEach { $0.update(proxy: self) }
self.elapsedTime += self.tickInterval
})
}
func stop() {
timer?.invalidate()
elapsedTime = 0
}
func push(message: ChatMessage) {
q.sync {
self.messages.append(message)
self.onMessageReceived?(message)
}
}
}
struct MessageGenerator {
static var hello: String {
return ["Hi", "Hello", "Hey there", "Hiya", "Aloha",
"How r u?", "Hows it been?", "Yo", "Dude!", "Hey bro",
"Hi hi", "Heya", "Hi yall", "Hey fellas", "Hoho",
"Long time!", "Ahem", "Heya", "Hellaws", "his",
"hiya", "hello", "hey", "heya", "hey there"
].randomElement()!
}
}
struct ScheduledTask {
let trigger: TimeInterval
let minRange: TimeInterval
let maxRange: TimeInterval
let action: (ChatRoomProxy) -> Void
let repeats: Bool
}
extension ScheduledTask {
static func once(at: TimeInterval, action: @escaping (ChatRoomProxy) -> Void) -> ScheduledTask {
return ScheduledTask(trigger: at, minRange: 0, maxRange: 0, action: action, repeats: false)
}
static func every(interval: TimeInterval, action: @escaping (ChatRoomProxy) -> Void, minRange: TimeInterval = 0, maxRange: TimeInterval = 0) -> ScheduledTask {
return ScheduledTask(trigger: interval, minRange: minRange, maxRange: maxRange, action: action, repeats: true)
}
var initialTrigger: TimeInterval {
if !repeats {
return trigger
}
return nextTrigger
}
var nextTrigger: TimeInterval {
if !repeats {
return TimeInterval.greatestFiniteMagnitude
}
if minRange != 0, maxRange != 0 {
return trigger
}
let min: TimeInterval = trigger - minRange
let max: TimeInterval = trigger + maxRange
let random: TimeInterval = TimeInterval.random(in: min...max)
return random
}
}
class ScheduledChatRoomUpdateSource : ChatRoomUpdateSource {
class ScheduledTrigger {
var nextTrigger: TimeInterval
let task: ScheduledTask
init(_ task: ScheduledTask) {
self.task = task
self.nextTrigger = task.initialTrigger
}
func process(proxy: ChatRoomProxy) {
if proxy.elapsedTime >= nextTrigger {
task.action(proxy)
nextTrigger = proxy.elapsedTime + task.nextTrigger
}
}
}
private var triggers = [ScheduledTrigger]()
func once(at: TimeInterval, action: @escaping (ChatRoomProxy) -> Void) {
triggers.append(ScheduledTrigger.init(ScheduledTask.once(at: at, action: action)))
}
func every(interval: TimeInterval, minRange: TimeInterval = 0, maxRange: TimeInterval = 0, action: @escaping (ChatRoomProxy) -> Void) {
triggers.append(ScheduledTrigger.init(ScheduledTask.every(interval: interval, action: action, minRange: minRange, maxRange: maxRange)))
}
override func update(proxy: ChatRoomProxy) {
triggers.forEach {
$0.process(proxy: proxy)
}
}
}
extension ChatRoom {
static func create() -> ChatRoom {
let schedule = ScheduledChatRoomUpdateSource()
schedule.once(at: 0) { (proxy) in
proxy.pushText("🏁 START")
}
schedule.every(interval: 5.0) { (proxy) in
proxy.pushText("🏁 CHECKPOINT")
}
schedule.every(interval: 2.0) { (proxy) in
let offset = [-1.0,1.0].randomElement()! * TimeInterval.random(in: 0.0...4.0)
proxy.pushHello(timeOffset: offset)
}
let room = ChatRoom(users: ["A", "B", "C", "D", "E", "F", "G"], updateSources: [schedule])
return room
}
}
let room = ChatRoom.create()
room.onMessageReceived = { message in
print(message)
}
room.start()
@BenziAhamed
Copy link
Author

Prints a random message stream, with induced latencies when receiving messages.

See sample output below

[00.000] A: START
[01.539] A: Hiya
[01.317] C: Hi
[05.000] A: CHECKPOINT
[03.082] G: Hey fellas
[11.371] A: Dude!
[10.000] A: CHECKPOINT
[12.991] B: hey
[11.535] D: Hello
[14.487] A: Hiya
[15.000] A: CHECKPOINT

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment