- 投稿日:2020-12-08T22:50:48+09:00
演算子について【Swift】
はじめに
初歩的な演算子についての備忘録になります。
比較演算子
例 等しい a == b 等しくない a != b 大なり a > b 小なり a < b より以上 a >= b より以下 a <= b 論理演算子
例 論理否定 !a 論理積 a && b 論理和 a ll b 複合代入演算子
変数の値を更新したいときに使う(整数のみ)
例 内容 += a += b a = a + b -= a -= b a = a - b *= a *= b a = a * b /= a /= b a = a / b %= a %= b a = a % b 自然数列の和を求める例
var sum = 0 for i in 1...100 { sum += i } print(sum) // 5050三項演算子
簡単な場合分けによる値代入
書式
条件 ? trueの値 : falseの値簡単な場合分けの例
let a = 30 let b = 60 var winner = "勝者は" // if文での条件分岐 if a > b { winner += "Aさん" } else { winner += "Bさん" } print(winner) // 勝者はBさんlet a = 30 let b = 60 var winner = "勝者は" // 三項演算子 winner += a > b ? "Aさん" : "Bさん" print(winner) // 勝者はBさん
- 5行が1行にまとまりスッキリとなりました。
- 三項演算子の気をつける点としまして、半角スペースを置かずに「?」をつけると「オプショナル型の宣言」と解釈されエラーになりますのでご注意下さい。
- 投稿日:2020-12-08T21:43:14+09:00
【Swift】これでワカッタ!なるほど!オプショナル型ってそーゆーこと
というわけで
つづき。
前回はこれを見てね。【Swift】オプショナル型の何が嬉しいのか
https://qiita.com/antk/items/2d780f152c26a3733109今回はこのオプショナル型を活用してみる。
ラップ
ラップとは?
とりあえず置いといて、前回「nilが使える」ということで定義したオプショナル型。var hoge: String?この
hoge
というオプショナル型の変数はそのままではいじることができない。
どういうことか?
上記のように文字列型(String)だとわかりにくいので、例えばvar number: Int? = 2という数値型(Int)のオプショナル型の変数を定義して、こいつに
2
を入れている。例えばこれを出力するとprint(number) // 出力 Optional(2)という結果が出る。
逆に、?
を付けない非オプショナル型だとvar number: Int = 2 print(number) // 出力 2さっきの
Optional
が付いていない、ただの2
が出力される
非オプショナル型と違い、オプショナル型で定義されているのはただの2
じゃないのである。
paiza.ioとかで試してみるといい。
これはなんなのかと言うと、Optional
という殻、カプセル、シェルター、バリアー……まあなんでもいいが、オプショナル型は要素がそういうものに入っていると考える。
これを『ラップ(wrap)されている』という。
ラップされているnumber
は殻に入っているのでvar number: Int? = 2 print(number + 4) // エラーこのように数字を足して
6
にしようとしてもOptionatl
の殻に弾かれてしまうのでうまくいかないのである。
これをやるには殻を解く、アンラップという作業が必要になる。アンラップ
オプショナル型変数の後ろに
!
を付ける。var number: Int? = 2 print(number! + 4) // 出力 6こうすることで望みの結果が出た。
全然関係ないがデーモン小暮閣下は昔『!(エクスクラメイション)』という名義でアルバムを出していた。
- 投稿日:2020-12-08T20:51:19+09:00
AtCoder過去問を使って新人にSwiftを練習させてみると良かった
はじめに
チームに入ってきた新人が、Swift/iOS開発をN予備校などでやったものの、
関数もかけない状況というのが判明しました。AtcoderでSwiftを練習させてみたので振り返りです。
訓練のフォーマット
- A問題を5問とく。B問題を1問とく。
- 全部完答したら、ACした他の人の回答をみる。初めてみるAPIを調べる。
- 自チームのプロジェクトでそのAPIを全文検索して、何をしているか議論してみる。
A問題を5問とく、B問題を1問とく
A問題を説いていくことで、程よい達成感を得てもらい、慣れたらB問題で少し考える問題にも挑戦してもらいます。
問題を説いてSwiftをガシガシ書いてもらいたいというフェーズです。
やってない問題はAtCoder Problemsで確認します。全部完答したら、ACした他の人の回答をみる。初めてみるAPIを調べる。
他の人の回答が見られるのは、AtCoderの良さの1つだと思います。
提出結果の全ての提出からSwift
を選択して見ていきます。(Swiftは人少ない!w)自分の書いた答えと、他の人が書いたコードを比べてみてもらいます。
その中で、自分の見たことのないAPIを調べてもらいます。(僕が説明したものもあります)
新人が見たことのないやつというのは、map
やfilter
、reduce
などです。自チームのプロジェクトでそのAPIを全文検索して、何をしているか議論してみる。
学んだAPIがちゃんとプロジェクトでも使われているよ!ということを知ってもらうために検索してもらいました。
使い方やコードリーディングの練習のためです。ここはどのように使われているかなど解説したり議論しました。
AtCoderでの落とし穴
Swiftが最初のA - Welcome to AtCoderという問題の例にありません!
初心者が解くのは難しいので、以下のコードを教えてあげる必要があります。
[AtCoder]Swiftでも競プロがしたい! - Qiitaより引用// 標準入力からIntを読み込む let a = Int(readLine()!)! // 標準入力からInt配列を読み込む let bc = readLine()!.split(separator: " ").map { Int($0)! } // 標準入力から文字列を読み込む let s = readLine()! // bcは配列なので、bc[0]にb bc[1]にcが入っている let sum = a + bc[0] + bc[1] // スペース区切りで出力 print(sum, s)AtCoderでの初心者教育のよかった点
- ちょうどいい問題の大きさで、プログラミングで問題を解く楽しさがわかる
- 他の人のコードを見て学べる
- 業務外でも勉強するきっかけになる (ちゃんと勉強しているらしい!)
- 仕様を読んで、実装する訓練になる
実務でも重要な点が詰まってますね!
おわりに
初心者がプログラミングの教本や入門動画を見ただけだと、いざ書いてみると全くできないことが多いと思います。
競技プログラミングでは、読んで、理解して、プログラムという流れが細かいリズムで体験できるので、プログラミングに慣れるのに適していると感じました。
- 投稿日:2020-12-08T20:42:17+09:00
複数のmlmodelを統一的に扱う
複数のmlmodelを統一的に扱う
はじめに
僕は機械学習系の研究室に所属している大学院生で、普段はPythonを用いてコードを書いています。また、Apple信者でもあるのでSwiftでアプリを作ることもあります。
SwiftにはCore ML1というApple製の機械学習ライブラリが存在します。Core MLは、mlmodelファイルをプロジェクトにドラッグ&ドロップするだけでモデルを使うためのコードが自動生成されるため、とても便利です。一方で、複数のモデルを扱うときに少し不便なことがあります。
機械学習系の研究をする際、複数の機械学習モデルを比較することが大いにあると思います。Pythonの場合は、
models = { "Random Forest": RandomForestClassifier(), "SVM": SVC(), "VGG16": ... } for model in models: model.fit() outputs = model.predict() ...のようにして、辞書(もしくは配列)にしておくことでfor文で複数のモデルを実行することができます。
しかし、Swiftの場合、静的型付けであるため動的型付けであるPythonのように違うクラス・型のものを辞書や配列に入れて扱うのは難しいと思います。そこで今回は、複数のCore MLモデル(mlmodel)を比較したり切り替えたいというときに、コードを共通化する方法を記事にしたいと思います。
(今回は、Visionを用いたものではなくMLMultiArray2を入力とするCore MLモデルを想定しています)
環境
- Xcode 12.2
- macOS 11.0.1
mlmodelを統一的に扱う
プロトコルを作る
MyModel.mlmodel
ファイルをXcodeにドラッグ&ドロップすると自動的にSwiftのコードが生成され、MyModelInput
、MyModelOutput
、MyModel
の3つのクラスができます。MyModelInput
とMyModelOutput
はMLFeatureProvider
3というプロトコルに準拠していますが、MyModel
はMLModel
を持っているだけで単体のクラスとして生成されます。そこで、プロトコルを作成し
MyModel
を拡張して準拠させることで統一したルールで扱うことができるようにします。また、生成されたコードのprediction
の戻り値はMyModelOutput
でありmlmodelごとに異なるため、prediction
の戻り値も統一的に扱えるようにします。今回は戻り値をStringにしましたが、クラスラベルの列挙型を作成しても良いと思います。protocol MLModelUnification { func prediction(input: MLMultiArray) throws -> String func predictions(inputs: [MLMultiArray]) throws -> [String] }モデルのクラスを拡張する
次に作成したプロトコルをモデルのクラスに準拠させるためにモデルのクラスを拡張します。
VGG16.mlmodel
とResNet50.mlmodel
の2つのモデルがあった場合の例を示します。2つのモデルの出力にあたるVGG16Output
とResNet50Output
は、
classLabel: String
: 予測ラベルIdentity: [String : Double]
: 各ラベルの予測確率を持っているとします。
VGG16のextensionextension VGG16: MLModelUnification { func prediction(input: MLMultiArray) throws -> String { let output = try self.prediction(input: VGG16Input(input: input)) return output.classLabel } func predictions(inputs: [MLMultiArray]) throws -> [String] { var results: [String] = [] try inputs.forEach { (input) in let output = try self.prediction(input: VGG16Input(input: input)) results.append(output.classLabel) } return results } }ResNet50のextensionextension ResNet50: MLModelUnification { func prediction(input: MLMultiArray) throws -> String { let output = try self.prediction(input: ResNet50Input(input: input)) return output.classLabel } func predictions(inputs: [MLMultiArray]) throws -> [String] { var results: [String] = [] try inputs.forEach { (input) in let output = try self.prediction(input: ResNet50Input(input: input)) results.append(output.classLabel) } return results } }複数のモデルで共通のコードを使うことができる
このようにプロトコルを作成しそれぞれのモデルのクラスを準拠させることで、予測部分のコードを共通化することができます。
共通化の例(モデルを比較)var models: [MLModelUnification] = [ { do { return try VGG16(configuration: config) } catch { fatalError("Couldn't create VGG16") } }(), { do { return try ResNet18(configuration: config) } catch { fatalError("Couldn't create ResNet 18") } }() ] for model in models { let outputs = try model.prediction(inputs: inputs) ... }共通化の例(モデルの切り替え)var model: MLModelUnification // モデルを列挙したenumでswitch文 switch selectedModel { case .vgg16: model_ = { do { return try VGG16(configuration: config) } catch { print(error) fatalError("Couldn't create VGG16") } }() case .resnet18: model_ = { do { return try ResNet18(configuration: config) } catch { print(error) fatalError("Couldn't create ResNet 18") } }() } let output = try model.prediction(input: input) ...おわりに
今回は、Core MLモデル(mlmodel)で生成される機械学習モデルのクラスを統一的に扱う方法についてまとめました。個人的に、Swiftの便利なところの1つは
extension
だなぁと思いました。同じようなことをしようとしている記事を見かけなかったので、CoreML上では複数のモデルを切り替えたり比較することがあまりないかも知れませんが、もし同じようなことがしたい方がいれば参考にしてくれると嬉しいです。M1 Mac mini欲しさに、「ちょっとした工夫で効率化!【PR】パソナテック Advent Calendar 2020」に向けて記事を書きました。初めてQiita記事を書いたため、拙い部分もあるかと思いますが、ここまで読んでくれてありがとうございました。
参考記事
- Swiftのプロトコル
- MLMultiArrayの初期化の仕方: How to initialize a MLMultiArray in CoreML
- 投稿日:2020-12-08T17:02:14+09:00
繰り返し文について〜while文〜
while文 とは
while文は、条件式が成り立つ限り繰り返される制御構文になります。
while文は条件式と繰り返し実行される文から成り立ちます。
while 条件式 { 条件式が成立する間、繰り返される処理 }while文の条件式は、Bool型を返す必要があります。
条件式は実行文の実行前に毎回評価され、条件式の結果がtrueの場合は継続処理を行います。
結果がfalseの場合は繰り返しを終了し、while文全体の処理を停止します。次のように条件式の
a < 4
が満たされる間は{ }内の処理を実行し続けます。
aの値が4になった時に条件式の結果はfalseを返すのでwhile文を終了します。var a = 0 while a < 4 { print(a) a += 1 } 実行結果 0 1 2 3repeat-while文
while文は、実行文の実行前に条件式の確認を行うため、
場合によっては一度も実行されないことがあります。つまり、while文の繰り返し回数は0回以上ということになります。
条件式の成否によらず必ず1回以上の繰り返し処理を行いたい場合には、
repeat-while文を使用します。repeat-while文はwhile文と違い、
実行文の実行後に条件式が評価されます。repeat { 1回は必ず実行され、それ以降は条件式が成立する限り実行される } while 条件式while文とrepeat-while文の違いを比較するために先ほどの例を少し変更し、
条件式の部分をa < 0
に変更しました。while文の場合は、条件式の結果がfalseなので一度も実行されることはなくwhile文が終了します。
var a = 0 while a < 0{ print(a) a += 1 }repeat-while文の場合は、初回実行が約束されているので0が出力されます。
repeat { print(a) a += 1 } while a < 0 実行結果 0while文とrepeat-while文の使い分けについては理解できましたか?
結構単純な処理だと思いますので是非覚えてみてください!以上、最後までご覧いただきありがとうございました。
- 投稿日:2020-12-08T16:46:32+09:00
App Store ConnectでApp Privacy Detailsを登録する
はじめに
- 2020/12/8からアプリのプライバシー情報を申請時に登録が必要になりました。 今後はApp Storeにアプリのプライバシー情報がアプリページに表示されるようになります。 今回は登録時にやったことについてまとめました。
- 詳細はこちら App privacy details on the App Store
項目
やったこと
- でてくる質問に対して、該当するところにチェックを入れていきます。
- 該当するデータタイプを選択していきます
- ここまで終わると該当する項目が一覧で表示されます。
- ここからは各項目の情報の利用目的について、チェックしていきます。
- 各項目はいつでも変更可能になっています。
- 公開ボタンを押すとダイアログが表示され、公開を押すと反映されます
運用方法案
エクセルなどで管理する
- 1番よく使われる方法だと思います。手動で項目を反映さえていくのが結構手間です。
fastlane
- Uploading App Privacy Details
- 設定項目をアップロードするActionが追加されている
- JSONで管理できるので一度つくってしまえば楽だと思いました
upload_app_privacy_details_to_app_store( username: "your@email.com", team_name: "Your Team", app_identifier: "com.your.bundle", json_path: "fastlane/app_privacy_details.json" )[ { "category": "PAYMENT_INFORMATION", "purposes": [ "APP_FUNCTIONALITY" ], "data_protections": [ "DATA_NOT_LINKED_TO_YOU" ] }, { "category": "NAME", "purposes": [ "PRODUCT_PERSONALIZATION", "APP_FUNCTIONALITY" ], "data_protections": [ "DATA_LINKED_TO_YOU", "DATA_USED_TO_TRACK_YOU" ] } ]まとめ
- アプリのプライバシー情報を申請時に必要になりました。
- fastlaneなどを使って、運用を楽にしていきたいと思いました。
参考リンク
- 投稿日:2020-12-08T15:43:18+09:00
swift基礎 UITextfieldsをLabelに表示する.
0.完成イメージ
https://twitter.com/kou09010901lam1/status/1334101860261662720?s=20
1. コード
DeleteButton()
player1.removeAll() player2.removeAll() player3.removeAll() player4.removeAll() player5.removeAll() player6.removeAll() JL.text = "参加者" JL6.text = "参加者" JL2.text = "参加者" JL3.text = "参加者" JL4.text = "参加者" JL5.text = "参加者" NameCount = 1JNNADDButton()
@IBAction func JNADDButton(_ sender: UIButton) { if NameCount == 1 { NameCount = NameCount + 1 JL.text = JNtext.text player1 = JNtext.text! } else if NameCount == 2 { NameCount = NameCount + 1 JL2.text = JNtext.text player2 = JNtext.text! } else if NameCount == 3 { NameCount = NameCount + 1 JL3.text = JNtext.text player3 = JNtext.text! } else if NameCount == 4 { NameCount = NameCount + 1 JL4.text = JNtext.text player4 = JNtext.text! } else if NameCount == 5 { NameCount = NameCount + 1 JL5.text = JNtext.text player5 = JNtext.text! } else if NameCount == 6 { NameCount = NameCount + 1 JL6.text = JNtext.text player6 = JNtext.text! } JNtext.text = "" }2.解説
1~6番までのラベルに表示するためには、Namecountで処理を6回分けることによって、実現しました。
.removeALLは削除のメソッドです
textFieldsはAnyで宣言します。actionで宣言すると、外からの処理がなぜかめんどくさかったのでやめました(何故だ???3.関連記事
[Qlita]
[Qlita]
[Qlita]
[Qlita]4.最後に
ラベルの文字数に制限があって文が省略される!とか細かいところは
attributes inspecter(右側のメニュー
をむちゃくちゃにいじると何故か改善し始める。などもあったので調べる前に全部いじってみる!を心がけました笑
- 投稿日:2020-12-08T15:36:14+09:00
[Swift]APIを使ってみる
表題通りです
APIを使ってみる!
地図をタップしてその位置の天気を取得する!
今回はOpenWeatherMapというAPIを使用しました。
APIキー取得は以下のサイトを参考にしております↓↓
https://auto-worker.com/blog/?p=1612環境
Xcode 12.2
Swift 5.3.1つくります
以前MapKitとCoreLocationを使うという記事で
地図をタップしてピンを立てる&タップ位置の住所を表示するというものを作ったので
そちらを活用して
タップされた位置の経度緯度情報をもとに天気情報を取得してみようと思います。UIは適当です。
適当に天気表示用のUILabelと天気アイコン表示用のUIImageViewをおきます。
通信はAlamofire
JSON解析にはSwiftyJSONを使用しました。import UIKit import MapKit import CoreLocation import SwiftyJSON import Alamofire class TestViewController: UIViewController{ @IBOutlet weak var mapView: MKMapView! @IBOutlet weak var label: UILabel! @IBOutlet weak var weatherLabel: UILabel! @IBOutlet weak var weatherIcon: UIImageView! var latitude:CLLocationDegrees = 0.0 var longitude:CLLocationDegrees = 0.0 let pin = MKPointAnnotation() override func viewDidLoad() { super.viewDidLoad() label.numberOfLines = 0 label.text = "住所" weatherLabel.numberOfLines = 0 //gestureを追加する let addGesture = UITapGestureRecognizer(target: self,action:#selector(didTap(_:))) mapView.addGestureRecognizer(addGesture) //pinを置く mapView.addAnnotation(pin) } @objc func didTap(_ sender:UITapGestureRecognizer){ // UIViewのタップ位置(CGPoint)を取得 let tapPoint: CGPoint = sender.location(in: view) //CGPointから地図上の緯度経度に変換 let locationCoordinate = mapView.convert(tapPoint, toCoordinateFrom: mapView) //タップした地図上の緯度経度を中心に地図を表示させる let region: MKCoordinateRegion = MKCoordinateRegion(center: locationCoordinate, latitudinalMeters: 5000, longitudinalMeters: 5000) mapView.setRegion(region, animated: true) //ピンの経度、緯度 self.pin.coordinate = locationCoordinate //緯度経度→住所に変換(逆ジオコーディング) let latitude = locationCoordinate.latitude let longitude = locationCoordinate.longitude let location = CLLocation(latitude: latitude, longitude: longitude) let geocoder = CLGeocoder() //トレイリングクロージャ(関数の最後の引数がクロージャの場合()の外に{}書ける記法) geocoder.reverseGeocodeLocation(location) { placemarks,error in guard let placemark = placemarks?.first, let postalCode = placemark.postalCode, let administrativeArea = placemark.administrativeArea, let locality = placemark.locality, let thoroughfare = placemark.thoroughfare, let subThoroughfare = placemark.subThoroughfare else { return } //labelに住所表示 self.label.text = "\(postalCode)\n\( administrativeArea)\( locality)\(thoroughfare)\(subThoroughfare)" //経度緯度から天気を取得してweatherLabel,weatherIconに設定 self.getWeather(lat: latitude, lon: longitude) } } func getWeather(lat:CLLocationDegrees,lon:CLLocationDegrees){ let apiKey = "******(APIKey)*******" //lang:jaにするとdescriptionが日本語になる //unitsで温度表記を摂氏に指定。デフォルトだとケルビン let url = "http://api.openweathermap.org/data/2.5/weather?lat=\(lat)&lon=\(lon)&appid=\(apiKey)&lang=ja&units=metric" AF.request(url).responseJSON{ (response) in switch response.result { case .success: let json:JSON = JSON(response.data as Any) let weather = json["weather"][0]["main"].string let weatherIcon = json["weather"][0]["icon"].string let description = json["weather"][0]["description"].string let temp = json["main"]["temp"].int if let weather = weather, let description = description, let temp = temp, let weatherIcon = weatherIcon { self.weatherLabel.text = "\(weather)\n\(description)\n\(temp)℃" self.weatherIcon.image = self.getIconByURL(iconID: weatherIcon) } case .failure: print("エラーです") } } } //URLから天気アイコンを取得 func getIconByURL(iconID: String) -> UIImage{ let url = "http://openweathermap.org/img/wn/\(iconID)@2x.png" let imageURL = URL(string: url) do { let data = try Data(contentsOf: imageURL!) return UIImage(data: data)! } catch { print("icon取得失敗") } return UIImage() } }シミュレーター立ち上げてみます
あれ?取得できてない
んん??
The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.App Transport Security policyってなんですか。
ということで、別記事に対処法まとめました
[Swift]ATS設定
今回は↑こちらの記事の指定するドメインを許可する方法を試しました。
- 投稿日:2020-12-08T13:47:49+09:00
SVProgressHUDのimageにtintColorを反映させないようにする
はじめに
Qiita Advent Calendar 2020 Swift 9日目のエントリーです。
すみません、色々考えたんですが大したこと思いつかなかったので最近やったことについて。
SVProgressHUD
業務でローディングダイアログを実装する際にSVProgressHUDを利用しました。
特にアニメーションの指定などなく、基本的なものでよかったのでほぼ問題なかったんですが
そのまま利用するとtintColorがimageViewにもかかって、画像をそのまま利用しつつ文字色を別のものに変えるというのができませんでした。(※できるのかもしれないのですが、自分ではわかりませんでした。。)なのでforkしてtintColorを画像に反映されないように修正して使用しました。
修正内容
僕の場合はそのあと画像に色を設定する必要がなかったので
「strongSelf.imageView.tintColor = strongSelf.foregroundImageColorForStyle;」
を適用させないようにif文をざっくり削除しました。
画像にもかけたいケースもある場合は分岐の条件を変更させたり必要だと思います。SVProgressHUD.mif (self.shouldTintImages) { if (image.renderingMode != UIImageRenderingModeAlwaysTemplate) { strongSelf.imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; } strongSelf.imageView.tintColor = strongSelf.foregroundImageColorForStyle; } else { strongSelf.imageView.image = image; } ↓ strongSelf.imageView.image = image;実際に使う側での設定
hoge.swift// ダイアログを表示するのに使用 private var requesting: Bool = false { didSet { if requesting { // R.string.localizable.progress_loading()->"読み込み中" SVProgressHUD.show(withStatus: R.string.localizable.progress_loading()) } else { SVProgressHUD.dismiss() } } } private func setupSVProgressHUD() { SVProgressHUD.setForegroundImageColor(.primary) SVProgressHUD.setMinimumSize(CGSize(width: 150, height: 150)) SVProgressHUD.setImageViewSize(CGSize(width: 60, height: 60)) SVProgressHUD.setRingRadius(28) SVProgressHUD.setRingThickness(5) SVProgressHUD.setDefaultMaskType(.clear) }
- 投稿日:2020-12-08T12:05:48+09:00
[Swift]App Transport Security policy
環境
Xcode 12.2
Swift 5.3.1エラー
Swiftでhttpリクエストの際にでたエラー
The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.
原因
App Transport Security(ATS)という安全な通信を行うための機能(制限?)があり
ATSが有効な場合、http通信を行うことができないようです。(httpsはOK)
それでも、http通信がしたい!という場合の対処法を以下でまとめます。対処法
ATS無効化
Info.plist > Information Property List に
App Transport Security settings > Allow Arbitrary Loadsを追加しvalue を "Yes" に変更
この方法だとATSが無効化されるため、すべてのhttp通信が可能となる。
指定ドメインのみ許可
Info.plist > Information Property List > App Transport Security settings (なければ追加する)に
①Exception Domains を追加し、TypeをDictionaryにする
↓
②Exception Domains 配下に
例外にしたいドメインを追加し、TypeをDictionaryにする
↓
③例外にしたいドメイン配下に
NSIncludesSubdomains Type:Boolean Value:Yes
NSTemporaryExceptionAllowsInsecureHTTPLoads Type:Boolean Value:Yes
NSTemporaryExceptionRequiresForwardSecrecy Type:Boolean Value:No
ドメインをさらに追加する場合は②、③を繰り返せばOK
以下のサイトに詳細があります
https://dev.classmethod.jp/articles/ios-ats-cheats-info-plist-settings/
- 投稿日:2020-12-08T12:05:48+09:00
[Swift]ATS設定
環境
Xcode 12.2
Swift 5.3.1エラー
Swiftでhttpリクエストの際にでたエラー
The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.
原因
App Transport Security(ATS)という安全な通信を行うための機能(制限?)があり
ATSが有効な場合、http通信を行うことができないようです。(httpsはOK)
それでも、http通信がしたい!という場合の対処法を以下でまとめます。対処法
ATS無効化
Info.plist > Information Property List に
App Transport Security settings > Allow Arbitrary Loadsを追加しvalue を "Yes" に変更
この方法だとATSが無効化されるため、すべてのhttp通信が可能となる。
指定ドメインのみ許可
Info.plist > Information Property List > App Transport Security settings (なければ追加する)に
①Exception Domains を追加し、TypeをDictionaryにする
↓
②Exception Domains 配下に
例外にしたいドメインを追加し、TypeをDictionaryにする
↓
③例外にしたいドメイン配下に
NSIncludesSubdomains Type:Boolean Value:Yes
NSTemporaryExceptionAllowsInsecureHTTPLoads Type:Boolean Value:Yes
NSTemporaryExceptionRequiresForwardSecrecy Type:Boolean Value:No
ドメインをさらに追加する場合は②、③を繰り返せばOK
以下のサイトに詳細があります
https://dev.classmethod.jp/articles/ios-ats-cheats-info-plist-settings/
- 投稿日:2020-12-08T11:08:37+09:00
クラシルとRxSwiftデビュー
こんにちは!
ずっと気になっていたサウナに先輩デザイナーに連れて行ってもらい完全にハマってしまい、最近は仕事中もサウナに行きたくて堪らない禁断症状が出ている
dely(クラシル)でiOSエンジニアをしています @RyogaBarbie です♨️このnoteは dely Advent Calender #2 の8日目の記事となります。
dely Advent Calendar #1
dely Advent Calendar #2昨日の記事はAndroidチームのマネージャーのうめもりさん(Twitter: @kr9ly) の記事になります!
Androidのビルド用Dockerイメージダイエット計画クラシル
クラシルの凄腕PdMの奥原さんの紹介で今年9月にdelyのiOSエンジニアとして入社しました。
現在はレシピ動画サービスのクラシルの速度改善チームにてiOSを担当してます。そんな奥原さんのAdvent Calendarは こちら
delyに入社した理由はたくさんあるのですが、いくつかあげると
- 料理が好き(食べる)
- 自粛期間で料理作ることが増えた
- 働いてる人が良さそう(知り合いが働いてることで雰囲気もある程度つかめた)
といった感じです。
dely入社前にシェアされていたdelyの資料 これや これに記載されてるように情報がフルオープンの会社になってます。
slackを追えば何がどうなっているかがわかる会社です。
逆にslackのチャンネル多すぎ&流れるの早すぎで、最初は困惑しました!そして、働いてる同僚が良い人しかいない、環境(オープンさ)がいいので心理的安全性高めで働くことができています!
ちょっとでも興味ある方は
Twitterまたは採用ページからぜひ!RxSwift
現在クラシルではRxSwiftを使ったMVVMでの開発を行っています。
おおざっぱに書くと↓のような感じ
Router - View(ViewController) - ViewModel - UseCase - Repository
(細かい話はまたいつか…)前職などではRxSwiftを使わないClosureを使ったMVVMでの開発だったので、RxSwiftを実務で使うのはクラシルが初めてでした。
一定まではClosureを使った開発でも特に辛さを感じないのですが、複数の非同期のAPIを待つなどで辛さが出てくるのでzip
には感動しました。
「細かいRxSwiftの書き方や使い方は実際に実務でいろいろ書かないとわからない」
というのがRxSwiftを使ってみての感想です。RxSwiftとMVVMの実装のStyle設計に関しては@yimajoのRxSwift研究読本シリーズがオススメです。
そしてクラシルではUnioを導入してViewModelのStyleを揃えています。
Viewで発生するInputをViewModelに定義し、ViewControllerでViewModel.input.hoge(())
のように発火させるのは導入当初は違和感しかなく、先輩エンジニアなど雑談したりしましたが、今では書き方統一するメリットの方が大きくUnio良き
という気持ちです。終わりに
明日は弊社CXOの坪田さんの
開発体制をSquad化してきてわかってきたコツと課題
です!そして、delyではエンジニア・デザイナー共に絶賛募集中です!
クラシルやTRILL開発部の情報が以下のリンク先にまとまっています。
採用ページまた、開発メンバーが組織や技術・手法などについてざっくばらんにお話しするオンラインイベントを開催しています。「雰囲気をみてみたい」「聞きたいことがある」などありましたら、ぜひご参加ください!
オンラインイベント
- 投稿日:2020-12-08T10:54:17+09:00
[macOS][cocoa][Swift]なかなか調べるのに時間がかかったりしたのでメモ
忘れないようにメモ
個人的にアプリを作った際に調べたものをメモ.....φ(・∀・*)
スクロールビューの有効/無効を出来るようにする
class MyScrollView: NSScrollView { /// スクロール有効/無効 var isScrollEnabled = true /// scrollWheel /// - Parameter event: override func scrollWheel(with event: NSEvent) { if self.isScrollEnabled { super.scrollWheel(with: event) } } }ESCをショートカットキーで利用する場合
他のアクションに繋いでも動かなくて悩みました。ESCキーはデフォルトで下記アクションが利用されるようで、overrideする事により出来るようになりました。
@IBAction override func cancelOperation(_ sender: Any?) { }presentAsSheet で呼び出したViewのサイズ固定
class MyViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() // サイズを固定 self.preferredContentSize = self.view.frame.size } }NSWindow のフルスクリーン判定
extension NSWindow { // フルスクリーン判定 var isFullScreen: Bool { return self.styleMask.contains(.fullScreen) } }メニューの有効/無効
NSMenuItemValidationプロトコルの実装を使う。
extension ViewController: NSMenuItemValidation { func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { switch (menuItem.action) { // toggleSidebarメニューを条件によって有効/無効にする case #selector(NSSplitViewController.toggleSidebar(_:)): if self.hoge { return false } else { return true } default: return true } } }他にも、あとで追記予定。。。
- 投稿日:2020-12-08T01:43:42+09:00
【Swift5】UITableViewCellで画像や色を変えたのにも関わらず同じセルが表示されるバグの対処法
はじめに
UItableViewCellのセルの再利用の仕組みによって、指定したcellの見た目を変える処理をした際に、過去のセルの変更が残ってしまうというちょっとしたバグが発生したので今回はUItableviewのセルの仕組みをみていきます
セルの再利用
TableViewは、大量にある同じ種類の情報をリスト表示する際に利用されます。そのため実装方法によってはセルの描画数が非常に多くなり、特にスクロール時には、パフォーマンスの低下が考えられます。これを解決するのがcellの再利用です。毎回新しくviewを作るのではなく以前に生成したcellを利用することで、メモリ割り当てを最小限にできます。
まずセルの再利用の際にはUItableviewにデフォルトで設定されているこのメソッドを使います公式ドキュメント参照
https://developer.apple.com/documentation/uikit/uitableview/1614891-dequeuereusablecellfunc dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCellそしてこのメソッドを、cellの生成されるタイミングで上のメソッドを発火させます
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //処理 }このメソッドは、再利用可能なcellがあればそれを、なければ新しく作成したcellを返します。再利用可能なcellは、reuseIdentifierに紐づけられた
reuse queue
という場所に格納されています。Reuse Queue
tableviewは裏側で
reuse queue
という、データの箱のようなものを持っており、reuseIdentifierごとにreuse queueが存在します。画面外に出たcellは、自身のidentifierに紐づいたreuse queueに追加されます。そして、同じidentifierのcellが表示されようとする時、queueから取り出されます。
下の図のようにスクロールしたら次のcellがqueueから取り出されるイメージ
セルの再利用の落とし穴
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // セルを取得(セルの再利用メソッド発火) let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellidendifier", for: indexPath) print(indexPath.row) //セルのviewを変更する処理 cell.textLabel!.text = "\(self.data[indexPath.row])" if indexPath.row == 3{ cell.backgroundColor = UIColor.red } return cell }例えばこんな感じで指定したセルの色を変えたい時にこのような処理を書くと、背景色を赤に変えたセルは、他のセルにも再利用されてしまいます。
指定した以外の色は元に戻す必要がありますfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // セルを取得(セルの再利用メソッド発火) let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellidendifier", for: indexPath) print(indexPath.row) //セルのviewを変更する処理 cell.textLabel!.text = "\(self.data[indexPath.row])" if indexPath.row == 3{ cell.backgroundColor = UIColor.red } else { cell.backgroundColor = 元の色 } return cell }カスタムセルの中身の画像を動的に変更したい場合はこのようにカスタムセルの画像を初期化する必要があります
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // セルを取得(セルの再利用メソッド発火) let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellidendifier", for: indexPath) //画像を初期化 cell.sampleimage.image = nil return cell }最後に
セルを再利用してることで前の状態が残るので、想定とは異なるview状態になります
今後似たような症状が起きた場合は、再利用によるセルリセットし忘れを疑いましょう
- 投稿日:2020-12-08T00:44:12+09:00
Xcode12以降のPlayground上でSwiftのパッケージをインポートする方法
はじめに
Xcode12以降のPlaygroundにおいて、Swiftのパッケージ?をドラッグ・アンド・ドロップすることで簡単にインポートしてできる機能が追加されました。
今までは簡単にドラッグ・アンド・ドロップでインポートできなかったのでこの使い方を紹介させて頂きたいと思います。https://developer.apple.com/documentation/xcode-release-notes/xcode-12-release-notes
Playgroundとは
プレイグラウンド(Playground)
は別のプロジェクトの生成をしなく、Swiftのコーディング結果をリアルタイムで確認できる機能です。
基本的な文法から複雑なコードまで様々なケースで利用することができます。Swiftを試しに動かしたり、文法の練習にも使えるいいツールです。使い方
まず、新しいプロジェクトを作成します。
Xcodeを開いてFile > New > Playground
を選択してPlaygroundのファイルを該当プロジェクトに追加します。
その後、Playgoundのファイルを開いた状態で
File > Save As Workspace
を選択してworkspaceファイルを生成します。?注意点として、ファイル生成後、右側の
show the file inspector
からRelative to Groupのオプションを選択することが必要です。
またPlayground SettingsのPlatformを現在使用しているPlatformを設定してBuild Active Scheme
にチェック☑️を入れます。ここまでできたらworksapceファイルを保存して再度開きます。
インポートしたいSwiftのバッケージをダウンロードしてXcodeの左側のファイルリストがある場所にドラッグアンドドロップします。インポートが終わったらすぐにPlaygroundに使用することができます。
以下はPlotというパッケージをインポートしてみた列です。
まとめ
PlaygroundはまだSwiftに慣れてない人や練習コードを作成したい人たちにとっても便利な機能だと思います。
これを利用して楽しくコーディングをしましょう!参考:
Plot
Importing Swift packages into a playground in Xcode 12
- 投稿日:2020-12-08T00:07:23+09:00
繰り返し文について〜for-in文〜
繰り返し文の種類
繰り返し処理を行う代表的な制御構文には、
for-in文
とwhile文
が存在します。for-in文はシーケンスの要素数によって、
while文は継続条件の評価によって繰り返しの終了を決定します。また、
repeat-while文
という制御構文も存在します。
repeat-while文は実行文の初回実行を保証する制御構文になります。for-in文
for-in文は、Sequenceプロトコルに準拠している型の要素にアクセスするための制御構文です。
Sequenceプロトコルに準拠している代表的な型には、
Array<Element>型、Dictionary<Key, Value>型などがあります。for-in文は次のように記述します。
要素名は基本的になんでも構いませんが、
意味のある分かりやすい要素名にした方が分かりやすくなります。for 要素名 in シーケンス { 要素ごとに繰り返し実行される処理 }Array<Element>型
Array<Element>型の要素をfor-in文で列挙する場合、要素の型はElement型になります。
つまり、Array<Int>型の要素をfor-in文で取り出す場合の要素の型はInt型になります。let arrayInt = [1, 2, 3, 4, 5] var count = 0 for int in arrayInt { count += int } print(count) 実行結果 15
arrayInt
の中に格納されている要素を先頭から順に取り出して、
for int in arrayInt
のintの中に代入しています。for-in文の中の処理を全て終えるとarrayIntの次の要素にアクセスしintに入れる。
それをarrayIntの先頭から末尾まで繰り返していきます。Dictionary<Key, Value>型
Dictionary<Key, Value>型の要素をfor-in文で列挙する場合、
要素の型は、(Key, value)型のタプルとなります。例えば、Dictionary<String, Int>型の値をfor-in文に渡すと、
要素は(String, Int)型になります。なお、Dictionary<Key, Value>型は要素の順序を保証していないため、
環境によっては実行結果の順序が異なるかもしれません。let dictionary = ["a": 1, "b": 2] for (key, value) in dictionary { print("Key: \(key), Value: \(value)") } 実行結果 Key: a, Value: 1 Key: b, Value: 2Range<Bound>型
また、範囲型についてもfor-in文で回すことが可能です。
今回は、1から10までの値を順にitemに代入しそれらを足す処理を行っています。
var count = 0 for item in 1...10 { count += item } print(count) 実行結果 55これらが代表的なfor-in文になります。
個人的にはif文より使用頻度は低いと思いますが、
かなり重要な制御構文なので絶対に覚えた方が良いと思います。以上、最後までご覧いただきありがとうございました。