Compare commits

...

14 Commits

Author SHA1 Message Date
Erik Little
1faa4b925c
Don't use NSDictionary 2017-09-17 11:31:24 -04:00
Erik Little
c20c286864
small refactor 2017-09-17 11:23:06 -04:00
Erik Little
239b30f2c8
oops 2017-09-17 11:12:39 -04:00
Erik Little
ecf9336132
keep trying 2017-09-17 11:11:56 -04:00
Erik Little
bcaf7ba62d
still trying 2017-09-17 10:59:27 -04:00
Erik Little
53289dc092
try and get something to compile 2017-09-17 10:54:05 -04:00
Erik Little
d0420bc3c6
Even more linux fiddling 2017-09-17 10:45:15 -04:00
Erik Little
26af5a6296
More linux stuff 2017-09-17 10:42:41 -04:00
Erik Little
4eccf87ecc
some ugly for linux 2017-09-17 10:35:52 -04:00
Erik Little
b85e42bbf9
add dummy protocol for linux 2017-09-16 12:13:35 -04:00
Erik Little
7120d89a17
Guard import 2017-09-16 12:09:53 -04:00
Erik Little
e0d8f16666
More linux work 2017-09-16 12:08:38 -04:00
Erik Little
95b3bfc802
more linux work 2017-09-16 11:55:06 -04:00
Erik Little
afc5a561d0
Start work on linux 2017-09-16 11:08:16 -04:00
9 changed files with 388 additions and 73 deletions

View File

@ -1,9 +1,15 @@
import PackageDescription import PackageDescription
let deps: [Package.Dependency]
#if !os(Linux)
deps = [.Package(url: "https://github.com/nuclearace/Starscream", majorVersion: 8)]
#else
deps = [.Package(url: "https://github.com/vapor/engine", majorVersion: 2)]
#endif
let package = Package( let package = Package(
name: "SocketIO", name: "SocketIO",
dependencies: [ dependencies: deps,
.Package(url: "https://github.com/nuclearace/Starscream", majorVersion: 8),
],
exclude: ["Source/Starscream"] exclude: ["Source/Starscream"]
) )

View File

