- 投稿日:2019-06-22T23:17:21+09:00
iOS10でもWKWebViewを使ってPOSTしたい
はじめに
初心者のハマりどころのひとつと思われるWebViewにまつわる話です(私もiOS初心者です)。
iOS10でWKWebViewを使用してPOSTでリクエストする時、httpBodyがnilになるというバグがあるようです。
バグについては、iOS10までとiOS11以降で検証されているこちらの記事が分かりやすいです。
解決策はこちらの記事を参考にしました。前提条件
XCode 10.2.1
Swift4
DeploymentTarget: iOS10解決案
今回は「はじめに」の参考にした記事で紹介されている、「URLSessionDataTaskで取得したデータをWKWebViewの以下のメソッドに渡す」方法で対応しました。
まずはURLSessionでリクエストを送信する処理を追加していきます。
今回はデータをjson形式にして送ります。
do-catch内でURLSessionDataTaskにリクエストの内容を渡して、リクエストを開始します。ViewController.swift@IBOutlet weak var webViewContainer: UIView! private let url = "http://xxx.xxx.x.x" override func viewDidLoad() { super.viewDidLoad() let config = URLSessionConfiguration.default let session = URLSession(configuration: config, delegate: self, delegateQueue: nil) var request = URLRequest(url: URL(string: url)!) request.httpMethod = "POST" request.addValue("application/json", forHTTPHeaderField: "Content-Type") let params: [String: String] = [ "hoge": "hoge", "huga": "huga" ] do { request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted) let task = session.dataTask(with: request as URLRequest) task.resume() } catch { print(error.localizedDescription) } }URLSessionDataDelegateに、URLSessionで受け取ったデータをWebViewに渡す処理を実装します。UIを更新する処理なのでメインスレッドで行う必要があります。DispatchQueueを使ってメインスレッドでWebViewのロードを行います。
ViewController.swiftextension ViewController: URLSessionDataDelegate { func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { DispatchQueue.main.async(execute: { let webView = WKWebView(frame: self.webViewContainer.bounds) self.webViewContainer.addSubview(webView) webView.load(data, mimeType: "text/html", characterEncodingName: "UTF-8", baseURL: URL(string: self.url)!) }) } }Web側のソースです。
今回はローカルにXAMPPを入れてテストします。test.php<?php $json = file_get_contents('php://input'); echo $json;結果
まとめ
iOS13が発表されましたが、iOS10がサポートされている間は、この手法を使うことがあるかもしれません。ただ私はまだiOSの経験が浅いですし、この実装方法で正しいか分かりませんので、もっといい方法があれば知りたいです。
参考
iOS(swift)ガワアプリの作成で色々苦労した話
iOSのWKWebViewの3つのつまずきポイントと解決方法
SwiftでjsonをphpへPOSTする
- 投稿日:2019-06-22T16:04:38+09:00
Xcode11で作成したプロジェクトを古いOSに対応させる(とりあえず版)
1. AppDelegate.swift と SceneDelegate.swift
エラーが出ているfunctionに@availableを付ける。AppDelegate.swift SceneDelegate.swift@available(iOS 13.0, *)
2. AppDelegateにwindowプロパティを追加する。
AppDelegate.swiftclass AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? // 追加
とりあえず、これでエラーは出なくなります。
ただし、
(iOS 13)
1. 起動
2.AppDelegate
application:didFinishLaunchingWithOptions: (windowはnil)
3.SceneDelegate
scene:willConnectTo:options (windowに値がセットされる)(iOS 12以下)
1. 起動
2.AppDelegate
application:didFinishLaunchingWithOptions: (windowに値がセットされる)
3.SceneDelegate
呼ばれないこのような起動時の流れになりますので、windowプロパティにget / setしている場合はもうひと工夫必要になります。
Beta版を元に作成しています。製品版リリースでは変更される可能性があります。
- 投稿日:2019-06-22T11:25:33+09:00
FirebaseだけでiOS版マッチングアプリ「Rose Me」をリリースするまでの話
限定公開した日付になっていたので再投稿しました泣
普段は京都大学で学生をしており、iOSエンジニアとしての歴も3年目となりました。
NoSQLもFirebaseも全く触ったことのなかった僕が、バックエンド全てFirebaseで完結させた知見を残しておきます。
何を作ったのか
ランディングページ
https://app-rose.me/AppStore
https://apps.apple.com/us/app/rose-me/id1453049174?l=ja&ls=1恋愛は試着する時代
という信念のもと、試着感覚のデートを提供するマッチングアプリRose Meを開発しました。
空いている時間に、好きな場所で1時間だけのデートのおさそいをして、ローズを送ることでデートに行きたいという気持ちを伝えることができます。
デート後に、お互いが気になるボタンを押していれば連絡はとりあうことができ、片方でも押さなければ連絡はとれなくなるといったシステムです。
バラを実際に渡す感じを実装したのでぜひアプリで触ってみてください!
開発チーム
iOSエンジニア2人というめちゃくちゃに偏ったチームでした。
役回りとしては
僕: プロダクト設計・DB設計・Firebase周り全て・アプリ全般・年齢確認Webアプリ・書類全般・UI 相方: 「チャット・プロフィール・マイページ」等の主要画面の開発・LP作成・UIって感じでした。
後半はZenHubを使用してWeek単位でタスク管理をして開発を進めていました。
ちなみに僕はプロダクト設計・DB設計(NoSQL)・Firebase・Webアプリ(React)は全部初めてでした。
UIは二人とも素人で、これだけを参考にして画面作ってましたww
なぜFirebaseだけでマッチングアプリを作ったのか
iOSエンジニアの友人と法人を立ててマッチングアプリを作ろうということで、2018年の10月から開発をスタートしました。
そこで直面した問題が
「バックエンドできる人いなくね?www」
でした。
「バックエンドを簡単に作れるFirebaseみたいなものがあるらしい。」ぐらいの知識でしたが、Cookpadさんの記事をみて、藁にもすがる思いで速攻採用しました。
結論から言うと、Firebaseを選択して大正解でした!!
使用した技術
Firebase
- Authentification
- Facebookログインに使用しました
- Cloud Firestore
- NoSQLのデータベース、必須です
- Cloud Functions
- DBの変更を監視してプッシュ通知を送ったり
- Cloud Storage
- プロフィール画像を保存しています
- Hosting
- 年齢確認用のWebアプリをのっけてます
主要なライブラリ
- Pring: Firestoreでモデルを扱うのにめちゃくちゃ重宝しました。今はdepricatedでBallcapを使うと良いみたいです。
- HGCircularSlider: Rose Meでは時間を視覚的に扱いたかったので採用しました。
- RxSwift: 言わずもがな
- IGListKit: タイムラインの差分更新するのに使用しました。
- etc...
アーキテクチャ
開発速度を重視したかったのでMVVMを採用しました。
MVVMを触ったことがなくこれもイケるやろ!とおもって採用したら、結局MVCとMVVM足して割る2みたいなコードができあがりました。ホクホク。
苦労したこと
Cloud FunctionsがNode.jsのみ対応
GCP版のCloud Functionsを使えばNode.js・Python・Goに対応していますが、Firebase版ではNode.jsのみです。
これに気づかずTypeScriptを1から勉強して書きました。ただし僕は、TypeScriptで書くことをオススメします。
動的型付けはかなり辛くて、ドキュメントとエディタの行き来で1日が終わります。Cloud Functionsをローカルで実行できなかった
Firestoreの変更をトリガーにして処理をするFunctionを書いていました。
そのためデプロイして、Firestoreのフィールドを弄って、ログを見て正しく実行されているかチェックしていました。このせいで無駄なデプロイを100回はした気がします。
ローカルエミュレータなるものがあるみたいですが、今回は諦めました。
https://firebase.google.com/docs/functions/local-emulator?hl=jaクライアント側にFirestoreの操作を記述する必要がある
クライアント側でクエリを構成してFirestoreからのフェッチ処理を記述しているので、Android版やWeb版を作る際に通信ロジックをそれぞれに記述しなければならないことが大変。
もしマルチプラットフォームを見据えているのであればCloud FunctionsでREST APIを構成したらいいのかもしれない。
Firestoreの監視による無限ループ
これはあるあるかもしれないですが、Firestoreでの変更を監視して、そのドキュメントに対して何か書き込みをした場合、再び
onUpdate
が呼ばれ無限に書き込みをしてしまうことがありました。RxSwiftでは、適切に
distinctUntilChanged()
を噛ませないと大変なことになります。(アプリも、請求金額も)テストデータを手作業で突っ込んでいた
日時を指定してデートを募集するアプリであるため、タイムラインに過去のデートは表示されない仕組みになっています。
(Develop環境は過去のデートを表示しても良かったですが、そこまで頭が回りませんでした)毎回相方とアプリ内で操作して、募集を作って、ローズを送りあってマッチしてました。
最初の段階でCloud FunctionsでHTTPメソッド叩くだけでテストデータが生成されるような仕組みを作っておいた方がいいです!
絶対に!!Appleの審査で必ずやりとりをしなければならなかった
合計5回ぐらいAppleに審査を出したんですが、毎回英語でメッセージのやり取りがありました。
毎回テストアカウントを用意して登録フローからレビューしてもらうのですが、
「アプリの全ての機能が使えない。」
と言われました。
というのも、年齢確認が完了していないと投稿や応募ができないため審査ができないとのこと。
やり取りした後も1-2日待たされて、審査に4日はかかります。
テストアカウントだけは年齢確認のフローは飛ばせるようにした方が審査は圧倒的に楽になります!
インターネット異性紹介事業の届出
個人的に何よりも辛かったです。
書類を10個以上用意して警察に持っていくんですが、京都ではインターネット異性紹介事業の届出の前例がなく受理できるかわからないと言われ、3日ほど待たされました
Webアプリじゃないから固定のURLがないのでダメという理由の一点張りで、何を言っても通じませんでした。
奥の方からITに詳しい人が出てきましたが、「GCP...?」「ハッピーメールと一緒?」と言われ悲しい気持ちになりました。よかったこと
Cloud Firestoreが正式リリースした
2019年2月にFirestoreの正式リリースが行われ東京リージョンができました。(asia-northeast1)
新規開発をする人は、Realtime DatabaseじゃなくてFirestore一択で!!オンライン/オフラインをあまり気にしなくていい
端末自体にFirestoreのキャッシュを持っているので、キャッシュに対してクエリを実行することができます。
なので、オフライン時に投稿やプロフィールの変更を行ったとしても、次にオンラインになった時に書き込みをしてくれるのでオフラインのハンドリングに神経質にならなくてもよくなりました。
Hostingが簡単すぎる
年齢確認をするために、WebアプリをReactで作成しました。
Buid
ディレクトリを指定してdeployするだけでhttps://{project-id}.web.app
が生成されます。こんな感じのWebアプリを作りました。
自分たちで地道に1件1件、年齢確認をしていきます。笑
Pringが神
例えばこれだけでメッセージのスキーマが作れます。
開発者の@1amageek さんに感謝しかありません。Message.swiftimport Pring @objcMembers class Message: Object { dynamic var text: String? dynamic var senderRef: Reference<User> = Reference() }結局Firebaseはどうなのか
開発スピードとスケーラビリティを重視するのであれば自信を持ってオススメします!
iOSエンジニアしかチームにいなくて、誰もFirebaseを使ったことがなくても全然なんとかなります。
Firebaseとの連携作業も慣れれば10分で終わります。
ひとこと
今日の朝シャワーを浴びながら思ったことを書こうと思います。
技術しか知らなかった僕が去年の6月に会社を立てて、マッチングサービスを出す!
と意気込んでからちょうど1年がたったのかとシャンプーをしながらしみじみと感じていました。思えばDeNAのインターンに行ってからユーザーのニーズを考え抜いてサービスを作る大事さ、楽しさを知ったなと思ったり。
デザイナーさんを雇うお金もないのでダサいUIだったのですが、たまたま東京で会うことのできたデザイナーさんにしつこく画面を見せてレビューしてもらったり。(あの時は本当にありがとうございます。)
男だけで恋愛バラエティをみてこんなデートいいなぁ〜っていいながら着想を得たり、毎日大学にこもって開発したり。
「マッチングはレッドオーシャンだから難しい。」
「試着できるデートは斬新で面白そう。」
「ネットで出会うなんてあり得ない。」
と色々な意見をもらって、自信がついたり無くしたりを繰り返していました。走馬灯のように色々思い出す中で、どれだけの人が自分たちのサービスを使ってくれるのか、パートナーが見つかるのか、不安とドキドキでいっぱいです。
Qiitaにそぐわない記事になってしまいそうなので、ここらへんにしておきます。
名前は伏せさせていただきますが、Rose Meを考案するにあたって様々な方にアドバイスを頂き、無事リリースすることができました。
本当にありがとうございます!