Announcement: Swift 5.7 is now compatible with SCADE IDE
Tags: android, scade, ios, swift
Published: 2023-03-23 09:29:42

---
title:  Announcement: Swift 5.7 is now compatible with SCADE IDE
published: true
description: Swift 5.7 is now supported by SCADE
tags: android,scade,ios,swift 
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nf292z1ths6psn9fy3lk.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-03-17 13:19 +0000
---





## Announcement📢

Hi everyone 🙋🏻‍♂️! My name is Vedant, and I have an important announcement to make. 

#### The SCADE IDE now officially supports Swift 5.7 🎉. 

In this article, we will discuss what’s new in Swift 5.7 that is supported by the SCADE editor as well. We will discuss the Swift-nio application and try using the async/await feature with the SCADE editor. 

## What’s new in Swift 5.7 + SCADE 🔥



1. **Concurrency in top-level code**: SwiftNIO is compatible with the SCADE IDE. The good thing is SCADE now builds the Swift NIO classes for the Android platform as well.
2. **Async/await much easier to Use**: The much-awaited async/await is now compatible with the latest SCADE version. In this article, its usage is explained.
3. **Distributed actor isolation**: Introduces the ability for actors to work in a distributed form – to read and write properties or call methods over a network using remote procedure calls (RPC). We can now use the **actor** keyword in SCADE.

## SwiftNIO with SCADE

SwiftNIO is a cross-platform asynchronous event-driven network application framework for the rapid development of maintainable high-performance protocol servers & clients.

As SCADE now supports Swift 5.7 it is very easy to implement protocol clients using async/await functionality. We will implement a **TCP protocol** connection as an example to demonstrate the functionality of SwiftNIO with the SCADE editor.

To begin, please make sure you have the latest SCADE editor build(greater than v2.0.57) installed. Create a sample SCADE project and set up the iOS and Android platforms simulators to test the application.

## Integrate Apple’s swift-nio SDK

You can check Apple's swift-nio SDK  [here](https://github.com/apple/swift-nio). It is open-source and you can easily add it to the project using SPM in `Package.swift`.


```swift
// swift-tools-version:5.7

import PackageDescription
import Foundation

let SCADE_SDK = ProcessInfo.processInfo.environment["SCADE_SDK"] ?? ""

let package = Package(
    name: "UgSwiftNioExample",
    platforms: [
        .macOS(.v10_14)
    ],
    products: [
        .library(
            name: "UgSwiftNioExample",
            type: .static,
            targets: [
                "UgSwiftNioExample"
            ]
        )
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-nio.git", from: "2.46.0")
    ],
    targets: [
        .target(
            name: "UgSwiftNioExample",
            dependencies: [
                .product(name: "NIO", package: "swift-nio")
            ],
            exclude: ["main.page"],
            swiftSettings: [
                .unsafeFlags(["-F", SCADE_SDK], .when(platforms: [.macOS, .iOS])),
                .unsafeFlags(["-I", "\(SCADE_SDK)/include"], .when(platforms: [.android])),
            ]
        )
    ]
)
```


## Create a TCP Client & Handler class

Now we will create two classes `TCPClient` & `TCPClientHandler`. These classes use the NIO framework to establish the TCP connection. Let’s import the required packages.


```swift
import Foundation
import NIO
import ScadeKit
```


The `TCPClient` class will take the host and port as the input endpoints to make a connection request. 

As the next step, the `ClientBootstrap` instance is used so that a `Channel` is created & initialized. It will accept the `TCPClientHandler` instance.


```swift
class TCPClient {
  private let group = MultiThreadedEventLoopGroup(numberOfThreads: 10)
  private var host: String
  private var port: Int
  private var channel: Channel?
  private var dataHandler: (String) -> Void

  init(host: String, port: Int, dataHandler: @escaping (String) -> Void) {
    self.host = host
    self.port = port
    self.dataHandler = dataHandler
  }

  func start() async throws -> Bool {
    do {
      let bootstrap = ClientBootstrap(group: group)
        .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
        .channelInitializer { channel in
          channel.pipeline.addHandler(TCPClientHandler(client: self))
        }

      channel = try await bootstrap.connect(host: host, port: port).wait()
      return true
    } catch let error {
      print(error)
      return false
    }
  }

  func callDataHandler(data: String) -> String {
    dataHandler(data)
    return data
  }

}
```


`TCPClientHandler` contains the lifecycle methods for receiving the data packets. 



1. **channelActive**: invoked as soon the channel is initialized. Then it establishes the HTTP connection in the given context of the current channel.
2. **channelRead**: Once data gets available, the current channel reads the data in bytes. In this method, we can preprocess the data or append it to the final data string.
3. **channelInactive**:  After all data is consumed, the existing channel instance gets closed. In this method, the `TCPClient` instance receives the final fetched data string.
4. **errorCaught**: During the entire TCP connection, if there is some sort of error occurred, this method gets called and terminates the TCP connection.

 ```swift
public class TCPClientHandler: ChannelInboundHandler {
  public typealias InboundIn = ByteBuffer
  public typealias OutboundOut = ByteBuffer
  private var client: TCPClient
  private var allData: String = ""

  init(client: TCPClient) {
    self.client = client
  }

  // channel is connected, send http request
  public func channelActive(context: ChannelHandlerContext) {
    let message = "GET / HTTP/1.0\r\n\r\n"
    var buffer = context.channel.allocator.buffer(capacity: message.utf8.count)
    buffer.writeString(message)
    context.writeAndFlush(wrapOutboundOut(buffer), promise: nil)
  }

  // channel data available, read data
  public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
    var buffer = unwrapInboundIn(data)
    let readableBytes = buffer.readableBytes
    if let received = buffer.readString(length: readableBytes) {
      allData += received
    }
  }

  // channel closed, invoking data handler
  public func channelInactive(context: ChannelHandlerContext) {
    client.callDataHandler(data: allData)

  }

  public func errorCaught(context: ChannelHandlerContext, error: Error) {
    print("error: \(error.localizedDescription)")
    context.close(promise: nil)
  }
}
```



