Pages

Saturday, July 18, 2020

A simple Example of SignalProducer ReactiveSwift

Recently I started to work on a project which is totally in Swift and Signal. As I mostly worked on Objective-C and C++ based projects, from Objective C background I really don’t like Swift that much, and honestly this ReactiveSwift, I hope it will change after some time. And for me, SignalProducer or Signals is very complex. When I searched for tutorials with real-life examples, what I found is that, whether they are too old or does not meet my requirements, also I didn’t get much time to study these things. 

If you want to learn theory or want to clear your concept about ReactiveSwift or Signals this article is not for you. Here I will share the examples I came up with to help me to implement the same scenario in my project. 

If you are already experienced in these please feel free to share your comments so that I and other people get to learn something from you :)


Let’s get to point

Let's say we have a Downloader class which only perform the download, and which have no idea about ReactiveSwift, we want to integrate this class with our app using ReactiveSwift, also we have DownloaderDelegate. Let's check them first.

protocol DownloaderDelegate {
func downloadFinished()
func downloadCanceled()
}
class Downloader {
var progress: Int
var shouldStop: Bool
var delegate: DownloaderDelegate?
init(delegate: DownloaderDelegate) {
progress = 0
shouldStop = false
self.delegate = delegate
}
func startDownloading() {
DispatchQueue.global().async {
//lets say we are donwloading here,
for index in 0..<10 {
sleep(1)
log("[Downloader] Progress \(index*10)%")
if self.shouldStop {
self.delegate?.downloadCanceled()
return
}
}
self.delegate?.downloadFinished()
}
}
func stopDownloading() {
shouldStop = true
}
}

Now Let's say we are going to create a DownloadManager class which will use this class and implement SignalProducer so that our application can access this just like other classes using ReactiveSwift.

class DownloadManager {
var observer: Signal<Int, Error>.Observer? = nil
var lifetime: Lifetime? = nil
var downloader: Downloader?
func start() -> SignalProducer<Int, Swift.Error> {
let signalProducer: SignalProducer<Int, Swift.Error> = SignalProducer { (observer, lifetime) in
//this block won't execute untill someone start this signal
log("[DownloadManager] Download Starting")
self.downloader = Downloader(delegate: self)
self.downloader?.startDownloading()
self.observer = observer
self.lifetime = lifetime
}
return signalProducer
}
func stop() {
log("[DownloadManager] IN")
self.downloader?.stopDownloading()
log("[DownloadManager] OUT")
}
}


Here is the Tester class where we will use DownloadManager class

class DownloadTester {
func testDownloader() {
let manager = DownloadManager()

manager.start().startWithResult { (result) in
switch result {
case .success(let progress) :
print(progress)
print("[DownloadTester] Download Complete: \(progress)")
case .failure(let err):
print(err)
}
}
//to test stop downloading
DispatchQueue.global().async {
sleep(3)
manager.stop()
}
}
}


If you want to see the whole class please check here

I wanted to use github gist but no way I could embedded it here :( 

If you know how to do that please share.

https://github.com/razibdeb/ReactiveSwiftSamples/blob/master/DownloaderSample.swift

Thursday, July 16, 2020

Simple BSD Socket Client Server Example for macOS iOS

Today I am going to share about a simple Client Server example I have been working on to clear my concepts of BSD Socket. You can work on high leven APIs like CFSocket, But sometimes you need the help of a pure socket. 

I tested this code in macOS

RCDBSDServer.h

//

//  RCDServer.h

//  HelloMacObj

//

//  Created by Razib Chandra Deb on 15/7/20.

//  Copyright © 2020 Razib Chandra Deb. All rights reserved.

//


#import <Foundation/Foundation.h>


NS_ASSUME_NONNULL_BEGIN


@interface RCDBSDServer : NSObject

-(BOOL) startOnPort:(int) port;

-(void) stop;

-(void) sendString: (NSString *) string;

-(void) readData:(int) length;

@end


NS_ASSUME_NONNULL_END



RCDBSDServer.mm


//

//  RCDServer.m

//  HelloMacObj

//

//  Created by Razib Chandra Deb on 15/7/20.

//  Copyright © 2020 Razib Chandra Deb. All rights reserved.

//


#import "RCDBSDServer.h"

#include <netdb.h>

#include <netinet/in.h>

#include <sys/ioctl.h>

#include <sys/un.h>

#include <err.h>

#include <fcntl.h>

#include <arpa/inet.h>

#import <objc/runtime.h>

/*

 

 https://www.tutorialspoint.com/unix_sockets/socket_server_example.htm

Create a socket with the socket() system call.


Bind the socket to an address using the bind() system call. For a server socket on the Internet, an address consists of a port number on the host machine.


Listen for connections with the listen() system call.


Accept a connection with the accept() system call. This call typically blocks until a client connects with the server.


Send and receive data using the read() and write() system calls.

*/

@implementation RCDBSDServer {

    int sockfd, portno, n, newSockFd;

    struct sockaddr serv_addr;

    struct hostent *server;

    

    struct sockaddr_in client_addr;

    socklen_t client_addr_len;

}



-(id) init {

    self = [super init];

    if ( self != nil) {

        

    }

    return self;

}


-(BOOL) startOnPort:(int) port {

    // Connect socket

    struct sockaddr_in addr;

    bzero((char *)&addr, sizeof(addr));

    

    addr.sin_family = AF_INET;

    addr.sin_port = htons(port);

    //addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    //addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

    

    socklen_t socklen = sizeof(addr);

    

    

    /* First call to socket() function */

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    

    if (sockfd < 0) {

        Log(@"[Server] socket creation failed");

        return NO;

    }

    

//    int on = 1;

//    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {

//        close(sockfd);

//        Log(@"Error on setsockopt");

//        return false;

//    }

//

//    if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {

//        close(sockfd);

//        Log(@"Error on fcntl");

//        return false;

//    }


    if (bind(sockfd, (struct sockaddr*)&addr, socklen) != 0) {

        close(sockfd);

        Log(@"Error on bind");

        return false;

    }

    

    /* Now start listening for the clients, here process will

       * go in sleep mode and will wait for the incoming connection

    */

    if (listen(sockfd, DATA_CHUNK) != 0) {

        close(sockfd);

        Log(@"Error on listen");

        return false;

    }

    else {

        Log(@"listen success");

    }

    

    client_addr_len = sizeof(client_addr);

    /* Accept actual connection from the client */

    newSockFd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addr_len);

     

    if (newSockFd < 0) {

       Log(@"ERROR on accept");

        return false;

    }

    

    /* If connection is established then start communicating */

  

    return true;

}

