スマフォアプリ(java/kotlin/Swift/Unity)からサーバ(java)のmariadbを操作する.7




●前提

同時接続は多数
サーバ言語:java+mariaDB+JDBCドライバ
MySQLのrootユーザー名root,passwordは1234abcde
クライアント言語:android-java/android-kotlin/iOS-Swift/unityC#
通信ポート:3456




y
今回はswiftでiOS版の記述をします。CFSocketではなく、iOS12(現在のiOSデバイスの90%以上がiOS12以上)で使えるNetworkFrameworkを使います。storyboardは画像を張っておくので参考にして下さい

多分、RxSwift使わずにNetworkFrameworkでjava/kotlin/swiftのCFSocketで言うストリーム風にソケットを送受信しているサンプルはそうそう無いと思います。実際はレシーバ関数でデータを受け取ったらフラグを立ててデータに詰め込み、メインスレッドでそれを取得します。でもそれをやると、RxJava/RxKotlin/RxSwift/UniRx等のRXフレームワークを使わないとコードがswiftだけ変わってしまうので、設計段階でどうするか考えないといけないです。

言語はSwfit5.0で、5.2は使ってないです。4.2系だとプロパティのデフォルト値を、直接値突っ込むのでは無く、init()で入れろとか言われるかもしれません。とにかく仕様変更が激しいですね、swiftたんは。勉強が大変っす〜。

swiftもkotlinも現代的なオブジェクト指向言語言語です。スマートキャスト、インターフェース(kotlin)やインターフェースプロトコル(swift)で仮想化して擬似的に多重継承できる(コンパイル時解決)、そしてメンバ関数よりもメンバプロパティ(変数)に重点を置きます。プロパティの値が変わった際のリアクションを重視します。自分はこれをプロパティ重視のオブジェクト指向と分類しております。


[ViewController.swift]


//
//  ViewController.swift
//  app0000
//
//  Created by kenjikakera on 2020/04/06.
//  Copyright c 2020 kenjikakera. All rights reserved.
//

import UIKit


class ViewController: UIViewController {
    let client = Client()
    
    @IBOutlet weak var result: UITextView!
    @IBOutlet weak var top: UITextField!
    @IBOutlet weak var count: UITextField!
    @IBOutlet weak var name: UITextField!
    @IBOutlet weak var score: UITextField!

    // 接続
    @IBAction func connect(_ sender: Any) {
        client.connect()
    }

    // 切断
    @IBAction func close(_ sender: Any) {
        client.disconnect()
    }

    // ランキングを見る
    @IBAction func getscore(_ sender: Any) {
        if(top.text != nil) && (count.text != nil){
            let topNum = Int(top.text!)
            let countNum = Int(count.text!)
            if !(topNum==nil) && !(countNum==nil)
                && (topNum!>=1) && (countNum!>=1) && (countNum!<=99) {
                // 入力された文字列の数字ではなく、チェック用に使った数字を文字列にしている実装。これだと010とかも10になる。
                let cmd = "2," + String(topNum!) + "," + String(countNum!) + "\n"
                top.endEditing(true)		// 入力パレットを閉じる
                count.endEditing(true)		// 入力パレットを閉じる
                let res =  client.sendget(cmd ,100)	// 今回は、kotlin/java版と違い、コマンドを送って結果をもらう物ですが、分けてもかまわないと思います。数値の100は大体1秒でタイムアウトします。
                if !res.isEmpty {
                    let arry = res.split(separator: ",")	// ,の区切りで配列に入れます
                    var scoreOut = arry[0] + "人\n"
                     let end = arry.count - 1            // arryの要素数はforの中で変わらないし、.countの計算量がO(1)と期待できないため
                    for i in stride(from:1,to:end,by:2){
                        scoreOut += "score=" + arry[i] + " name =" + arry[i + 1] + "\n"

                    }
                    result.text = String(scoreOut)
                }
            }
        }
    }

    // スコアと名前の登録
    @IBAction func registscore(_ sender: Any) {
        if(name.text != nil) && (score.text != nil){
            let nameStr = name.text!
            let scoreNum = Int(score.text!)
            if !(scoreNum==nil) && (scoreNum!>=0) &&
                (nameStr.prefix(1) != " ") && (nameStr.prefix(1) != " "){
                let cmd = "3," + String(scoreNum!) + "," + nameStr + "\n"
                score.endEditing(true)
                name.endEditing(true)
                client.send(cmd)
            }
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

    }
}

[Client.swift]


//
//  Client.swift
//  app0000
//
//  network framework を使ったsocketの送受信
//  Created by kenjikakera on 2020/04/06.
//  Copyright c 2020 kenjikakera. All rights reserved.
//

import Foundation
import Network

public class Client: NSObject, StreamDelegate  {
    override init(){
        super.init()
        
    }

    var connection:NWConnection? = nil
    var resData:String? = nil
    var isData:Bool = false
    
    /**
     接続と受信ファンクションの登録
     */
    func connect(){
        connection = NWConnection(host: "219.117.194.51", port: 3456,using:.tcp)
        connection?.stateUpdateHandler = { state in
            DispatchQueue.main.async {
                switch state {
                case .setup:
                    print("Setup\n")
                case .preparing:
                    print("Preparing\n")
                case .waiting( _):
                    print("Waiting\n")
                case .ready:
                    print("Ready\n")
                case .failed( _):
                    print("Failed\n")
                case .cancelled:
                    print("Cancelled\n")
                default:
                    print("default\n")
                    break
                }
            }
        }
        let queue = DispatchQueue.global()
        self.connection?.start(queue: queue)
        self.receive(on: connection)
    }
    
    /**
     受信ファンクション
     */
    func receive(on connection: NWConnection?) {
        connection?.receive(minimumIncompleteLength: 0, maximumLength: Int(UInt32.max)) { [weak self] (data, _, _, error) in
            if let data = data {
                self?.resData = String(data: data, encoding: .utf8)
                print(self?.resData ?? "")
                if(self?.resData != nil){
                    self?.isData = true
                }
                self?.receive(on: connection)
            } else {
                print("Received data is nil !")
            }
        }
    }
    
    /**
     フラグ初期化
     */
    func initFlag(){
        self.isData = false
        self.resData = nil
    }
    
    /**
     文字列を送信して受信データを待つ(タイムアウト付き,10ms単位で指定。100を指定したなら大体一秒)
	- parameter str:サーバに送るコマンド
	- parameter count:タイムアウト指定で、こちらが10msなので100で大体10秒
        = returns:サーバからの返値(String)
     */
    func sendget(_ str:String,_ count:Int) -> String {
        send(str)
        for _ in 1...count {
            if self.isData {
                break
            }
            Thread.sleep(forTimeInterval: 0.01)
        }
        let res = self.resData ?? ""
        initFlag()
        return res
    }
    
    /**
     文字列の送信
	- parameter str:サーバに送るコマンド
     */
    func send(_ str:String) {
        initFlag()
        let sendmes:Data = str.data(using: String.Encoding.utf8)!
        let completion = NWConnection.SendCompletion.contentProcessed { (error: NWError?) in
            print("send end/n")
        }
        connection?.send(content:sendmes,completion: completion)
    }
    
    /**
     切断
     */
    func disconnect(){
        connection?.cancel()
        connection = nil
    }
}



次はunityでiOS/android端末から。


戻る