Skip to content

Commit

Permalink
Merge pull request #10 from MohammadRezaAnsari/develop
Browse files Browse the repository at this point in the history
Add document
  • Loading branch information
MohammadRezaAnsari authored Aug 12, 2021
2 parents 65327fa + f91583b commit 3a759d3
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 53 deletions.
3 changes: 3 additions & 0 deletions Sources/SocketKit/Socket/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
//
// Created by MohammadReza Ansary on 8/2/21.
//
// Linkedin: https://www.linkedin.com/in/mohammadrezaansary/
// GitHub: https://github.com/MohammadRezaAnsari
//

import Foundation
import PusherSwift
Expand Down
3 changes: 3 additions & 0 deletions Sources/SocketKit/Socket/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
//
// Created by MohammadReza Ansary on 8/2/21.
//
// Linkedin: https://www.linkedin.com/in/mohammadrezaansary/
// GitHub: https://github.com/MohammadRezaAnsari
//

import Foundation

Expand Down
136 changes: 92 additions & 44 deletions Sources/SocketKit/Socket/Socket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,86 +4,132 @@
//
// Created by MohammadReza Ansary on 7/19/21.
//
// Linkedin: https://www.linkedin.com/in/mohammadrezaansary/
// GitHub: https://github.com/MohammadRezaAnsari
//

import Foundation
import PusherSwift


protocol SocketManager {
func establishSocket(_ key: String) throws
func disbandSocket()
func subscribe(_ name: String) throws
func unsubscribe(_ name: String) throws
}

public class Socket {

public static let `default`: Socket = {
Socket()
}()

private init() {
delegate = self
}


private var pusher: Pusher!
private lazy var parser: Parser = { JSONParser() }()


/// The delegate will be set whenever the socket did established.
/// - Note: The delegate default value did set in the Socket initializer.
public weak var delegate: PusherDelegate?


/// Socket Options
///
/// The options parameter on the Pusher constructor is an optional parameter used to apply configuration on a newly created Pusher instance.
///
/// - Warning: If options are nil, establishing socket will throw `SocketError.emptyOption`
/// - Note: Options conform `PusherClientOptions`
public var options: Options?

private init() {}

/// A parser for parsing event data
/// ‌‌Base on your sever events data type choose and set the correct parser.
/// - Note: parser default value is a `JSONParser`.
public lazy var parser: Parser = { JSONParser() }()
}