@ -136,6 +136,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
super.init() super.init()
} }
#if !os(Linux)
/// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity. /// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
/// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)` /// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
/// ///
@ -144,6 +145,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
public convenience init(socketURL: NSURL, config: NSDictionary?) { public convenience init(socketURL: NSURL, config: NSDictionary?) {
self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? []) self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? [])
} }
#endif
deinit { deinit {
DefaultSocketLogger.Logger.log("Client is being released", type: SocketIOClient.logType) DefaultSocketLogger.Logger.log("Client is being released", type: SocketIOClient.logType)

View File

@ -22,8 +22,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
import Dispatch
import Foundation import Foundation
#if !os(Linux)
import StarscreamSocketIO import StarscreamSocketIO
#endif
protocol ClientOption : CustomStringConvertible, Equatable { protocol ClientOption : CustomStringConvertible, Equatable {
func getSocketIOOptionValue() -> Any func getSocketIOOptionValue() -> Any

View File

@ -24,7 +24,11 @@
import Dispatch import Dispatch
import Foundation import Foundation
#if !os(Linux)
import StarscreamSocketIO import StarscreamSocketIO
#else
import WebSockets
#endif
/// The class that handles the engine.io protocol and transports. /// The class that handles the engine.io protocol and transports.
/// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods. /// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods.
@ -60,6 +64,9 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
/// **Do not touch this directly** /// **Do not touch this directly**
public var waitingForPost = false public var waitingForPost = false
/// The WebSocket for this engine.
public var ws: WebSocket?
/// `true` if this engine is closed. /// `true` if this engine is closed.
public private(set) var closed = false public private(set) var closed = false
@ -95,6 +102,15 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
/// If `true`, the engine is currently seeing whether it can upgrade to WebSockets. /// If `true`, the engine is currently seeing whether it can upgrade to WebSockets.
public private(set) var probing = false public private(set) var probing = false
/// Whether or not this engine uses secure transports
public private(set) var secure = false
/// A custom security validator for Starscream. Useful for SSL pinning.
public private(set) var security: SSLSecurity?
/// Whether or not to allow self signed certificates.
public private(set) var selfSigned = false
/// The URLSession that will be used for polling. /// The URLSession that will be used for polling.
public private(set) var session: URLSession? public private(set) var session: URLSession?
@ -113,9 +129,6 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
/// If `true`, then the engine is currently in WebSockets mode. /// If `true`, then the engine is currently in WebSockets mode.
public private(set) var websocket = false public private(set) var websocket = false
/// The WebSocket for this engine.
public private(set) var ws: WebSocket?
/// The client for this engine. /// The client for this engine.
public weak var client: SocketEngineClient? public weak var client: SocketEngineClient?
@ -133,9 +146,6 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
private var pongsMissed = 0 private var pongsMissed = 0
private var pongsMissedMax = 0 private var pongsMissedMax = 0
private var probeWait = ProbeWaitQueue() private var probeWait = ProbeWaitQueue()
private var secure = false
private var security: SSLSecurity?
private var selfSigned = false
// MARK: Initializers // MARK: Initializers
@ -206,7 +216,7 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
private func checkAndHandleEngineError(_ msg: String) { private func checkAndHandleEngineError(_ msg: String) {
do { do {
let dict = try msg.toNSDictionary() let dict = try msg.toDictionary()
guard let error = dict["message"] as? String else { return } guard let error = dict["message"] as? String else { return }
/* /*
@ -308,32 +318,6 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
return (urlPolling.url!, urlWebSocket.url!) return (urlPolling.url!, urlWebSocket.url!)
} }
private func createWebSocketAndConnect() {
ws?.delegate = nil // TODO this seems a bit defensive, is this really needed?
ws = WebSocket(url: urlWebSocketWithSid)
if cookies != nil {
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
for (key, value) in headers {
ws?.headers[key] = value
}
}
if extraHeaders != nil {
for (headerName, value) in extraHeaders! {
ws?.headers[headerName] = value
}
}
ws?.callbackQueue = engineQueue
ws?.enableCompression = compress
ws?.delegate = self
ws?.disableSSLCertValidation = selfSigned
ws?.security = security
ws?.connect()
}
/// Called when an error happens during execution. Causes a disconnection. /// Called when an error happens during execution. Causes a disconnection.
public func didError(reason: String) { public func didError(reason: String) {
DefaultSocketLogger.Logger.error("%@", type: SocketEngine.logType, args: reason) DefaultSocketLogger.Logger.error("%@", type: SocketEngine.logType, args: reason)
@ -435,7 +419,7 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
} }
private func handleOpen(openData: String) { private func handleOpen(openData: String) {
guard let json = try? openData.toNSDictionary() else { guard let json = try? openData.toDictionary() else {
didError(reason: "Error parsing open packet") didError(reason: "Error parsing open packet")
return return
@ -486,6 +470,44 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
} }
} }
/// Called when a successful WebSocket connection is made.
public func handleWSConnect() {
if !forceWebsockets {
probing = true
probeWebSocket()
} else {
connected = true
probing = false
polling = false
}
}
/// Called when the WebSocket disconnects.
public func handleWSDisconnect(error: NSError?) {
probing = false
if closed {
client?.engineDidClose(reason: "Disconnect")
return
}
guard websocket else {
flushProbeWait()
return
}
connected = false
websocket = false
if let reason = error?.localizedDescription {
didError(reason: reason)
} else {
client?.engineDidClose(reason: "Socket Disconnected")
}
}
/// Parses raw binary received from engine.io. /// Parses raw binary received from engine.io.
/// ///
/// - parameter data: The data to parse. /// - parameter data: The data to parse.
@ -601,45 +623,19 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
} }
} }
#if !os(Linux)
// MARK: Starscream delegate conformance // MARK: Starscream delegate conformance
/// Delegate method for connection. /// Delegate method for connection.
public func websocketDidConnect(socket: WebSocket) { public func websocketDidConnect(socket: WebSocket) {
if !forceWebsockets { handleWSConnect()
probing = true
probeWebSocket()
} else {
connected = true
probing = false
polling = false
}
} }
/// Delegate method for disconnection. /// Delegate method for disconnection.
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) { public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
probing = false handleWSDisconnect(error: error)
if closed {
client?.engineDidClose(reason: "Disconnect")
return
}
guard websocket else {
flushProbeWait()
return
}
connected = false
websocket = false
if let reason = error?.localizedDescription {
didError(reason: reason)
} else {
client?.engineDidClose(reason: "Socket Disconnected")
}
} }
#endif
} }
extension SocketEngine { extension SocketEngine {

View File

@ -25,6 +25,7 @@
import Foundation import Foundation
#if !os(Linux)
/// Declares that a type will be a delegate to an engine. /// Declares that a type will be a delegate to an engine.
@objc public protocol SocketEngineClient { @objc public protocol SocketEngineClient {
// MARK: Methods // MARK: Methods
@ -54,3 +55,34 @@ import Foundation
/// - parameter data: The data the engine received. /// - parameter data: The data the engine received.
func parseEngineBinaryData(_ data: Data) func parseEngineBinaryData(_ data: Data)
} }
#else
/// Declares that a type will be a delegate to an engine.
public protocol SocketEngineClient : class {
// MARK: Methods
/// Called when the engine errors.
///
/// - parameter reason: The reason the engine errored.
func engineDidError(reason: String)
/// Called when the engine closes.
///
/// - parameter reason: The reason that the engine closed.
func engineDidClose(reason: String)
/// Called when the engine opens.
///
/// - parameter reason: The reason the engine opened.
func engineDidOpen(reason: String)
/// Called when the engine has a message that must be parsed.
///
/// - parameter msg: The message that needs parsing.
func parseEngineMessage(_ msg: String)
/// Called when the engine receives binary data.
///
/// - parameter data: The data the engine received.
func parseEngineBinaryData(_ data: Data)
}
#endif

View File

@ -23,9 +23,15 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
import Dispatch
import Foundation import Foundation
#if !os(Linux)
import StarscreamSocketIO import StarscreamSocketIO
#else
import WebSockets
#endif
#if !os(Linux)
/// Specifies a SocketEngine. /// Specifies a SocketEngine.
@objc public protocol SocketEngineSpec { @objc public protocol SocketEngineSpec {
/// The client for this engine. /// The client for this engine.
@ -40,6 +46,9 @@ import StarscreamSocketIO
/// The connect parameters sent during a connect. /// The connect parameters sent during a connect.
var connectParams: [String: Any]? { get set } var connectParams: [String: Any]? { get set }
/// Whether or not to use WebSocket compression.
var compress: Bool { get }
/// An array of HTTPCookies that are sent during the connection. /// An array of HTTPCookies that are sent during the connection.
var cookies: [HTTPCookie]? { get } var cookies: [HTTPCookie]? { get }
@ -64,6 +73,15 @@ import StarscreamSocketIO
/// If `true`, the engine is currently seeing whether it can upgrade to WebSockets. /// If `true`, the engine is currently seeing whether it can upgrade to WebSockets.
var probing: Bool { get } var probing: Bool { get }
/// Whether or not this engine uses secure transports
var secure: Bool { get }
/// A custom security validator for Starscream. Useful for SSL pinning.
var security: SSLSecurity? { get }
/// Whether or not to allow self signed certificates.
var selfSigned: Bool { get }
/// The session id for this engine. /// The session id for this engine.
var sid: String { get } var sid: String { get }
@ -80,7 +98,7 @@ import StarscreamSocketIO
var websocket: Bool { get } var websocket: Bool { get }
/// The WebSocket for this engine. /// The WebSocket for this engine.
var ws: WebSocket? { get } var ws: WebSocket? { get set }
/// Creates a new engine. /// Creates a new engine.
/// ///
@ -131,6 +149,123 @@ import StarscreamSocketIO
/// - parameter withData: Any data that this message has. /// - parameter withData: Any data that this message has.
func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data]) func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data])
} }
#else
public protocol SocketEngineSpec : class {
/// The client for this engine.
var client: SocketEngineClient? { get set }
/// `true` if this engine is closed.
var closed: Bool { get }
/// `true` if this engine is connected. Connected means that the initial poll connect has succeeded.
var connected: Bool { get }
/// The connect parameters sent during a connect.
var connectParams: [String: Any]? { get set }
/// Whether or not to use WebSocket compression.
var compress: Bool { get }
/// An array of HTTPCookies that are sent during the connection.
var cookies: [HTTPCookie]? { get }
/// The queue that all engine actions take place on.
var engineQueue: DispatchQueue { get }
/// A dictionary of extra http headers that will be set during connection.
var extraHeaders: [String: String]? { get }
/// When `true`, the engine is in the process of switching to WebSockets.
var fastUpgrade: Bool { get }
/// When `true`, the engine will only use HTTP long-polling as a transport.
var forcePolling: Bool { get }
/// When `true`, the engine will only use WebSockets as a transport.
var forceWebsockets: Bool { get }
/// If `true`, the engine is currently in HTTP long-polling mode.
var polling: Bool { get }
/// If `true`, the engine is currently seeing whether it can upgrade to WebSockets.
var probing: Bool { get }
/// Whether or not this engine uses secure transports
var secure: Bool { get }
var security: SSLSecurity? { get }
/// Whether or not to allow self signed certificates.
var selfSigned: Bool { get }
/// The session id for this engine.
var sid: String { get }
/// The path to engine.io.
var socketPath: String { get }
/// The url for polling.
var urlPolling: URL { get }
/// The url for WebSockets.
var urlWebSocket: URL { get }
/// If `true`, then the engine is currently in WebSockets mode.
var websocket: Bool { get }
/// The WebSocket for this engine.
var ws: WebSocket? { get set }
/// Creates a new engine.
///
/// - parameter client: The client for this engine.
/// - parameter url: The url for this engine.
/// - parameter options: The options for this engine.
init(client: SocketEngineClient, url: URL, options: NSDictionary?)
/// Starts the connection to the server.
func connect()
/// Called when an error happens during execution. Causes a disconnection.
func didError(reason: String)
/// Disconnects from the server.
///
/// - parameter reason: The reason for the disconnection. This is communicated up to the client.
func disconnect(reason: String)
/// Called to switch from HTTP long-polling to WebSockets. After calling this method the engine will be in
/// WebSocket mode.
///
/// **You shouldn't call this directly**
func doFastUpgrade()
/// Causes any packets that were waiting for POSTing to be sent through the WebSocket. This happens because when
/// the engine is attempting to upgrade to WebSocket it does not do any POSTing.
///
/// **You shouldn't call this directly**
func flushWaitingForPostToWebSocket()
/// Parses raw binary received from engine.io.
///
/// - parameter data: The data to parse.
func parseEngineData(_ data: Data)
/// Parses a raw engine.io packet.
///
/// - parameter message: The message to parse.
/// - parameter fromPolling: Whether this message is from long-polling.
/// If `true` we might have to fix utf8 encoding.
func parseEngineMessage(_ message: String)
/// Writes a message to engine.io, independent of transport.
///
/// - parameter msg: The message to send.
/// - parameter withType: The type of this message.
/// - parameter withData: Any data that this message has.
func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data])
}
#endif
extension SocketEngineSpec { extension SocketEngineSpec {
var urlPollingWithSid: URL { var urlPollingWithSid: URL {
@ -161,12 +296,11 @@ extension SocketEngineSpec {
func createBinaryDataForSend(using data: Data) -> Either<Data, String> { func createBinaryDataForSend(using data: Data) -> Either<Data, String> {
if websocket { if websocket {
var byteArray = [UInt8](repeating: 0x4, count: 1) var mutData = Data(bytes: [UInt8](repeating: 0x4, count: 1))
let mutData = NSMutableData(bytes: &byteArray, length: 1)
mutData.append(data) mutData.append(data)
return .left(mutData as Data) return .left(mutData)
} else { } else {
return .right("b4" + data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0))) return .right("b4" + data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0)))
} }

View File

@ -24,10 +24,22 @@
// //
import Foundation import Foundation
#if !os(Linux)
import StarscreamSocketIO import StarscreamSocketIO
#else
import WebSockets
import Sockets
import TLS
#endif
/// Protocol that is used to implement socket.io WebSocket support /// Protocol that is used to implement socket.io WebSocket support
public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate { public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate {
/// Called when a successful WebSocket connection is made.
func handleWSConnect()
/// Called when the WebSocket disconnects.
func handleWSDisconnect(error: NSError?)
/// Sends an engine.io message through the WebSocket transport. /// Sends an engine.io message through the WebSocket transport.
/// ///
/// You shouldn't call this directly, instead call the `write` method on `SocketEngine`. /// You shouldn't call this directly, instead call the `write` method on `SocketEngine`.
@ -40,6 +52,89 @@ public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate {
// WebSocket methods // WebSocket methods
extension SocketEngineWebsocket { extension SocketEngineWebsocket {
#if os(Linux)
func attachWebSocketHandlers() {
ws?.onText = {[weak self] ws, text in
guard let this = self else { return }
this.parseEngineMessage(text)
}
ws?.onBinary = {[weak self] ws, bin in
guard let this = self else { return }
this.parseEngineData(Data(bytes: bin))
}
ws?.onClose = {[weak self] _, _, reason, clean in
guard let this = self else { return }
this.handleWSDisconnect(error: nil)
}
}
#endif
func createWebSocketAndConnect() {
#if !os(Linux)
ws?.delegate = nil // TODO this seems a bit defensive, is this really needed?
ws = WebSocket(url: urlWebSocketWithSid)
if cookies != nil {
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
for (key, value) in headers {
ws?.headers[key] = value
}
}
if extraHeaders != nil {
for (headerName, value) in extraHeaders! {
ws?.headers[headerName] = value
}
}
ws?.callbackQueue = engineQueue
ws?.enableCompression = compress
ws?.delegate = self
ws?.disableSSLCertValidation = selfSigned
ws?.security = security
ws?.connect()
#else
let url = urlWebSocketWithSid
do {
let socket = try TCPInternetSocket(scheme: url.scheme ?? "http",
hostname: url.host ?? "localhost",
port: Port(url.port ?? 80))
if secure {
let stream = try TLS.InternetSocket(socket, TLS.Context(.client))
try WebSocket.background(to: urlWebSocketWithSid.absoluteString, using: stream) {[weak self] ws in
guard let this = self else { return }
this.onConnect(ws: ws)
}
} else {
try WebSocket.background(to: urlWebSocketWithSid.absoluteString, using: socket) {[weak self] ws in
guard let this = self else { return }
this.onConnect(ws: ws)
}
}
} catch {
DefaultSocketLogger.Logger.error("Error connecting socket", type: "SocketEngineWebsocket")
}
#endif
}
#if os(Linux)
func onConnect(ws: WebSocket) {
self.ws = ws
attachWebSocketHandlers()
handleWSConnect()
}
#endif
func probeWebSocket() { func probeWebSocket() {
if ws?.isConnected ?? false { if ws?.isConnected ?? false {
sendWebSocketMessage("probe", withType: .ping, withData: []) sendWebSocketMessage("probe", withType: .ping, withData: [])
@ -65,6 +160,7 @@ extension SocketEngineWebsocket {
} }
} }
#if !os(Linux)
// MARK: Starscream delegate methods // MARK: Starscream delegate methods
/// Delegate method for when a message is received. /// Delegate method for when a message is received.
@ -76,4 +172,45 @@ extension SocketEngineWebsocket {
public func websocketDidReceiveData(socket: WebSocket, data: Data) { public func websocketDidReceiveData(socket: WebSocket, data: Data) {
parseEngineData(data) parseEngineData(data)
} }
#endif
} }
#if os(Linux)
/// Does nothing on Linux.
public protocol WebSocketDelegate { }
/// SSLSecurity does nothing on Linux.
public final class SSLSecurity { }
extension WebSocket {
var isConnected: Bool {
return state == .open
}
func disconnect() {
do {
try close()
} catch {
DefaultSocketLogger.Logger.error("Error closing ws", type: "SocketEngineWebsocket")
}
}
func write(string: String) {
do {
try send(string)
} catch {
DefaultSocketLogger.Logger.error("Error sending string", type: "SocketEngineWebsocket", args: string)
}
}
func write(data: Data) {
do {
try data.withUnsafeBytes {(bytes: UnsafePointer<UInt8>) in
try self.send(Array(UnsafeBufferPointer(start: bytes, count: data.count)))
}
} catch {
DefaultSocketLogger.Logger.error("Error sending data", type: "SocketEngineWebsocket", args: data)
}
}
}
#endif

View File

@ -22,8 +22,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
import Dispatch
import Foundation import Foundation
#if !os(Linux)
import StarscreamSocketIO import StarscreamSocketIO
#endif
enum JSONError : Error { enum JSONError : Error {
case notArray case notArray
@ -111,9 +114,9 @@ extension String {
return array return array
} }
func toNSDictionary() throws -> NSDictionary { func toDictionary() throws -> [String: Any] {
guard let binData = data(using: .utf16, allowLossyConversion: false) else { return [:] } guard let binData = data(using: .utf16, allowLossyConversion: false) else { return [:] }
guard let json = try JSONSerialization.jsonObject(with: binData, options: .allowFragments) as? NSDictionary else { guard let json = try JSONSerialization.jsonObject(with: binData, options: .allowFragments) as? [String: Any] else {
throw JSONError.notNSDictionary throw JSONError.notNSDictionary
} }

View File

@ -62,10 +62,12 @@ public extension SocketLogger {
private func abstractLog(_ logType: String, message: String, type: String, args: [Any]) { private func abstractLog(_ logType: String, message: String, type: String, args: [Any]) {
guard log else { return } guard log else { return }
#if !os(Linux)
let newArgs = args.map({arg -> CVarArg in String(describing: arg)}) let newArgs = args.map({arg -> CVarArg in String(describing: arg)})
let messageFormat = String(format: message, arguments: newArgs) let messageFormat = String(format: message, arguments: newArgs)
NSLog("\(logType) \(type): %@", messageFormat) NSLog("\(logType) \(type): %@", messageFormat)
#endif
} }
} }