Server Side Swift: basic Client-Server communication example for iOS and Android

This is a repost of a Q&A pair I wrote on Stack Overflow.

Image for post
Image for post
  • An Android app
  • An iOS app

Linux server running Swift

These directions are based on unit one of the free Udacity course titled Server-Side Swift. I recommend taking the whole thing. There have been updates since then, but it explains things slowly and clearly.

1. Log in over ssh to your Linux server

In this example we will use a VPS with a static IP. If you don’t have one you can get one from quite a few places. LowEndBox has inexpensive options. I’m using Ubuntu Linux for this example. You should receive an ip address for the server along with a root password.

  • sudo apt-get install git

2. Install swiftenv

This will help you download Swift server and manage different version environments. The installation directions are pretty clear. Choose the Git option.

git clone https://github.com/kylef/swiftenv.git ~/.swiftenv
  • sudo apt-get install git
# swiftenv
export SWIFTENV_ROOT="$HOME/.swiftenv"
export PATH="$SWIFTENV_ROOT/bin:$PATH"
eval "$(swiftenv init -)"
. ~/.bash_profile
. ~/.bashrc

3. Install Swift

You could download Swift directly, but it is a lot easier to use swiftenv.

swiftenv install 4.0.2
swift
Welcome to Swift version 4.0.2 (swift-4.0.2-RELEASE). Type :help for assistance.
1>

4. Create our Swift service project

This is where we will put the actual Swift server code that will communicate with our Android and iOS apps.

mkdir MyFirstSwiftServer
cd MyFirstSwiftServer
swift package init --type executable

5. Include the Kitura web framework for Swift

The Kitura framework provides the tools to make client-server communication possible. Kitura is from IBM. There are a number of other ones (see a comparison). Perfect is a popular one, but the only tutorial I watched was hard to follow. Anyway, I’m choosing Kitura for now because the Udacity series was so easy to understand.

nano Package.swift
// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescriptionlet package = Package(
name: "MyFirstSwiftServer",
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/IBM-Swift/Kitura.git", from: "2.1.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "MyFirstSwiftServer",
dependencies: ["Kitura"]),
]
)

6. Write the server code

Open the main.swift file

nano Sources/MyFirstSwiftServer/main.swift
import Kitura// Create a new router
let router = Router()
// Handle HTTP GET requests to /
router.get("/") {
request, response, next in
response.send("Hello, Client!")
next()
}
// Add an HTTP server and connect it to the router
Kitura.addHTTPServer(onPort: 8090, with: router)
// Start the Kitura runloop (this call never returns)
Kitura.run()

7. Test the server

In your projects root folder, build the project with

swift build
./.build/debug/MyFirstSwiftServer 
http://93.184.216.34:8090   // replace 93.184.216.34 with your vps ip address
Hello, Client!

Android client

1. Layout

In Android Studio create the following layout

Image for post
Image for post

2. Request the INTERNET permission

Add the following line to the manifest.

<uses-permission android:name="android.permission.INTERNET" />
<application
android:usesCleartextTraffic="true"
...
>

3. Code

Copy in the code. Most of this is adapted from Android AsyncTask HTTP GET request Tutorial. Don’t forget to replace the ip address with your server.

public class MainActivity extends AppCompatActivity {    // replace 93.184.216.34 with your vps server ip address
private static final String SERVER = "http://93.184.216.34:8090/";
private TextView tvServerResponse; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvServerResponse = findViewById(R.id.textView);
Button contactServerButton = findViewById(R.id.button);
contactServerButton.setOnClickListener(onButtonClickListener);
}
View.OnClickListener onButtonClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
HttpGetRequest request = new HttpGetRequest();
request.execute();
}
};
public class HttpGetRequest extends AsyncTask<Void, Void, String> { static final String REQUEST_METHOD = "GET";
static final int READ_TIMEOUT = 15000;
static final int CONNECTION_TIMEOUT = 15000;
@Override
protected String doInBackground(Void... params){
String result;
String inputLine;
try {
// connect to the server
URL myUrl = new URL(SERVER);
HttpURLConnection connection =(HttpURLConnection) myUrl.openConnection();
connection.setRequestMethod(REQUEST_METHOD);
connection.setReadTimeout(READ_TIMEOUT);
connection.setConnectTimeout(CONNECTION_TIMEOUT);
connection.connect();
// get the string from the input stream
InputStreamReader streamReader = new InputStreamReader(connection.getInputStream());
BufferedReader reader = new BufferedReader(streamReader);
StringBuilder stringBuilder = new StringBuilder();
while((inputLine = reader.readLine()) != null){
stringBuilder.append(inputLine);
}
reader.close();
streamReader.close();
result = stringBuilder.toString();
} catch(IOException e) {
e.printStackTrace();
result = null;
}
return result;
}
protected void onPostExecute(String result){
super.onPostExecute(result);
if (result == null) return;
tvServerResponse.setText(result);
}
}
}

4. Test the Android app

Run the app. Press the button and hopefully you should see

Image for post
Image for post

iOS client

1. Layout

In Xcode create the following layout

Image for post
Image for post

2. Disable App Transport Security

This is not ideal for a production app, but it will help us avoid an error since our VPS only supports an unencrypted http connection currently.

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Image for post
Image for post

3. Code

See this Q&A for more info about making an HTTP request in Swift.

import UIKitclass ViewController: UIViewController {    // replace 93.184.216.34 with your vps server ip address
let server = "http://93.184.216.34:8090"
@IBOutlet weak var serverResponseLabel: UILabel! @IBAction func sendRequestButtonTappled(_ sender: UIButton) { guard let url = URL(string: server) else {return} // background task to make request with URLSession
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
if let error = error {
print(error)
return
}
guard let data = data else {return}
guard let dataString = String(data: data, encoding: String.Encoding.utf8) else {return}
// update the UI if all went OK
DispatchQueue.main.async {
self.serverResponseLabel.text = dataString
}
}
// start the task
task.resume()
}
}

4. Test the iOS app

Run the app. Press the button and hopefully you should see

Image for post
Image for post

Update

Rather than develop separate apps for Android and iOS, I am now learning Flutter, which uses the Dart language. (Sigh, yes, another language to learn, but it has similarities to both Java and Swift.) I’m not there yet but I will probably write the backend with Dart as well. See here, here, here, and here.

A Flutter and Dart developer. Follow me on Twitter @suragch1 to get updates of new articles.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store