- 投稿日:2020-10-14T22:55:29+09:00
swift実践入門output Chapter4 コレクションを表す型
CollectionとしてのString型
String型は単一の文字を表すCharacter型のコレクションとして定義されており、文字の列挙や文字数のカウントなどの機能を持つ
chapter4.swift//StringIndex型 文字列内の位置を表すかた let string = "abcdefghijelmnopqustuvwxyza" let startString = string[string.startIndex] print(startString) //a //n番目、最後のindexを取得するには let string2 = "abcdefghijelmnopqustuvwxyza" let startString2 = string2.startIndex let bIndex = string.index(startString2, offsetBy: 8) print(bIndex) let bb = string[bIndex] print(bb) //i let endString = string2.endIndex let cIndex = string.index(endString, offsetBy: -1) let cc = string[cIndex] print(cc) //countプロパティを用いて要素数を取得 print(string2.count) //27シーケンス
その要素に一方向から順次アクセス可能なデータ構造
コレクションはシーケンスを包括する概念
一方向からの順次アクセスと特定のインデックスの値へ直接アクセスが可能なデータ構造Sequenceプロトコル 要素へ順次アクセス
chapter4.swift//forEach(_:)メソッド 要素に対して順次アクセス let darray = [1,2,3,4,5,6,7] var enumerated = [] as [Int] darray.forEach ({ element in enumerated.append(element) }) print(enumerated) //[1, 2, 3, 4, 5, 6, 7] //filterd(_:)メソッド 要素を絞り込む let filtered = darray.filter ({ element in element % 2 == 0 }) print(filtered) //[2, 4, 6] //map(_:)メソッド 要素を変換する //全ての要素を特定の要素を用いて変換 let double = darray.map({element in element * 2}) print(double) //[2, 4, 6, 8, 10, 12, 14] //別の型のシーケンスへと変換 Int → String let farray = [1,2,3,4,5] let converted = farray.map({element in String(element)}) print(converted) //["1", "2", "3", "4", "5"] //flatMap(_:)メソッド 要素をシーケンスに変換し、それを一つのシーケンスに追加 let sss = [1,2,3] let ttt = sss.flatMap({value in [value, value * 2]}) print(ttt) //compacttMap(_:)メソッド 要素を、失敗する可能sるのある処理を用いて変換する //全ての要素を特定の処理で変換するが、変換できない値は無視する let ggg = ["abc","123","kjg","456"] let integers = ggg.compactMap({value in Int(value)}) print(integers) //[123, 456] //reduce(_:)メソッド 要素を1つの値にまとめる let sarray = [1,2,3,4,5,6,7,8,9] let sum = sarray.reduce(0, {result, element in result + element} ) print(sum) //45 let concat = sarray.reduce("", {result, element in result + String(element)}) print(concat) //123456789 //Collectionプロトコル サブスクリプトによる要素へのアクセス let warray = [1,2,2,3,2,1,2,3,4] print(warray.count) //9 print(warray.isEmpty) //false print(warray.first) //Optional(1) print(warray.last) //Optional(4) print(warray[3]) //3
- 投稿日:2020-10-14T22:55:29+09:00
swift実践入門output Chapter4 コレクションを表す型 後編
CollectionとしてのString型
String型は単一の文字を表すCharacter型のコレクションとして定義されており、文字の列挙や文字数のカウントなどの機能を持つ
chapter4.swift//StringIndex型 文字列内の位置を表すかた let string = "abcdefghijelmnopqustuvwxyza" let startString = string[string.startIndex] print(startString) //a //n番目、最後のindexを取得するには let string2 = "abcdefghijelmnopqustuvwxyza" let startString2 = string2.startIndex let bIndex = string.index(startString2, offsetBy: 8) print(bIndex) let bb = string[bIndex] print(bb) //i let endString = string2.endIndex let cIndex = string.index(endString, offsetBy: -1) let cc = string[cIndex] print(cc) //countプロパティを用いて要素数を取得 print(string2.count) //27シーケンス
その要素に一方向から順次アクセス可能なデータ構造
コレクションはシーケンスを包括する概念
一方向からの順次アクセスと特定のインデックスの値へ直接アクセスが可能なデータ構造Sequenceプロトコル 要素へ順次アクセス
chapter4.swift//forEach(_:)メソッド 要素に対して順次アクセス let darray = [1,2,3,4,5,6,7] var enumerated = [] as [Int] darray.forEach ({ element in enumerated.append(element) }) print(enumerated) //[1, 2, 3, 4, 5, 6, 7] //filterd(_:)メソッド 要素を絞り込む let filtered = darray.filter ({ element in element % 2 == 0 }) print(filtered) //[2, 4, 6] //map(_:)メソッド 要素を変換する //全ての要素を特定の要素を用いて変換 let double = darray.map({element in element * 2}) print(double) //[2, 4, 6, 8, 10, 12, 14] //別の型のシーケンスへと変換 Int → String let farray = [1,2,3,4,5] let converted = farray.map({element in String(element)}) print(converted) //["1", "2", "3", "4", "5"] //flatMap(_:)メソッド 要素をシーケンスに変換し、それを一つのシーケンスに追加 let sss = [1,2,3] let ttt = sss.flatMap({value in [value, value * 2]}) print(ttt) //compacttMap(_:)メソッド 要素を、失敗する可能sるのある処理を用いて変換する //全ての要素を特定の処理で変換するが、変換できない値は無視する let ggg = ["abc","123","kjg","456"] let integers = ggg.compactMap({value in Int(value)}) print(integers) //[123, 456] //reduce(_:)メソッド 要素を1つの値にまとめる let sarray = [1,2,3,4,5,6,7,8,9] let sum = sarray.reduce(0, {result, element in result + element} ) print(sum) //45 let concat = sarray.reduce("", {result, element in result + String(element)}) print(concat) //123456789 //Collectionプロトコル サブスクリプトによる要素へのアクセス let warray = [1,2,2,3,2,1,2,3,4] print(warray.count) //9 print(warray.isEmpty) //false print(warray.first) //Optional(1) print(warray.last) //Optional(4) print(warray[3]) //3
- 投稿日:2020-10-14T22:24:40+09:00
swift実践入門output Chapter4 コレクションを表す型
コレクションとは値の集まりのこと
配列を表す
Array< Element>型辞書を表す
Dictionary< Key, Value>型範囲を表す
Range< Bound>型配列を表す Array< Element >型
実際にはArray< Int >型、Array< String >型のように使う
chapter4.swiftlet a = [1,2,3] let b = ["a","b","c"] let array :[Int] = [] var strings = ["aaa","bbb","ccc"] var strings1 = strings[0] print(strings1) //aaa strings[2] = "gagaga" print(strings) ["aaa", "bbb", "gagaga"] //末尾に追加 strings.append("yamato") print(strings) //["aaa", "bbb", "gagaga", "yamato"] //任意の位置に追加 strings.insert("mama", at: 2) print(strings) //["aaa", "bbb", "mama", "gagaga", "yamato"] //削除は3タイプ var integer = [1,2,3,4,5] integer.remove(at: 2) integer integer.removeLast() integer integer.removeAll() integer辞書を表す Dictionary< Key, Value >型
chapter4.swiftlet dictionary = ["Key":1] let value = dictionary["key"] print(value) //["key": 1] //変更 var dictionary1 = ["key": 2] dictionary1["key"] = 1 print(dictionary1) //追加 var dictionary2 = ["key":3] dictionary2["key2"] = 4 print(dictionary2) //["key2": 4, "key": 3] //削除 var dictionary3 = ["key":5] dictionary3["key"] = nil print(dictionary3) //[:]範囲型 範囲を表す型
chapter4.swift//末尾を含まない範囲 countableRange<Bound>型 let range = 1..<4 for value in range { print(value) } //1 //2 //3 //... 末尾を含む型 CountableClosedRange<Bound>型 let range2 = 1...4 for value in range2 { print(value) } //1 //2 //3 //4 let range3 = 1...5 print(range3.upperBound) print(range3.lowerBound) //5 //1 //値が範囲に含まれるかの判定 let range4 = 1..<10 print(range4.contains(4)) print(range4.contains(10)) //true //false
- 投稿日:2020-10-14T22:24:40+09:00
swift実践入門output Chapter4 コレクションを表す型 前編
コレクションとは値の集まりのこと
配列を表す
Array< Element>型辞書を表す
Dictionary< Key, Value>型範囲を表す
Range< Bound>型配列を表す Array< Element >型
実際にはArray< Int >型、Array< String >型のように使う
chapter4.swiftlet a = [1,2,3] let b = ["a","b","c"] let array :[Int] = [] var strings = ["aaa","bbb","ccc"] var strings1 = strings[0] print(strings1) //aaa strings[2] = "gagaga" print(strings) ["aaa", "bbb", "gagaga"] //末尾に追加 strings.append("yamato") print(strings) //["aaa", "bbb", "gagaga", "yamato"] //任意の位置に追加 strings.insert("mama", at: 2) print(strings) //["aaa", "bbb", "mama", "gagaga", "yamato"] //削除は3タイプ var integer = [1,2,3,4,5] integer.remove(at: 2) integer integer.removeLast() integer integer.removeAll() integer辞書を表す Dictionary< Key, Value >型
chapter4.swiftlet dictionary = ["Key":1] let value = dictionary["key"] print(value) //["key": 1] //変更 var dictionary1 = ["key": 2] dictionary1["key"] = 1 print(dictionary1) //追加 var dictionary2 = ["key":3] dictionary2["key2"] = 4 print(dictionary2) //["key2": 4, "key": 3] //削除 var dictionary3 = ["key":5] dictionary3["key"] = nil print(dictionary3) //[:]範囲型 範囲を表す型
chapter4.swift//末尾を含まない範囲 countableRange<Bound>型 let range = 1..<4 for value in range { print(value) } //1 //2 //3 //... 末尾を含む型 CountableClosedRange<Bound>型 let range2 = 1...4 for value in range2 { print(value) } //1 //2 //3 //4 let range3 = 1...5 print(range3.upperBound) print(range3.lowerBound) //5 //1 //値が範囲に含まれるかの判定 let range4 = 1..<10 print(range4.contains(4)) print(range4.contains(10)) //true //false
- 投稿日:2020-10-14T21:34:26+09:00
SwiftでProperty List(.plist)への書き込みと読み込みを実装する
Property List(.plistファイル)へのデータ書き込みとデータ読み込みを、
PropertyListEncoder(Decoder)
とCodable
に準拠したモデルを利用して行います。前提
Swift: 5.0
実装
モデルの用意
Property Listに保存したいデータのモデルを、
Codable
に準拠する形で作成します。
今回は例として、タイトルと著者名を要素に持つBook
をモデルとします。Book.swiftstruct Book: Codable { var title: String var writerName: String }データの書き込み
ファイル操作は
FileManager
を用いて行います。
先程用意したモデルを、PropertyListEncoder
を使ってエンコードすることで、Property Listへの書き込みが行えます。BookManager.swiftclass BookManager { // 扱うProperty ListのURL Path static private var plistURL: URL { let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! return documents.appendingPathComponent("book.plist") } static func write(book: Book) { let encoder = PropertyListEncoder() // 保存したいデータをエンコード guard let data = try? encoder.encode(book) else { return } // すでにProperty Listが存在する場合は上書き。そうでない場合は新しく作成 if FileManager.default.fileExists(atPath: plistURL.path) { try? data.write(to: plistURL) } else { FileManager.default.createFile(atPath: plistURL.path, contents: data, attributes: nil) } } }データの読み込み
データの書き込みと同様に、ファイル操作は
FileManager
を用いて行います。
PropertyListDecoder
を用いてProperty Listから読み込んだデータをBook
モデルにデコードしています。BookManager.swiftclass BookManager { // 省略 // static func load() -> Book { let decoder = PropertyListDecoder() // 保存先のProperty Listからデータを読み込んでデコード guard let data = try? Data.init(contentsOf: plistURL), let book = try? decoder.decode(Book.self, from: data) else { return Book(title: "", writerName: "") } return book } }終わりに
Codable
及びPropertyListEncoder(Decoder)
を使うことで、Property List(.plist)でのデータ操作を楽に実装することができました。参考
- 投稿日:2020-10-14T15:18:39+09:00
Swift cornerRadiusのあれこれ
指定の角を丸くしたい
今回はUIImageについて触れる
任意のファイルに記載(わたしはUIパーツのextensionファイルを作成してそこに記載している)
///指定の角を指定の半径で丸くするメソッド ///- Parameters: /// - 半径 /// - 指定の角 public func setCornerRadius(_ radius: CGFloat,_ corners: CACornerMask) { layer.cornerRadius = radius layer.masksToBounds = true layer.maskedCorners = corners }使い方
///今回は全ての角に対して丸くする処理を行う。 let corners = [.layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner, .layerMaxXMaxYCorner ] view.setCornerRadius(15, corners)以上。参考にしていただけると幸いです。
- 投稿日:2020-10-14T13:20:14+09:00
ヒラギノフォント使用時のUILabelの高さ調整
概要
iOS14にて、ヒラギノフォントを指定していたUILabelで、 g や jといった、
下にはみ出す文字の下部がはみ出すようになったので、その対応。問題の詳しい解説などは以下などを参考にしてください
https://koze.hatenablog.jp/entry/2020/05/11/093000
http://akisute.com/2015/11/ios.html環境
Swift5
Xcode11.3 (Xcode12.0でも動作確認)解決作
*正直この対応で本当に問題がないのか、あまり自信はありません。間違いなどありましたら、ご指摘いただけますと幸いです??♀️
Extentionで関数使って修正している情報は見つかったのですが、
今回すでに問題が起こりうるヒラギノをつかった箇所がたくさんあり、ヒラギノフォントの設定はxibでやっていたため、
あまりコードはいじりたくないなあというところがあったので、カスタムクラスを作成して、そのカスタムクラスをxibで設定すれば、勝手に調整するようにしてみました。
調整の内容じたいは上でも紹介している以下と同じです。
http://akisute.com/2015/11/ios.htmlimport UIKit class HiraginoUILabel: UILabel { override var intrinsicContentSize: CGSize { var size = super.intrinsicContentSize // ヒラギノフォントで日本語と英語が混じっている場合のラベルサイズずれ対応 if let font = self.font, font.familyName.hasPrefix("Hiragino") { // 念の為ヒラギノフォントであること確認 size.width = ceil(size.width); size.height = ceil(size.height + abs(font.descender)); } return size; } }これで、xib側で、ヒラギノを指定しているLabelにカスタムクラスを指定すると、高さが自動で調節されます。
念の為カスタムクラス側でフォントを確認しているため、他のフォントに変わってクラス指定を外すことを忘れても、何もしないようにしています。
以上です。
- 投稿日:2020-10-14T13:20:14+09:00
[iOS14]ヒラギノフォント使用時のUILabelの高さ調整
概要
iOS14にて、ヒラギノフォントを指定していたUILabelで、 g や jといった、
下にはみ出す文字の下部が見切れるようになったので、その対応。問題の詳しい解説などは以下などを参考にしてください
https://koze.hatenablog.jp/entry/2020/05/11/093000
http://akisute.com/2015/11/ios.html環境
Swift5
Xcode11.3 (Xcode12.0でも動作確認)解決作
*正直この対応で本当に問題がないのか、あまり自信はありません。間違いなどありましたら、ご指摘いただけますと幸いです??♀️
Extentionで関数使って修正している情報は見つかったのですが、
今回すでに問題が起こりうるヒラギノをつかった箇所がたくさんあり、かつヒラギノの設定はxibでやっていたため、修正もxibできる方法を探しました。サイズを調整するカスタムクラスを作成しました。
調整の内容自体は上でも紹介している以下の内容と同じです。
http://akisute.com/2015/11/ios.htmlimport UIKit class HiraginoUILabel: UILabel { override var intrinsicContentSize: CGSize { var size = super.intrinsicContentSize // ヒラギノフォントで日本語と英語が混じっている場合のラベルサイズずれ対応 if let font = self.font, font.familyName.hasPrefix("Hiragino") { // 念の為ヒラギノフォントであること確認 size.width = ceil(size.width); size.height = ceil(size.height + abs(font.descender)); } return size; } }これで、xib側で、ヒラギノを指定しているLabelにカスタムクラスを指定すると、高さが自動で調節されます。
念の為カスタムクラス側でフォントを確認しているため、他のフォントに変わってクラス指定を外すことを忘れても、何もしないようにしています。
以上です。
- 投稿日:2020-10-14T11:06:07+09:00
Swiftを使ってsinxの近似値を求める
なぜSwiftなの?
Swiftを使うために環境構築を行うが必要ないため,すぐに始められる。MacかiPadがあればPlaygroundsをインストールするだけで使うことができる。
また,Playgroundsにはステップ実行やゆっくり実行という機能があり,for文やwhile分をステップごとに実行し,実行されている時の値の変化を確認することもできる。
さらに,ビューア機能を使えば変数の変化の様子を勝手にグラフ表示してくれる。
sinxのマクローリン展開
$sinx$をマクローリン展開すると,
$$
sinx = x - \frac{x^3}{3!}+\frac{x^5}{5!}-\frac{x^7}{7!}+⋯+\frac{(-1)^{n-1}x^{2n-1}}{(2n-1)!}+⋯
$$と表されるのであった。この記事では$sinx$の近似値をプログラミングを使って求めていく。
1.変数を用意する
マクローリン展開は何回でも微分できる関数$f(x)$を$x$の冪乗関数の級数の展開式の和で近似させるものであるのであった。この記事では$sinx$の値を
sum
として,sum
にだんだん値を加えていく。var sum = 0 print(sum) //0 sum += 1 print(sum) //1また,近似する$x$を入れる定数も用意しなければならない。この時近似する値として$\pi/6$や$\pi/3$などの値を用いたいので,$\pi$が使えるよう
import Foundation
を書いておく。import Foundation var sum = 0 let x = Double.pi/2さらに,項として更新され続ける変数を用意する。
var item = x第一項は定数と同じになるので,
x
を入れておく。最終的なコードは以下のようになる。
2.x=π/2として,第二項までコーディングする
コーディングをする上で小さな値から始めることや具体的な値で試行錯誤することは重要だと思う。(個人の見解)。この記事でもその理念に則り$x=\pi/2$として第二項まで,つまり
$$
sinx=x-\frac{x^3}{3!}
$$
までコーディングする。import Foundation var sum = 0.0 //int型とdouble型は足算できないので,0.0と書きdouble型にする。 var x = Double.pi/2 sum += x sum += -(x*x*x)/Double(3*2*1)
sum
に$x$を加え,次の行で$-\frac{x^3}{3!}$を加えている。最後に真値と比較する
import Foundation var sum = 0.0 //int型とdouble型は足算できないので,0.0と書きdouble型にする。 var x = Double.pi/2 sum += x sum += -(x*x*x)/Double(3*2*1) print(sum)//近似値0.9248322292886504 print(sin(Double.pi/2))//真値1.0
Double(3*2*1)
としているのはInt型をDoule型に変換しているのだが,この記事では本質的な部分ではないので説明は割愛。3.一般化しやすいように工夫する
さっきは第二項を
-(x*x*x)/3*2*1
としたが,これでは項数が増えるたびに$x$を増やさなければならない。項数が増えても短くコーディングすることができるように工夫を加える。import Foundation var sum = 0.0 //int型とdouble型は足算できないので,0.0と書きdouble型にする。 var x = Double.pi/2 sum += x for i in 2 ..< 3{ x *= -(x*x)/Double((2*(i-1))*(2*i-1)) sum += x print(x) } print(sum)//近似値0.9248322292886504 print(sin(Double.pi/2))//真値1.0
for
を使い,項と変数i
を対応させた。(1回しか実行されないが。)この場合第二項のみしか行わないので,for i in 2 ..< 3
としている。一般化しているが,計算としては同じであるので2で出した答えと同じになるはずだ。4.項数を増やす
一般化することによって項数を増やしても対応できる。
import Foundation var sum = 0.0 //int型とdouble型は足算できないので,0.0と書きdouble型にする。 let x = Double.pi/2 var item = x sum += item for i in 2 ..< 6{ item *= -(x*x)/Double((2*(i-1))*(2*i-1)) sum += item } print(sum)//近似値1.00000035425842861 print(sin(Double.pi/2))//真値1.0第6項までの近似,つまり
$$
sinx= x-\frac{x^3}{3!}+\frac{x^5}{5!}-\frac{x^7}{7!}+\frac{x^9}{9!}-\frac{x^{11}}{11!}
$$
ではほとんど誤差が無いことがわかる。
- 投稿日:2020-10-14T10:32:44+09:00
[Swift5]"IBM Watson ToneAnalyzer"を使用して感情分析を行う
投稿のポイント
個人アプリ開発の機能として
IBM Watson ToneAnalyzer
を用いて感情分析機能
を実装したいので、私なりに公式ドキュメントを参考にしながら実装してみました。今回アウトプットするのは初期段階のAPIキーの取得
と、実際に分析を行い、分析結果を表示する
といったところです。APIキーの取得
まず、
Watson IBM Cloud
のアカウントを作成します。下記urlから公式ページに遷移し、右上にある登録
からアカウントを作成して下さい。
https://cloud.ibm.com/developer/watson/services
登録が完了したら下記画像のようにWatsonサービスのToneAnalyze
を選択してください。
続いて、リージョンを東京、プランのライトを選択して作成をクリックします。
すると専用ページに遷移するので資格情報の取得を選択し、APIキー
とURL
を取得します。この時点で
APIキー
とURL
をプロジェクトに記述し、コメントアウトしておくことをオススメします!ToneAnalyzerをインポート
APIキーの取得はできたので、ここからコードの実装に参りたいと思います。まず、Cocoapodsを使用して
ToneAnalyzer
をインポートします。まだ、podsをインストールしたことがないという方は下記urlを参考にしてください。
https://qiita.com/ShinokiRyosei/items/3090290cb72434852460それではpodのインストールを行います。
Podfile.target 'アプリ名' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for アプリ名 pod 'IBMWatsonToneAnalyzerV3', '~> 3.6.0' #ここを記述 end次に、フレームワークを使用したいコントローラーにインポートします。
ここで注意していただきたいのは、IBMWatsonプレフィックスとバージョンサフィックスを除外します。
IBMWatsonToneAnalyzerV3
の、IBMWatson
と、V3
を削除してimport ToneAnalyzer
と記述するということです。APIの認証
フレームワークのインポートが完了したら次は認証フェーズです。スタンダートな方法として、
IBM Cloud Identity and Access Management(IAM)
を使用し、APIキーとAPIのversionとURLを定義します。ViewController.swiftimport UIKit import ToneAnalyzer class ViewController: UIViewController { //WatsonAPIキーを定義 let authenticator = WatsonIAMAuthenticator(apiKey: "ここに取得したAPIキーを添付") override func viewDidLoad() { super.viewDidLoad() //WatsonAPIのversionとURLを定義 let toneAnalyzer = ToneAnalyzer(version: "2017-09-21", authenticator: authenticator) toneAnalyzer.serviceURL = "ここに取得したURLを添付" } }ここで注意点ですが、
serviceURL
は APIを作成した時に設定した場所によって変化します。下記に列挙しているので、設定した場所に合わせてtoneAnalyzer.serviceURL
に代入してあげてください。場所別のサービスURL ダラス: https://api.us-south.tone-analyzer.watson.cloud.ibm.com ワシントンDC: https://api.us-east.tone-analyzer.watson.cloud.ibm.com フランクフルト: https://api.eu-de.tone-analyzer.watson.cloud.ibm.com ロンドン: https://api.eu-gb.tone-analyzer.watson.cloud.ibm.com ソウル: https://api.kr-seo.tone-analyzer.watson.cloud.ibm.com シドニー: https://api.au-syd.tone-analyzer.watson.cloud.ibm.com Tokyo: https://api.jp-tok.tone-analyzer.watson.cloud.ibm.comエラー処理とデータ処理
続いてエラー処理とデータ処理を記述します。と、その前に分析に使用するサンプルテキストを宣言します。
ViewController.swiftimport UIKit import ToneAnalyzer class ViewController: UIViewController { //WatsonAPIキーを定義 let authenticator = WatsonIAMAuthenticator(apiKey: "ここに取得したAPIキーを添付") //分析用サンプルテキスト let sampleText = """ Team, I know that times are tough! Product \ sales have been disappointing for the past three \ quarters. We have a competitive product, but we \ need to do a better job of selling it! """ override func viewDidLoad() { super.viewDidLoad() //WatsonAPIのversionとURLを定義 let toneAnalyzer = ToneAnalyzer(version: "2017-09-21", authenticator: authenticator) toneAnalyzer.serviceURL = "ここに取得したURLを添付" } }それでは定義した
sampleText
を使って分析を行います。switch文で条件分岐を行い、ステータスコードによって表示される内容を変更します。ステータスコードの200範囲は成功、400範囲は障害、500範囲は内部システムエラーを表しております。ViewController.swiftimport UIKit import ToneAnalyzer class ViewController: UIViewController { //WatsonAPIキーを定義 let authenticator = WatsonIAMAuthenticator(apiKey: "q6GL14WCXtIbNgwYazVmBDNGlyd3jmxglni-pmk96g0z") //分析用サンプルテキスト let sampleText = """ Team, I know that times are tough! Product \ sales have been disappointing for the past three \ quarters. We have a competitive product, but we \ need to do a better job of selling it! """ override func viewDidLoad() { super.viewDidLoad() //WatsonAPIのversionとURLを定義 let toneAnalyzer = ToneAnalyzer(version: "2017-09-21", authenticator: authenticator) toneAnalyzer.serviceURL = "https://api.jp-tok.tone-analyzer.watson.cloud.ibm.com" //エラー処理 toneAnalyzer.tone(toneContent: .text(sampleText)){ #ここでsampleTextを定義 response, error in if let error = error { switch error { case let .http(statusCode, message, metadata): switch statusCode { case .some(404): // Handle Not Found (404) exceptz1zion print("Not found") case .some(413): // Handle Request Too Large (413) exception print("Payload too large") default: if let statusCode = statusCode { print("Error - code: \(statusCode), \(message ?? "")") } } default: print(error.localizedDescription) } return } //データ処理 guard let result = response?.result else { print(error?.localizedDescription ?? "unknown error") return } print(result) //ステータスコードの表示(200範囲は成功、400範囲は障害、500範囲は内部システムエラー) print(response?.statusCode as Any) //ヘッダーパラメータ print(response?.headers as Any) } } }実行
ここまで記述することができたらビルドを行い、実際に分析情報を引っ張ってこれるかの確認を行います。
デバックエリアに下記のように分析結果が表示されれば成功です。ToneAnalysis(documentTone: ToneAnalyzer.DocumentAnalysis(tones: Optional([ToneAnalyzer.ToneScore(score: 0.6165, toneID: "sadness", toneName: "Sadness"), ToneAnalyzer.ToneScore(score: 0.829888, toneID: "analytical", toneName: "Analytical")]), toneCategories: nil, warning: nil), sentencesTone: Optional([ToneAnalyzer.SentenceAnalysis(sentenceID: 0, text: "Team, I know that times are tough!", tones: Optional([ToneAnalyzer.ToneScore(score: 0.801827, toneID: "analytical", toneName: "Analytical")]), toneCategories: nil, inputFrom: nil, inputTo: nil), ToneAnalyzer.SentenceAnalysis(sentenceID: 1, text: "Product sales have been disappointing for the past three quarters.", tones: Optional([ToneAnalyzer.ToneScore(score: 0.771241, toneID: "sadness", toneName: "Sadness"), ToneAnalyzer.ToneScore(score: 0.687768, toneID: "analytical", toneName: "Analytical")]), toneCategories: nil, inputFrom: nil, inputTo: nil), ToneAnalyzer.SentenceAnalysis(sentenceID: 2, text: "We have a competitive product, but we need to do a better job of selling it!", tones: Optional([ToneAnalyzer.ToneScore(score: 0.506763, toneID: "analytical", toneName: "Analytical")]), toneCategories: nil, inputFrom: nil, inputTo: nil)])) Optional(200) Optional(["x-service-build-number": "2020-10-12T05:02:12", "x-dp-watson-tran-id": "e7f11a38-2089-4860-8414-7f1e3878f449", "Content-Security-Policy": "default-src \'none\'", "x-xss-protection": "1; mode=block", "x-service-api-version": "null; 2017-09-21", "x-global-transaction-id": "e7f11a38-2089-4860-8414-7f1e3878f449", "Date": "Wed, 14 Oct 2020 01:20:56 GMT", "Server": "watson-gateway", "x-powered-by": "Servlet/3.1", "X-EdgeConnect-Origin-MEX-Latency": "212", "Content-Language": "en-US", "x-request-id": "e7f11a38-2089-4860-8414-7f1e3878f449", "Access-Control-Allow-Origin": "*", "Content-Length": "726", "Cache-Control": "no-store", "Content-Type": "application/json", "Pragma": "no-cache", "Connection": "keep-alive", "Strict-Transport-Security": "max-age=31536000; includeSubDomains;", "X-EdgeConnect-MidMile-RTT": "7", "x-content-type-options": "nosniff"])
ViewController.swift
で記述したprintの内容がしっかりと表示されていますね。
ステータスコードも(200)
と表示されているので成功ということです。ViewController.swiftprint(result) //ステータスコードの表示(200範囲は成功、400範囲は障害、500範囲は内部システムエラー) print(response?.statusCode as Any) //ヘッダーパラメータ print(response?.headers as Any)これで実際にアプリケーションに組み込む実装はまだですが、初期段階の
APIキーの取得
と、実際に分析を行い、分析結果を表示する
という点に関して実装できました!最後に
引き続き個人アプリの開発に取り組みながらアウトプットを行いますので、参考にしていただけると幸いです。最後までご覧いただきありがとうございました!
- 投稿日:2020-10-14T03:15:49+09:00
【Swift5】Firebaseで会員登録の実装
はじめに
Firebaseとチャット機能の学習としてLINEのクローンアプリを作成しているのですが、その際のFirebaseを用いた会員登録の実装を備忘録として投稿します。
初学者ですので訂正点ございましたら、ご指摘よろしくお願いします。概要
今回、会員登録機能を実装するにあたり下記のように順序立てしました。
1.FirebaseAuth
へユーザー登録
2.FirebaseStorage
へプロフィール画像を登録
3.FirebaseFirestore
へユーザー情報を登録
メールアドレス
、パスワード
、ユーザーネーム
の全てに記入がされていれば登録処理可(新規登録ボタン有効)としています。1.
の処理の際に、承認メールを使い会員登録を完了させてから2.
の処理に移るのがよくある手法かと思われますが、今回の目的が学習のためでしたのでその辺りは考慮しておりません。実行環境
【Xcode】Version 12.0.1
【Swift】Version 5.3
【CocoaPods】version 1.9.3
【Firebase】version 6.29.0実装後の画面
実装コード
SignUpModel.swiftimport Foundation import Firebase //delegateはweak参照したいため、classを継承する protocol SignUpModelDelegate: class { func createImageToFirestorageAction() func createUserToFirestoreAction(fileName: String?) func completedRegisterUserInfoAction() } class SignUpModel { // delegateはメモリリークを回避するためweak参照する weak var delegate: SignUpModelDelegate? func createUser(email: String, password: String) { // FirebaseAuthへ保存 Auth.auth().createUser(withEmail: email, password: password) { (res, err) in if let err = err { print("FirebaseAuthへの保存に失敗しました。\(err)") // ユーザー情報の登録が失敗した時の処理 return } print("FirebaseAuthへの保存に成功しました。") // FirebaseAuthへ保存完了 -> FirebaseStorageへ保存処理 self.delegate?.createImageToFirestorageAction() } } func creatrImage(fileName: String, uploadImage: Data) { // FirebaseStorageへ保存 let storageRef = Storage.storage().reference().child("profile_image").child(fileName) storageRef.putData(uploadImage, metadata: nil) { (metadate, err) in if let err = err { print("Firestorageへの保存に失敗しました。\(err)") // ユーザー情報の登録が失敗した時の処理 return } print("Firestorageへの保存に成功しました。") // FirebaseStorageへ保存完了 -> FirebaseFirestoreへ保存処理 self.delegate?.createUserToFirestoreAction(fileName: fileName) } } func createUserInfo(uid: String, docDate: [String : Any]) { // FirebaseFirestoreへ保存 Firestore.firestore().collection("users").document(uid).setData(docDate as [String : Any]) { (err) in if let err = err { print("Firestoreへの保存に失敗しました。\(err)") // ユーザー情報の登録が失敗した時の処理 return } print("Firestoreへの保存に成功しました。") // ユーザー情報の登録が完了した時の処理 self.delegate?.completedRegisterUserInfoAction() } } }SignUpViewController.swiftimport UIKit import Firebase import FirebaseStorage import IQKeyboardManagerSwift class SignUpViewController: UIViewController { @IBOutlet weak var profileImageButton: UIButton! @IBOutlet weak var emailTextField: UITextField! @IBOutlet weak var passwordTextField: UITextField! @IBOutlet weak var userNameTextField: UITextField! @IBOutlet weak var signUpButton: UIButton! let signUpModel = SignUpModel() override func viewDidLoad() { super.viewDidLoad() IQKeyboardManager.shared.enable = true emailTextField.delegate = self passwordTextField.delegate = self userNameTextField.delegate = self signUpModel.delegate = self // 画面UIについての処理 setupUI() } // 画面UIについての処理 func setupUI() { signUpButton.layer.cornerRadius = 3 signUpButton.isEnabled = false profileImageButton.layer.masksToBounds = true profileImageButton.layer.cornerRadius = 75 profileImageButton.layer.borderColor = UIColor.lightGray.cgColor profileImageButton.layer.borderWidth = 0.1 } // プロフィール画像の選択(フォトライブラリーへ遷移) @IBAction func profileImageButtonAction(_ sender: Any) { let imagePickerController = UIImagePickerController() imagePickerController.allowsEditing = true imagePickerController.delegate = self self.present(imagePickerController, animated: true, completion: nil) } // 新規登録処理 @IBAction func signUpButtonAction(_ sender: Any) { guard let email = emailTextField.text, let password = passwordTextField.text else { return } // FirebaseAuthへ保存 signUpModel.createUser(email: email, password: password) } # ・・・省略・・・ // プロフィール画像をFirebaseStorageへ保存する処理 private func createImageToFirestorage() { // プロフィール画像が設定されている場合の処理 if let image = self.profileImageButton.imageView?.image { let uploadImage = image.jpegData(compressionQuality: 0.5) let fileName = NSUUID().uuidString // FirebaseStorageへ保存 signUpModel.creatrImage(fileName: fileName, uploadImage: uploadImage!) } else { print("プロフィール画像が設定されていないため、デフォルト画像になります。") // User情報をFirebaseFirestoreへ保存 self.createUserToFirestore(profileImageName: nil) } } // User情報をFirebaseFirestoreへ保存する処理 private func createUserToFirestore(profileImageName: String?) { guard let email = Auth.auth().currentUser?.email, let uid = Auth.auth().currentUser?.uid, let userName = self.userNameTextField.text else { return } // 保存内容を定義する(辞書型) let docData = ["email": email, "userName": userName, "profileImageName": profileImageName, "createdAt": Timestamp()] as [String : Any?] // FirebaseFirestoreへ保存 signUpModel.createUserInfo(uid: uid, docDate: docData as [String : Any]) } } extension SignUpViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate { // 写真が選択された時に呼ばれるメソッド func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { if let editedImage = info[.editedImage] as? UIImage { profileImageButton.setImage(editedImage.withRenderingMode(.alwaysOriginal), for: .normal) } else if let originalImage = info[.originalImage] as? UIImage { profileImageButton.setImage(originalImage.withRenderingMode(.alwaysOriginal), for: .normal) } dismiss(animated: true, completion: nil) } } extension SignUpViewController: UITextFieldDelegate { // textFieldでテキスト選択が変更された時に呼ばれるメソッド func textFieldDidChangeSelection(_ textField: UITextField) { // textFieldが空かどうかの判別するための変数(Bool型)で定義 let emailIsEmpty = emailTextField.text?.isEmpty ?? true let passwordIsEmpty = passwordTextField.text?.isEmpty ?? true let userNameIsEmpty = userNameTextField.text?.isEmpty ?? true // 全てのtextFieldが記入済みの場合の処理 if emailIsEmpty || passwordIsEmpty || userNameIsEmpty { signUpButton.isEnabled = false signUpButton.backgroundColor = UIColor.systemGray2 } else { signUpButton.isEnabled = true signUpButton.backgroundColor = UIColor(named: "lineGreen") } } // textField以外の部分を押したときキーボードが閉じる override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { self.view.endEditing(true) } } extension SignUpViewController: SignUpModelDelegate { // FirebaseAuthへ保存完了 -> FirebaseStorageへ保存処理 func createImageToFirestorageAction() { print("FirebaseAuthへの保存に成功しました。") self.createImageToFirestorage() } // FirebaseStorageへ保存完了 -> FirebaseFirestoreへ保存処理 func createUserToFirestoreAction(fileName: String?) { print("Firestorageへの保存に成功しました。") self.createUserToFirestore(profileImageName: fileName) } // ユーザー情報の登録が完了した時の処理 func completedRegisterUserInfoAction() { // ChatListViewControllerへ画面遷移 let storyboard = UIStoryboard(name: "ChatList", bundle: nil) let chatListVC = storyboard.instantiateViewController(withIdentifier: "ChatListVC") as! ChatListViewController let nav = UINavigationController(rootViewController: chatListVC) nav.modalPresentationStyle = .fullScreen nav.modalTransitionStyle = .crossDissolve self.present(nav, animated: true, completion: nil) } }Firebaseに関しての処理にフォーカスを当てていますので下記項目の説明は省かせていただきます。
- エラー処理について
UIActivityIndicatorView
についてUIImagePickerController
についてIQKeyboardManagerSwift
について(詳細はこちら参照ください。)準備
①前提条件
下記項目が完了しているのを前提条件としまして進めていきます。
- Firebaseプロジェクトの作成
- XcodeでのFirebaseSDK組み込み
このあたりがまだという方は、ドキュメントを参照よろしくお願いします。
②Xcode側の準備
Podfile
に以下を追加して、ターミナルでpod install
をします。pod 'Firebase/Analytics' pod 'Firebase/Auth' pod 'Firebase/Core' pod 'Firebase/Firestore' pod 'Firebase/Storage' pod 'FirebaseUI/Storage'③Firebase側の準備
上図のようにSign-in method
タブからメール/パスワード
を選択します。鉛筆のアイコンで編集画面を開きます。
有効にしたら保存します。準備としてはこれで終わりになります。実装詳細①(FirebaseAuth編)
SignUpViewController.swift// 新規登録処理 @IBAction func signUpButtonAction(_ sender: Any) { guard let email = emailTextField.text, let password = passwordTextField.text else { return } // FirebaseAuthへ保存 signUpModel.createUser(email: email, password: password) }SignUpModel.swiftfunc createUser(email: String, password: String) { // FirebaseAuthへ保存 Auth.auth().createUser(withEmail: email, password: password) { (res, err) in if let err = err { print("FirebaseAuthへの保存に失敗しました。\(err)") // ユーザー情報の登録が失敗した時の処理 return } print("FirebaseAuthへの保存に成功しました。") // FirebaseAuthへ保存完了 -> FirebaseStorageへ保存処理 self.delegate?.createImageToFirestorageAction() } }実装詳細②(FirebaseStorage編)
SignUpViewController.swift// FirebaseAuthへ保存完了 -> FirebaseStorageへ保存処理 func createImageToFirestorageAction() { print("FirebaseAuthへの保存に成功しました。") self.createImageToFirestorage() }SignUpViewController.swift// プロフィール画像をFirebaseStorageへ保存する処理 private func createImageToFirestorage() { // プロフィール画像が設定されている場合の処理 if let image = self.profileImageButton.imageView?.image { // 画像を圧縮 let uploadImage = image.jpegData(compressionQuality: 0.5) // ユニークなIDを取得 let fileName = NSUUID().uuidString // FirebaseStorageへ保存 signUpModel.creatrImage(fileName: fileName, uploadImage: uploadImage!) } else { print("プロフィール画像が設定されていないため、デフォルト画像になります。") // User情報をFirebaseFirestoreへ保存 self.createUserToFirestore(profileImageName: nil) } }SignUpModel.swiftfunc creatrImage(fileName: String, uploadImage: Data) { // FirebaseStorageへ保存 let storageRef = Storage.storage().reference().child("profile_image").child(fileName) storageRef.putData(uploadImage, metadata: nil) { (metadate, err) in if let err = err { print("Firestorageへの保存に失敗しました。\(err)") // ユーザー情報の登録が失敗した時の処理 return } print("Firestorageへの保存に成功しました。") // FirebaseStorageへ保存完了 -> FirebaseFirestoreへ保存処理 self.delegate?.createUserToFirestoreAction(fileName: fileName) } }実装詳細③(FirebaseFirestore編)
SignUpViewController.swift// FirebaseStorageへ保存完了 -> FirebaseFirestoreへ保存処理 func createUserToFirestoreAction(fileName: String?) { print("Firestorageへの保存に成功しました。") self.createUserToFirestore(profileImageName: fileName) }SignUpViewController.swift// User情報をFirebaseFirestoreへ保存する処理 private func createUserToFirestore(profileImageName: String?) { guard let email = Auth.auth().currentUser?.email, let uid = Auth.auth().currentUser?.uid, let userName = self.userNameTextField.text else { return } // 保存内容を定義する(辞書型) let docData = ["email": email, "userName": userName, "profileImageName": profileImageName, "createdAt": Timestamp()] as [String : Any?] // FirebaseFirestoreへ保存 signUpModel.createUserInfo(uid: uid, docDate: docData as [String : Any]) }SignUpModel.swiftfunc createUserInfo(uid: String, docDate: [String : Any]) { // FirebaseFirestoreへ保存 Firestore.firestore().collection("users").document(uid).setData(docDate as [String : Any]) { (err) in if let err = err { print("Firestoreへの保存に失敗しました。\(err)") // ユーザー情報の登録が失敗した時の処理 return } print("Firestoreへの保存に成功しました。") // ユーザー情報の登録が完了した時の処理 self.delegate?.completedRegisterUserInfoAction() } }SignUpViewController.swift// ユーザー情報の登録が完了した時の処理 func completedRegisterUserInfoAction() { // ChatListViewControllerへ画面遷移 # ・・・省略・・・ }参考