Help us understand the problem. What is going on with this article?

【Swift x PHP】iOS端末とPHPサーバでHTTP通信

More than 3 years have passed since last update.

概要

サーバとの通信を実装する場合はライブラリを用いることが多いですが、今回はライブラリ未使用で実装してみます。Swiftの勉強などにどうでしょうか。
(実際には「タイムアウト処理は?」「オフラインのときは?」などの処理が必要になってきます。)

対象

サーバ上にPHP環境の構築ができることを前提としております。
また、iOS開発に関しても経験があるといいですが、今回作成するアプリをGitHubに上げているので未経験者でも始められると思います。

手順

  1. お好みのサーバにPHP環境を構築
  2. Install xcode
  3. Clone project file from GitHub
  4. Execute

1. PHP Server

Macでローカルサーバを立ち上げる方法(Qiita)
※私はラズパイを使用しました

PHPのコードは以下のようになります。
$_GET[]の部分で受け取ることのできるパラメータとしてtitlenoteを準備しています。その後、printで文字を出力しています。

RaspberryOne.php
<?php 
  $title = $_GET["title"];
  $note = $_GET["note" ];
  print "$title$note";
?>

試しに、http://[IP Address]/RaspberryOne.php?title=あーちゃんへ&note=大好きですとブラウザに入力してアクセスしてみてください。あーちゃんへ : 大好きですと告白されたら成功です。

2. Install Xcode

Swift3とSwift2のコードを準備しているので準備するXcodeもお好きな方をご準備ください。
Androidほど開発環境構築に時間がかかることはないと思います。

3. iOS App Development

※Serverへの接続先が192.168.2.22であるものとして記述しているので、臨機応変に変えてください(GitHubのコードも同様です)

まず、iOS9以降でセキュリティ的に問題のあるHTTP通信が禁止されているので例外で許可させておきましょう。HTTPSで通信できるようにサーバを構築した場合はこの作業は必要ありません。

Info.plist
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <key>NSExceptionDomains</key>
    <dict>
        <key>192.168.2.22</key>
        <dict>
            <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
            <false/>
        </dict>
    </dict>
</dict>

次に、アプリの処理を実際に書いていきます。
clickButtonメソッド内に通信処理を書いています。
ユーザにTextFieldへ送信内容を入力してもらい、その内容を含めたURLでアクセスし、結果を取得・表示するといった流れを記述しています。
UIはお好みで!@IBOutletとなっている部分がStoryboardとリンクさせてる部分です。

Swift2 version

ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var resultLabel: UILabel!
    @IBOutlet weak var inputText1: UITextField!
    @IBOutlet weak var inputText2: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        inputText1.delegate = self
        inputText2.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func clickButton(sender: AnyObject) {
        if inputText1.text != "" && inputText2.text != "" {
            let title = inputText1.text!
            let note  = inputText2.text!

            // 接続先のURLにパラメータを追加
            let stringUrl = "http://192.168.2.22/RaspberryOne.php?title=\(title)&note=\(note)"
            // 文字列のURLからNSURLに変換
            let URL = NSURL(string: stringUrl.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)!
            // NSURLを引数にRequestの生成
            let req = NSURLRequest(URL: URL)

            // ここからが実際に通信をする所(非同期通信)
            let task = NSURLSession.sharedSession().dataTaskWithRequest(req, completionHandler: {
                (data, res, err) in
                // サーバから出力はありますか?
                if data != nil {
                    // あるみたいだから文字列に変換しておく
                    let text = NSString(data: data!, encoding: NSUTF8StringEncoding)
                    // 非同期通信内でUIに変更を加えるときのおまじないと思って!
                    dispatch_async(dispatch_get_main_queue(), {
                        self.resultLabel.text = text as String?
                    })
                }else{
                    // サーバから返事がないみたいだからエラー表示
                    dispatch_async(dispatch_get_main_queue(), {
                        self.resultLabel.text = "ERROR"
                    })
                }
            })
            task.resume()
        }
    }

    func textFieldShouldReturn(textField: UITextField) -> Bool{
        //Close keyboard.
        textField.resignFirstResponder()

        return true
    }
}

Swift3 version
Swift3に対応するとともにアラートも追加してみました。
コードの解説はSwift2と変わらないところは省略しています。

ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    //PHP
    @IBOutlet weak var resultLabel: UILabel!
    @IBOutlet weak var inputText1: UITextField!
    @IBOutlet weak var inputText2: UITextField!


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        inputText1.delegate = self
        inputText2.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func clickButton(_ sender: AnyObject) {
        if inputText1.text != "" && inputText2.text != "" {
            let title = inputText1.text!
            let note  = inputText2.text!

            let stringUrl = "http://192.168.1.21/RaspberryOne.php?title=\(title)&note=\(note)"
            // Swift3からはNSがなくなってURLになりました
            let url = URL(string: stringUrl.addingPercentEscapes(using: String.Encoding.utf8)!)!
            // これもNSがなくなりました
            let req = URLRequest(url: url)

            // ここもNSがなくなりました
            let task = URLSession.shared.dataTask(with: req, completionHandler: {
                (data, res, err) in
                if data != nil {
                    let text = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
                    DispatchQueue.main.async(execute: {
                        self.resultLabel.text = text as String?
                    })
                }else{
                    DispatchQueue.main.async(execute: {
                        self.resultLabel.text = "ERROR"
                    })
                }
            })
            task.resume()
        }else{
            // 未入力なのにサーバにアクセスしようとしたら怒るようにした
            alert("error", messageString: "It is not entered.", buttonString: "OK")
        }
    }

    // 標準のアラートを表示させる
    func alert(_ titleString: String, messageString: String, buttonString: String){
        //Create UIAlertController
        let alert: UIAlertController = UIAlertController(title: titleString, message: messageString, preferredStyle: .alert)
        //Create action
        let action = UIAlertAction(title: buttonString, style: .default) { action in
            NSLog("\(titleString):Push button!")
        }
        //Add action
        alert.addAction(action)
        //Start
        present(alert, animated: true, completion: nil)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool{
        //Close keyboard.
        textField.resignFirstResponder()

        return true
    }
}

4. Execute

上部に送信した内容がフォーマットされて返ってきているのがわかると思います。
PHP側の処理をもっと加えれば、あーちゃんからの返事をもらえるアプリになりますね!

結果 結果

最後に

明日はバレンタインです(記事作成時)!
PHPサーバに向けてiOS端末から告白してみては?

サーバ構築とアプリ開発を別の人が担当して共同開発するのもいいですね。きっと面白くなります!!

以下がiOS側のサンプルです。
GitHub | AkkeyLab/SendMessage

planningdev
九州工業大学 ITサービス開発・運用団体
https://www.planningdev.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした