I use NSStreamDelegate protocol in A UIViewController subclass,
And then send setDelegate message to a NSInputStream.
var input : NSInputStream?
var output: NSOutputStream?
func connectToSocket(host: String, port: Int) {
NSStream.getStreamsToHostWithName(host, port: port, inputStream: &(self.input), outputStream: &(self.output)
let str = "test"
let data = str.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
self.input?.setDelegate(self)
self.input?.open()
self.output?.open()
// ...
}
I got a 'NSInputStream' does not have a member named 'setDelegate' error message
Why can I use 'setDelegate'` like below document?
https://developer.apple.com/library/prerelease/iOS/documentation/Cocoa/Reference/Foundation/Classes/NSStream_Class/index.html
This should work:
self.input?.delegate = self
Looks like the documentation isn't quite up to date.
Related
I am trying to make a simple echo UDP server that sends back all incoming datagrams prefixed with a UTF8 string.
In my attempts to reach this goal, I succeeded in sending back the incoming data, but when I try to prefix this data with the string: "You sent: ", I get an error writeDataUnsupported
This is my code:
I made a ChannelInboundHandler called Echo all it does is: For each incoming datagram, it sends the string "You sent: " and then the data of the incoming datagram.
final class Echo: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
typealias OutboundOut = ByteBuffer
var wroteResponse = false
static let response = "You sent: ".data(using: .utf8)!
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
if !wroteResponse {
var buffer = ctx.channel.allocator.buffer(capacity: Echo.response.count)
buffer.write(bytes: Echo.response)
ctx.write(self.wrapOutboundOut(buffer), promise: nil)
wroteResponse = true
}
ctx.write(data, promise: nil)
}
func channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush()
wroteResponse = false
}
}
Then I made a single threaded event loop group and assigned a datagram bootsrap to it. Then I bound the bootstrap to port 4065.
let π = MultiThreadedEventLoopGroup(numThreads: 1)
let bootstrap = DatagramBootstrap(group: π)
.channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.channelInitializer { $0.pipeline.add(handler: Echo()) }
defer {
try! π.syncShutdownGracefully()
}
try bootstrap
.bind(host: "127.0.0.1", port: 4065)
.wait()
.closeFuture
.wait()
Why do I always get this writeDataUnsupported while trying to send the string: "You sent: "?
For DatagramChannel you need to wrap your ByteBuffer into an AddressEnvelope. Which also means your ChannelInboundHandler should operate on AddressedEnvelope<ByteBuffer>.
To make the ChannelInboundHandler operate on AddressedEnvelope<ByteBuffer>, as Norman Maurer suggests, you can rewrite Echo so it looks more like:
final class Echo: ChannelInboundHandler {
typealias InboundIn = AddressedEnvelope<ByteBuffer>
typealias OutboundOut = AddressedEnvelope<ByteBuffer>
static let response = "You sent: ".data(using: .utf8)!
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
var incomingEnvelope = unwrapInboundIn(data)
var buffer = ctx.channel.allocator.buffer(capacity: Echo.response.count + incomingEnvelope.data.readableBytes)
buffer.write(bytes: Echo.response)
buffer.write(buffer: &incomingEnvelope.data)
let envelope = AddressedEnvelope(remoteAddress: incomingEnvelope.remoteAddress, data: buffer)
ctx.write(wrapOutboundOut(envelope), promise: nil)
}
func channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush()
}
}
we're using Locksmith to save user data for Keychain. In our end everything works as it should but for some reason we receive crashes with the error Locksmith.LocksmithError.interactionNotAllowed.
Follows the code where the crash happen:
func updateUserAccessToken(forAccount account: String, token: String) {
var userAccessToken = Locksmith.loadDataForUserAccount(userAccount: account) ?? [String: Any]()
userAccessToken[βtokenβ] = token
try! Locksmith.updateData(data: userAccessToken, forUserAccount: account)
}
Why is the code above crashes for other users? 'til . now we can't replicate the said crash. Any help is very much appreciated. Thanks!
UPDATE:
So we finally able to replicate this crash, and it's because we're accessing the keychain while the device is locked. I found out you can change the Keychain's "accessibility option" but I'm not sure how to do it in Locksmith. Anybody?
I found out that if you're using a protocol base approach changing the accessibility option is much more easier. But unfortunately our app doesn't use it. So what I did was I created an extension as follow:
extension Locksmith {
fileprivate static func loadDataForUserAccount(userAccount: String,
inService service: String = LocksmithDefaultService,
accessibleOption: LocksmithAccessibleOption) -> [String: Any]? {
struct ReadRequest: GenericPasswordSecureStorable, ReadableSecureStorable {
let service: String
let account: String
var accessible: LocksmithAccessibleOption?
}
let request = ReadRequest(service: service, account: userAccount, accessible: accessibleOption)
return request.readFromSecureStore()?.data
}
fileprivate static func updateData(data: [String: Any],
forUserAccount userAccount: String,
inService service: String = LocksmithDefaultService,
accessibleOption: LocksmithAccessibleOption) throws {
struct UpdateRequest: GenericPasswordSecureStorable, CreateableSecureStorable {
let service: String
let account: String
let data: [String: Any]
var accessible: LocksmithAccessibleOption?
}
let request = UpdateRequest(service: service, account: userAccount, data: data, accessible: accessibleOption)
return try request.updateInSecureStore()
}
}
NOTE: changing the "accessibility option" may loss your access to the data previously saved with the default "accessibility option". If you need those data you may need to handle it separately.
I can't seem a to find a way to create http requests using proxies, let say i have a Sock5 proxy or HTTP Proxy how would i go about creating a GET/POST request via a proxy, without having to apply the proxy to the system.
I only want the single request to go through a proxy.
Let's say i create a http request using Alamofire, then the request would go through my ip address, but what if i want to apply a http proxy or socks5 proxy to send the request through.
Let's say I make a request like that:
Alamofire.request("https://httpbin.org/get").responseJSON { response in
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
How would i apply the http or socks proxy to this request?
Can't seem to find anything about it.
Taken from this thread:
You create proxy details:
struct ProxyItem: Equatable, Hashable {
let host: String
let port: String
let HTTPOnly = true
var hashValue: Int {
return host.hashValue ^ port.hashValue
}
}
then create proxy configuration
var proxyConfiguration = [NSObject: AnyObject]()
proxyConfiguration[kCFNetworkProxiesHTTPProxy] = item.host
proxyConfiguration[kCFNetworkProxiesHTTPPort] = port
proxyConfiguration[kCFNetworkProxiesHTTPEnable] = 1
set the Alamo configuration:
let sessionConfiguration = AFManager.sharedInstance.session.configuration
sessionConfiguration.connectionProxyDictionary = proxyConfiguration
Create an alamo manager from this configuration:
manager = Alamofire.Manager(configuration: sessionConfiguration)
Finally use the manager to connect to your proxy:
manager.request(.GET, urlString)
.response {
(request, response, data, error) in
if let response = response {
var statusCode = response.statusCode
println("-->statusCode: \(statusCode)")
}
if (error == nil) {
var serializationError: NSError?
let jsonData: AnyObject? = NSJSONSerialization.JSONObjectWithData(data! as! NSData, options: NSJSONReadingOptions.AllowFragments, error: &serializationError)
var parser: Parser = Parser()
let menu: Menu = parser.parseMenuJSON(jsonData)
var dataAccess: DataAccess = DataAccess.sharedInstance
dataAccess.addMenu(menu)
} else {
println("Webservice error: \(error)")
}
}
I'm currently able to send text to a printer (like EPSON TM20-II) using Swift. The printer receives the data and prints it without any problems. (For that, I'm encoding my string in UTF-8)
My custom class for the socket :
class OutSocket {
var addr: String
var port: Int
var task: NSURLSessionStreamTask!
init(host: String, port: Int){
print("init")
self.addr = host
self.port = port
setupConnection()
}
func setupConnection() {
print("setup")
let session = NSURLSession(configuration: .defaultSessionConfiguration())
task = session.streamTaskWithHostName(addr, port: port)
self.task.resume()
}
func send(data: NSData) {
self.task.writeData(data, timeout: 5.0) { (error) in
if error == nil {
print("Data sent")
} else {
print("Nope")
}
}
}
}
And I'm using it like this :
let socket = OutSocket(host: "192.168.1.42", port: 9100)
socket.send((itemText + "\n").dataUsingEncoding(NSUTF8StringEncoding)!)
I can send an image to the printer, but it only prints me the data characters.
How can I print an image, and not the data characters ?
I saw the Epson SDK but I want it to works with any printer that support TCP/IP printing. I tried to encode my image in base64 but still not print the image.
here the code :
let socket = OutSocket(host: "192.168.1.42", port: 9100)
let image = UIImage(named: "name-logo")
let imgData = UIImagePNGRepresentation(image!)
let imgEncoded = imgData!.base64EncodedDataWithOptions(.Encoding64CharacterLineLength)
socket.send(imgEncoded)
Thanks in advance,
Currently I am working on a program in which i am connecting two IOS devices using NSNetService class. I am able to connect both the devices but i don't know how to send data using getInoutStream function. Can any buddy help me.
Why not use multipeer? It's a much simpler solution to what you're trying to do. Also NSNetService has a few known bugs. However, you send data between the two by setting up a Bonjour connection on NSNetService. The getInputStream method requires arguments of the type UnsafeMutablePointer:
public func getInputStream(inputStream: UnsafeMutablePointer<NSInputStream?>, outputStream: UnsafeMutablePointer<NSOutputStream?>) -> Bool
var inputStream : NSInputStream?
var outputStream : NSOutputStream?
let success = service.getInputStream(&inputStream, outputStream: &outputStream)
Then just write data using a memory stream and it'll get passed to the listener port.
This is the finished code:
This function initiates the connection on 127.0.0.1
func initNetworkCommunication(){
var host : CFString = "127.0.0.1"
var port : UInt32 = 7001
var readstream : Unmanaged<CFReadStream>?
var writestream : Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readstream, &writestream)
inputstream = readstream!.takeRetainedValue()
outputstream = writestream!.takeRetainedValue()
inputstream.delegate = self
outputstream.delegate = self
inputstream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
outputstream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
inputstream.open()
And this is for the stream IO:
func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent) { //This is the stream IO function. It allows RW of the stream
switch (eventCode){
case NSStreamEvent.ErrorOccurred:
NSLog("ErrorOccurred")
break
case NSStreamEvent.EndEncountered:
NSLog("EndEncountered")
break
case NSStreamEvent.None:
NSLog("None")
break
case NSStreamEvent.HasBytesAvailable:
NSLog("HasBytesAvaible")
var buffer = [UInt8](count: 4096, repeatedValue: 0)
if ( aStream == inputstream){
while (inputstream.hasBytesAvailable){
var len = inputstream.read(&buffer, maxLength: buffer.count)
if(len > 0){
var output = NSString(bytes: &buffer, length: buffer.count, encoding: NSUTF8StringEncoding)
if (output != ""){
NSLog("server said: %#", output!)
}
}
}
}
break
case NSStreamEvent.allZeros:
NSLog("allZeros")
break
case NSStreamEvent.OpenCompleted:
NSLog("OpenCompleted")
break
case NSStreamEvent.HasSpaceAvailable:
NSLog("HasSpaceAvailable")
break
default:
// default code here
break
}