-(void) stop {

    close(sockfd);

}

-(void) sendString: (NSString *) string {

     const char * buffer = [string cStringUsingEncoding:NSUTF8StringEncoding];

     ssize_t n = write(newSockFd, buffer, (int) strlen(buffer));

     Log(@"[Server] Data sent %zd", n);

}

-(void) readData:(int) length {

    char data[DATA_CHUNK];

    

    memset(data, 0, DATA_CHUNK);

    ssize_t n = read(newSockFd, data, length);

    Log(@"[Server] Data read %zd Data \n%s\n", n, data);

}

@end




RCDBSDClient.h

//

//  RCDBSDClient.h

//  HelloMacObj

//

//  Created by Razib Chandra Deb on 15/7/20.

//  Copyright © 2020 Razib Chandra Deb. All rights reserved.

//


#import <Foundation/Foundation.h>


NS_ASSUME_NONNULL_BEGIN


@interface RCDBSDClient : NSObject

-(BOOL) connectToServerIp:(NSString *) ipAddres withPort:(int) port;

-(void) stop;

-(void) sendString: (NSString *) string;

-(void) readData:(int) length;

@end


NS_ASSUME_NONNULL_END



RCDBSDClient.mm

//

//  RCDBSDClient.m

//  HelloMacObj

//

//  Created by Razib Chandra Deb on 15/7/20.

//  Copyright © 2020 Razib Chandra Deb. All rights reserved.

//


#import "RCDBSDClient.h"

#include <netdb.h>

#include <netinet/in.h>

#include <sys/ioctl.h>

#include <sys/un.h>

#include <err.h>

#include <fcntl.h>

#include <arpa/inet.h>


/*

 https://www.tutorialspoint.com/unix_sockets/socket_client_example.htm

 Create a socket with the socket() system call.


 Connect the socket to the address of the server using the connect() system call.


 Send and receive data. There are a number of ways to do this, but the simplest way is to use the read() and write() system calls.

 */


@implementation RCDBSDClient {

    int sockfd, portno, n;

    struct sockaddr serv_addr;

    struct hostent *client;

}


-(id) init {

    self = [super init];

    if ( self != nil) {

        

    }

    return self;

}



-(BOOL) connectToServerIp:(NSString *) ipAddres withPort:(int) port {

    

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd == -1) {

        Log(@"Error on socket");

        return NO;

    }

    

    // prevent SIGPIPE

    int on = 1;

    setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));

    

    struct hostent *serverAddres = gethostbyname([ipAddres cStringUsingEncoding:NSUTF8StringEncoding]);

    

    // Connect socket

    struct sockaddr_in addr;


    

    bzero((char *) &addr, sizeof(addr));

    addr.sin_family = AF_INET;

    bcopy((char *)serverAddres->h_addr, (char *)&addr.sin_addr.s_addr, serverAddres->h_length);

    addr.sin_port = htons(port);

    

    socklen_t socklen = sizeof(addr);

    if (connect(sockfd, (struct sockaddr*)&addr, socklen) == -1) {

        Log(@"Error on connect");

        return NO;

    }

    

    Log(@"Connection Success");

    

    return true;

}

-(void) stop {

    close(sockfd);

}

-(void) sendString: (NSString *) string {

    const char * buffer = [string cStringUsingEncoding:NSUTF8StringEncoding];

    ssize_t n = write(sockfd, buffer, (int) strlen(buffer));

    Log(@"[Client] Data sent %zd", n);

}

-(void) readData:(int) length {

    char data[DATA_CHUNK];

    memset(data, 0, DATA_CHUNK);

    ssize_t n = read(sockfd, data, length);

    Log(@"[Client] Data read %zd Data \n%s\n", n, data);

}

@end




Please feel free to share your thoughts.