// MARK: - Socket Manager
extension Socket: SocketManager {
// MARK: - Socket Base Methods
extension Socket {

public func establishSocket(_ key: String) throws {
guard let options = options else {
throw(SocketError.emptyOption)
}

/// Establishing socket connection with specific key
///
/// Initializing pusher and connect via key and options and set pusher delegate.
///
/// - Parameter key: The application key is a string which is globally unique to your application. It can be found in the API Access section of your application within the Channels user dashboard.
/// - Precondition: If options are nil, establishing socket will throw `SocketError.emptyOption`
public final func establishSocket(_ key: String) throws {

guard let options = options else { throw(SocketError.emptyOption) }

pusher = Pusher.init(key: key, options: options.pusherClientOptions)
pusher.delegate = self
pusher.delegate = delegate
pusher.connect()
}

public func disbandSocket() {

/// Easily disconnect from pusher
/// - Note: All options and variables which have been set before will be remain.
public final func disbandSocket() {
unsubscribeAll()
pusher.disconnect()
}

public func subscribe(_ name: String) throws {
guard !pusher.connection.channels.channels.contains(where: { $0.key == name }) else {
throw(SocketError.channelExist)
}

/// Unsubscribe all channels.
/// - SeeAlso: `disbandSocket()`
public final func unsubscribeAll() {
pusher.unsubscribeAll()
}


/// Subscribing to given channel name via pusher.
///
/// - Parameter name: The name of the channel to subscribe to
/// - Warning: If pusher contains a channel with the given name, it will throw `SocketError.channelExist`
private func subscribe(_ name: String) throws {
guard !pusher.connection.channels.channels.contains(where: { $0.key == name }) else { throw(SocketError.channelExist) }
_ = pusher.subscribe(name)
}

public func unsubscribe(_ name: String) throws {
guard pusher.connection.channels.channels.contains(where: { $0.key == name }) else {
throw(SocketError.noChannel)
}

/// Unsubscribing to given channel name via pusher.
///
/// - Parameter name: The name of the channel to unsubscribe to
/// - Warning: If pusher NOT contains a channel with the given name, it will throw `SocketError.noChannel`
private func unsubscribe(_ name: String) throws {
guard pusher.connection.channels.channels.contains(where: { $0.key == name }) else { throw(SocketError.noChannel) }
pusher.unsubscribe(name)
}

public func unsubscribeAll() {
pusher.unsubscribeAll()
}
}


// MARK: - Socket Function
extension Socket {

public func subscribe<T: Codable>(for event: String, on channel: String, handler: @escaping (Result<T, Error>) -> Void) {

/// Subscribe on the channel and bind receiving event to result.
///
///
/// ## Important ##
/// * Recursively subscribe to channel if the channel does not subscribe before, then it calls itself.
///
/// - Parameter event: The name of the event to bind to
/// - Parameter channel: The name of the channel to subscribe to
/// - Parameter result: The return value of the method which is` Result<T: Codable, Error>` type
open func subscribe<T: Codable>(for event: String, on channel: String, result: @escaping (Result<T, Error>) -> Void) {


// Subscribing to channel
guard let channel = pusher.connection.channels.channels.first(where: { $0.key == channel }) else {

do {
try subscribe(channel)
subscribe(for: event, on: channel, handler: handler)
} catch let error {
handler(.failure(error as! SocketError))
subscribe(for: event, on: channel, result: result)
}
catch let error {
assertionFailure("ForDebug: Logically it should not happen at all.")
result(.failure(error as! SocketError))
}
return
}


// Binding event
channel.value.bind(eventName: event) { event in

guard let json: String = event.data, let jsonData: Data = json.data(using: .utf8) else {
Expand All @@ -92,23 +138,25 @@ extension Socket {
}

do {
let result = try self.parser.parseData(jsonData, to: T.self)
handler(.success(result))
let data = try self.parser.parseData(jsonData, to: T.self)
result(.success(data))
}
catch let error {
handler(.failure(SocketError.parsingError(error)))
result(.failure(SocketError.parsingError(error)))
}
}
}


/// Trigger an event to given channel
public func trigger<T: Codable>(for event: String, on channel: String, data: T) {
print("trigger event")
print("ForDebug: The trigger is under develop.")
}
}



// MARK: - Pusher Delegate
// MARK: - Default Pusher Delegate
extension Socket: PusherDelegate {

public func debugLog(message: String) {
Expand Down
17 changes: 8 additions & 9 deletions Sources/SocketKit/Socket/SocketError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,27 @@
//
// Created by MohammadReza Ansary on 8/2/21.
//
// Linkedin: https://www.linkedin.com/in/mohammadrezaansary/
// GitHub: https://github.com/MohammadRezaAnsari
//

public enum SocketError: Error {

case emptyOption
case channelExist
case noChannel
case parsingError(_ error: Error)

public var message: String {
switch self {
case .emptyOption: return "There is no option."
case .channelExist: return "The channel currently is subscribing."
case .noChannel: return "There is no channel with given name."
case .emptyOption: return "Pusher options did NOT set."
case .channelExist: return "The channel currently is subscribing."
case .noChannel: return "There is NO channel with given name."
case .parsingError(let error): return "Parsing event data got error: \(error.localizedDescription)"
}
}

public var localizedDescription: String {
switch self {
case .emptyOption: return "There is no option."
case .channelExist: return "The channel currently is subscribing."
case .noChannel: return "There is no channel with given name."
case .parsingError(let error): return "Parsing event data got error: \(error.localizedDescription)"
}
return message
}
}

0 comments on commit 3a759d3

Please sign in to comment.