Monday, September 10, 2012

Network Connection on IOS 5.1+


It started with a project, which needs my app connect with another device via WIFI without using http protocol. It's not like the REST model for the most of the case, but here I need to connect to the device kind of using socket programming like staff.

After searching around, I really didn't find too much resource could make me understand network connection on IOS better. So, I just uses a workable solution for my problem.

After then, I made it as a [.mh] file, so people could reuse it in other IOS apps.

Here is the code of .h file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//
//  NetworkManager.h
//
//  Created by antonio081014 on 7/12/12.
//  Copyright (c) 2012 Antonio081014. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Defines.h"

@protocol NetworkManagerDelegate <NSObject>

@optional
//Send back the data received from the server;
- (void)receivedData:(uint8_t *)data WithLength:(int)length;
@end

@interface NetworkManager : NSObject

@property (nonatomic) BOOL debug;
@property (nonatomic) id <NetworkManagerDelegate> delegate;

//Start the Connection with specified address and port;
- (void) startNetworkCommunicationWithAddress:(NSString *)address withPort:(NSNumber *)port;
//Disconnect the current connection;
- (void)disconnect;
//Send Command to the server;
- (void)sendCMDWithArray:(unsigned char[])cmd;

@end

Here is the code of .m, implementation file:
The AlertView is used to notify user about the status of network connection purpose.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//
//  NetworkManager.m
//
//  Created by antonio081014 on 7/12/12.
//  Copyright (c) 2012 Antonio081014. All rights reserved.
//

#import "NetworkManager.h"

@interface NetworkManager() <NSStreamDelegate, UIAlertViewDelegate>

@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, strong) NSOutputStream *outputStream;

@end

@implementation NetworkManager

@synthesize inputStream = _inputStream;
@synthesize outputStream = _outputStream;

- (void) startNetworkCommunicationWithAddress:(NSString *)address
                                     withPort:(NSNumber *)port
{ 
 CFReadStreamRef readStream = NULL;
 CFWriteStreamRef writeStream = NULL;
 CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)address, [port integerValue], &readStream, &writeStream);
 
 self.inputStream = (__bridge NSInputStream *)readStream;
 self.outputStream = (__bridge NSOutputStream *)writeStream;
    self.inputStream.delegate = self; //delegate;
    self.outputStream.delegate = self; //delegate;
 [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
 [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
 [self.inputStream open];
 [self.outputStream open];
}

- (void)disconnect
{
    if (self.inputStream) {
        [self.inputStream close];
        [self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        self.inputStream = nil;
    }
    if (self.outputStream) {
        [self.outputStream close];
        [self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        self.outputStream = nil;
    }
}

- (void)sendCMDWithArray:(unsigned char[])cmd
{
    int size = CMD_LENGTH;
    NSData *data = [[NSData alloc] initWithBytes:(const void *)cmd length:sizeof(unsigned char) * size];
 [self.outputStream write:[data bytes] maxLength:[data length]];
}

- (void)stream:(NSStream *)theStream
   handleEvent:(NSStreamEvent)streamEvent
{
 
 switch (streamEvent) {
  case NSStreamEventOpenCompleted:
            if (self.debug) {
                NSLog(@"Stream opened");
            }
   break;
            
  case NSStreamEventHasBytesAvailable:
   if (theStream == self.inputStream) {
    uint8_t buffer[BUFFER_SIZE];
    int len;
                
    while ([self.inputStream hasBytesAvailable]) {
     len = [self.inputStream read:buffer maxLength:sizeof(buffer)];
     if (len > 0) {
                        [self.delegate receivedData:buffer WithLength:len];
     }
    }
   }
   break;
            
  case NSStreamEventErrorOccurred:
            if (self.debug) {
                NSLog(ERROR_MSG);
            }
            
            [self popupMessageWithTitle:ERROR_TITLE
                             andMessage:ERROR_MSG
                             confirmMSG:OK
                                 cancel:nil];
   break;
            
        case NSStreamEventHasSpaceAvailable:
//            NSLog(@"NSStreamEventHasSpaceAvailable");
            break;
        
//        case NSStreamEventEndEncountered:
//            [theStream close];
//            [theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//            theStream = nil;
//   break;
            
  default:
   NSLog(@"None event");
            break;
 }
}

- (void)popupMessageWithTitle:(NSString *)title
                   andMessage:(NSString *)msg
                   confirmMSG:(NSString *)confirm
{
    
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
                                                    message:msg
                                                   delegate:self
                                          cancelButtonTitle:confirm
                                          otherButtonTitles:nil];
    
    [alert show];
}

- (void)popupMessageWithTitle:(NSString *)title
                   andMessage:(NSString *)msg
                   confirmMSG:(NSString *)confirm
                       cancel:(NSString *)cancel
{
    if (!cancel) {
        [self popupMessageWithTitle:title
                         andMessage:msg
                         confirmMSG:confirm];
        return;
    }
    
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
                                                    message:msg
                                                   delegate:self
                                          cancelButtonTitle:cancel
                                          otherButtonTitles:confirm, nil];
    
    [alert show];
}
@end

Here is how to use it.

1
2
3
4
5
[self.networkManager startNetworkCommunicationWithAddress:address withPort:port];

[self.networkManager setDelegate:self];

[self.networkManager disconnect];

Once you started the network communication, the connection will be kept through your views, even you push and back around, until you shut it down by using the disconnect() function. BUT, if you hope to use it in multiple views, don't forget to setDelegate:self in each viewcontroller's viewwillAppear function.