## Design some UI for SCADE App

We will create a page design to enter the input fields host & port. With the click of a button, it will display the fetched data on a label.  
It is optional and you can also create the page as per your choice of design.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/53ox1om0vx3souekkl29.png)

## Call the TCPClient Methods in SCADE App

As the first step, let’s fetch the values stored in the Host & Port text fields, and store them in some variables.


```swift
    var currHostString: String = ""
    var currPortString: String = ""

    self.host_tb.onTextChange.append(
      SCDWidgetsTextChangeEventHandler { ev in
        currHostString = ev!.newValue
      })

    self.port_tb.onTextChange.append(
      SCDWidgetsTextChangeEventHandler { ev in
        currPortString = ev!.newValue
      })
```


On the click of the `Get Data` button, it will initialize the `TCPClient` instance with the host & port as parameters. 


```swift
    self.getdata_btn.onClick { _ in
      print("\(currHostString) \(currPortString)")

      if currHostString.isEmpty || currPortString.isEmpty {
        print("Host or Port is Empty")
        return
      }

      self.client = TCPClient(host: currHostString, port: Int(currPortString)!) {
        data in
        print(data)
        let stringData: String = String(data)
        let a = stringData.index(stringData.startIndex, offsetBy: 500)
        let result = stringData.substring(to: a)
        self.response_label.text = result
      }
}
```


It has a callback function that returns the data. Then as soon as the data is returned, the label displays the data string.

## Async/Await usage to start the TCPClient

The final step is now to call the `start()` method of the `TCPClient` instance. Since this action is asynchronous in nature, we will use the async/await keyword to make an async call here. The good news is SCADE's latest version is now compatible with the Async/Await usage.


```swift
Task {
        let result = try await self.client?.start()
}
```



Here we used the await keyword to call the `start()` method of the client as this one is an async network call. The `start()` function of the client is also marked async as shown below.


```swift
func start() async throws -> Bool {
    do {
      let bootstrap = ClientBootstrap(group: group)
        .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
        .channelInitializer { channel in
          channel.pipeline.addHandler(TCPClientHandler(client: self))
        }
 …
```


## `actor` is now supported by SCADE

In Swift, an actor is a new feature introduced in Swift 5.5 that allows for safe and efficient concurrency. Actors are a type of class that provides a way to encapsulate state and behavior while ensuring that only one piece of code can access the actor's mutable state at any given time. Actors in Swift can be thought of as objects that execute in a separate task, allowing multiple tasks to concurrently communicate with and access the actor's state. This concurrency model enables the safe sharing of mutable states between tasks without the need for locks or other synchronization primitives.

To define an actor in Swift, you use the `actor` keyword before the class keyword, as shown in the example: 



```swift
actor MyActor {
  // actor implementation
}
```


## SCADE example App using `actor`

Given below is a small example demonstrating the usage of `actor` keyword in SCADE application. We have created an async method `asynCalculation()` which uses await keyword to perform the asynchronous operation.

Here we use the `MainActor.run()` method to run the concurrent operation on the main thread. The `MainActor` helps to call the UI widgets inside the `run()` method. 


```swift
class Calculator {
	private var adapter: MainPageAdapter

	public init(adapter: MainPageAdapter) {
		self.adapter = adapter
	}

    func runCalculation() {
    	print("runCalculation BEGIN")
        Task.detached(priority: .background) {
        	print("runCalculation BACKGROUND")
            let _ = await self.asyncCalculation()
            print("runCalculation BACKGROUND 1")
            await MainActor.run {
            	print("runCalculation SET TEXT")
            	self.adapter.label.text = "Async task finished!"
            }
            print("runCalculation BACKGROUND END")
        }

        print("runCalculation END")
    }


    func asyncCalculation() async -> Bool {
        await Task.sleep(3 * 1_000_000_000)
        return true
    }
}
```


Hence, we are able to use the `actor` functionality in SCADE editor. Let’s now deploy the application and test it for iOS and Android platforms.

## Run the App!

Let’s now first run the app on any iOS simulator and check if it’s working fine or not.

**iOS**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5dw57g1g3t362yacol95.gif)

**Android**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fb3edflcorszbz0nozeh.gif)

Voila 🎉! It is very easy to integrate AppleNIO with the async/await & actor keywords into the SCADE app. You can find the full source code of the demo application [here](https://github.com/scadedoc/UgExamples). We recommend you to use the SCADE editor for developing cross-platform Swift apps. Thank you for reading and see you in the next article!