I know there are tools available to send push notification that are implemented with php or C++ or something else. I am trying develop my own using NSStream. I have tried the following code.
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 80, &readStream, &writeStream);
NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
Delegate method:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
NSLog(#"stream:handleEvent: is invoked...");
switch(eventCode) {
case NSStreamEventOpenCompleted:
// it is getting here
break;
case NSStreamEventHasSpaceAvailable:
{
if (stream == oStream) {
NSString * str = [NSString stringWithFormat:
#"{"aps":{"alert":"Hello from APN server.","badge":"1"}}"];
// how do i add device token
const uint8_t * payload =
(const uint8_t *)[str UTF8String];
[oStream write:payload maxLength:strlen(payload)];
[oStream close];
}
break;
}
}
}
Now the problems are:
How do I open the stream with a push certificate
How do I generate the payload with a device token
Related
How do I handle errors in a stream? If the user is connected to the wrong network I want to handle that. Thanks!
Code:
- (void)initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"IP HERE", 7777, &readStream, &writeStream);
_inputStream = (NSInputStream *)CFBridgingRelease(readStream);
_outputStream = (NSOutputStream *)CFBridgingRelease(writeStream);
[_inputStream setDelegate:self];
[_outputStream setDelegate:self];
[_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[_inputStream open];
[_outputStream open];
}
I figured out a solution that worked in my case. This code will print out the current network's BSSID in the console, and I simply check if the BSSID matches the one for my preferred network with an if-statement:
#import <SystemConfiguration/CaptiveNetwork.h>
//Checks which network the user is connected to.
CFArrayRef myArray = CNCopySupportedInterfaces();
CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
NSLog(#"Connected at: %#", myDict);
NSDictionary *myDictionary = (__bridge_transfer NSDictionary*)myDict;
NSString * BSSID = [myDictionary objectForKey:#"BSSID"];
NSLog(#"BSSID is: %#", BSSID);
//Handling wrong/correct BSSID.
if (![BSSID isEqualToString:#"PREFERRED BSSID HERE"]) {
//Handle error however you want.
}
else {
//If correct BSSID, handle that here however you want.
}
}
I have two Applaction one is the client (IOS device) and the other one is the server (PC device).
I would like to get the ip address in the client (IOS device ) Automatically.
I'm using this line of code to type the IP address
NSString *ipAddressText = #"192.168.211.62";
I don't want to keep typing the Ip address, becuase the IP address will change most of the time.
here's my code
-(void)viewDidLoad{
[super viewDidLoad];
NSString *ipAddressText = #"192.148.211.42";
NSLog(#"Setting up connection to %# : %i", ipAddressText, 111);
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef) ipAddressText, 111, &readStream, &writeStream);
messages = [[NSMutableArray alloc] init];
[self open];
}
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %lu \n ", streamEvent);
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
_connectedLabel.text = #"Connected";
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream)
{
uint8_t buffer[1024];
NSInteger len;
while ([inputStream hasBytesAvailable])
{
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0)
{
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output)
{
NSLog(#"server said: %# \n ", output);
[self messageReceived:output];
}
}
}
}
break;
case NSStreamEventHasSpaceAvailable:
NSLog(#"Stream has space available now");
break;
case NSStreamEventErrorOccurred:
NSLog(#"%#\n",[theStream streamError].localizedDescription);
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
_connectedLabel.text = #"Disconnected";
NSLog(#"close stream");
break;
default:
NSLog(#"Unknown event");
}
}
- (void)open {
NSLog(#"Opening streams.");
outputStream = (__bridge NSOutputStream *)writeStream;
inputStream = (__bridge NSInputStream *)readStream;
[outputStream setDelegate:self];
[inputStream setDelegate:self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream open];
[inputStream open];
_connectedLabel.text = #"Connected";
}
- (void)close {
NSLog(#"Closing streams.");
[inputStream close];
[outputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream setDelegate:nil];
[outputStream setDelegate:nil];
inputStream = nil;
outputStream = nil;
_connectedLabel.text = #"Disconnected";
}
This is in Swift, but can easily be dropped into any Xcode project.
NB: This example is taken from a GitHub project Host.swift.
Getting information about network interfaces in Unix type systems like iOS or Mac OS X requires using some arcane C APIs. These APIs have varying amounts of visibility in swift and objective-c, ios and mac-os-x.
If you're exclusively targeting macOS with Objective-C, then nshost is your best solution. However, if you require iOS compatibility then you'll have to drop down to lower level C API such as getifaddrs(...) or cfhost.
If you want a swift based solution even getifaddrs(...) comes with some bridging header requirements (making it unusable in a Framework).
Here is an example using CFHost and sockaddr_in struct that will work in Swift and on macOS and iOS (even in Frameworks). See Host.swift on GitHib for a fully working example.
Use CFHost to get the addressing info, this will be a CFArray of CFData objets.
let sockaddrs = CFHostGetAddressing("apple.com", &resolved)?.takeRetainedValue()
Get the first object
let data = CFArrayGetValueAtIndex(sockaddrs, 0)
Cast the bytes into a sockaddr struct
var storage = sockaddr_storage()
data.getBytes(&storage, length: sizeof(sockaddr_storage))
Then force the sockaddr struct into a sockaddr_in struct so we can use it
let addr = withUnsafePointer(&storage) { UnsafePointer<sockaddr_in>($0).memory
Use the inet_ntoa(...) function to turn addr.sin_addr (IP address) into a C-string. We can then use String(Cstring: encoding:) to get a nice String of the IP Address.
let address = String(CString: inet_ntoa(addr.sin_addr), encoding: NSUTF8StringEncoding)
Here is a GitHub project called Host.swift that I created to solve these issues using the technique above.
I'm developing an application for iOS which is sharing its screen with another device through constant flow of screenshot images sent through TCP sockets connection.
Here's the code I'm written so far:
#implementation newClient {
NSData *data;
NSInputStream *iStream;
NSOutputStream *oStream;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
BOOL boolean;
}
-(void) connectToServerUsingCFStream:(UIImage *) bitmap {
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(CFStringRef) #"192.168.1.9",
8080,
&readStream,
&writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
iStream = (NSInputStream *)CFBridgingRelease(readStream);
[iStream setDelegate:self];
//[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
// forMode:NSDefaultRunLoopMode];
[iStream open];
oStream = (NSOutputStream *)CFBridgingRelease(writeStream);
[oStream setDelegate:self];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream open];
// data = UIImageJPEGRepresentation(bitmap, 50);
boolean = YES;
[self writeToServer:bitmap];
}
}
- (void) writeToServer:(UIImage *)bitmap {
dispatch_async(dispatch_get_main_queue(), ^{
#autoreleasepool {
NSData *data = UIImageJPEGRepresentation(bitmap, 50);
int len;
uint32_t length = (uint32_t)htonl([data length]);
// Sending out the length of the screenshot image and then the image
len=[oStream write:(const uint8_t *)&length maxLength:4];
NSLog(#"len=%d",len);
len=[oStream write:(const uint8_t *)[data bytes] maxLength:[data length]];
NSLog(#"len=%d",len);
//[oStream write:(const uint8_t *)"/n" maxLength:0];
}
});
}
#end
When I click the button which is activating this object in sends the first screenshot and then crashes giving me "Thread 1: EXC_BAD_ACCESS(code=1, address=0xd000000c) in the main.m file of my project.
The server application is running on Android and it's a thread which is constantly running in a loop.
I don't know what is causing the problem.
I'm trying to write telnet client on iOS and I can't set up streams correctly. I think that they work, but I am getting empty buffer each time I get delegate method triggered.
Here is how I set up streams:
self.manager = [[TelnetManager alloc] init];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)[[NSURL URLWithString:#"arda.pp.ru"] host], 7000, &readStream, &writeStream);
NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setDelegate:self.manager];
[outputStream setDelegate:self.manager];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
Here is the delegate method that is on manager that handles events:
-(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
if (eventCode == NSStreamEventHasBytesAvailable) {
NSLog(#"We got some data over here");
if(!_data) {
_data = [NSMutableData data];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)aStream read:buf maxLength:1024];
NSLog(#"length - %d", len);
NSLog(#"buf:%s", buf);
if(len) {
[_data appendBytes:(const void *)buf length:len];
_bytesRead = #([_bytesRead intValue]+len);
} else {
NSLog(#"no buffer!");
}
}
}
It is getting fired, but buffer is always empty and "no buffer" is logged.
What's wrong with my code?
There actually is nothing wrong with your code. I have used it to connect to a telnet server and it worked fine.
In your code, "no buffer" is logged when len is 0, which means the buffer's end has been reached. In the case of a telnet server, this usually means that either the server disconnected your connection or the server quit.
I am using a device called a WiFly that has ip address:169.254.1.1 and port 2000. I am trying to connect to this device via an iOS application. I use the following code to connect:
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
UInt32 port = 2000;
CFStringRef host = CFSTR("169.254.1.1");
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
// set the delegates to this view controller
[inputStream setDelegate:self];
[outputStream setDelegate:self];
// Set run loops to continuous receive information
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// Finally, open the connection
[inputStream open];
[outputStream open];
Then I use the following to handle stream events:
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent);
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(#"server said: %#", output);
[self messageReceived:output];
}
}
}
}
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can't connect to server");
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
break;
default:
NSLog(#"Unknown event");
}
So, I can see that the first two streams are opened correctly. Then it follows immediately with a stream event 4, which from my understanding is to be expected. However, I then try to call a function:
- (IBAction)moveForward
{
NSLog(#"move forward called");
NSString *response = [NSString stringWithFormat:#"2"];
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
Which should return "forward" from an arduino uno via the wifly. However, when I click, I get another NSStreamEvent 4 for some reason. I also telnetted in to the device via the terminal with:
telnet 169.254.1.1 2000
and subsequently type a "2"... This returned the desired "forward" immediately. What am I doing wrong from the perspective of the iPad?
Help is much appreciated!
EDIT For some reason, everytime the handler is accessed, the stream that is passed is the output stream, which is completely wrong... Why wouldn't the input stream ever be passed through?
And here is the arduino code:
void loop()
{ while (Serial.available() > 0) {
int next = Serial.parseInt();if(Serial.available() > 0)
{
if(next == 20) {
Serial.println("leftMove");
}}}}