20191011のSwiftに関する記事は8件です。

Swift5.1 PropetyWrappersを使ったUserDefaultsの例

はじめに

Swift 5.1から新しく追加されたPropertyWrapper
@State@Bindingなどでお馴染みですが、カスタム属性を作成して使っているでしょうか?

今回は、WWDC2019セッションUserDefaultsを使用したカスタム属性の内容が紹介されていたので、その例を備忘録兼ねて書こうと思います。

PropertyWrappersとは?

Swift5.1で導入されている機能で、プロパティをラップしてアクセスパターンを指定することができる機能。
すごくざっくり言うと、「プロパティにアクセスする際の面倒な処理をまとめておいて、使う時に@なんちゃらで呼び出すことができるよ」というものです。

ちなみに@State@PublishedなどもPropertyWrappersです。

独自に定義する場合は@propertyWrapperを宣言して使います。

今まで

通常はこんな処理はしないと思いますが、例えばこのような処理があったとします。

struct Person {
    var name: String {
        get {
            UserDefaults.standard.object(forKey: "person_name") as? String ?? ""
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "person_name")
        }
    }
}

Person以外にも、このようなコンピューテッドプロパティを使用してUserDefaultsにアクセスするような処理が複数あったらとても冗長です。

PropertyWrappersを使う

ここでPropertyWrappersの出番です。
PropertyWrappersを使用してUserDefaultsのアクセスパターンを定義してみます。
(ちなみにこのUserDefaultsのアクセスパターンは冒頭で書いたように、WWDC2019セッションで紹介されている例です。)

@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T

    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get {
            return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}

こう定義することで、外部からUserDefaultでプロパティを宣言することが可能になりました。

今回の例だと、実際に@UserDefaultで定義されたプロパティにアクセスした際はこのwrappedValueの中が呼ばれます。

@UserDefaultを使ってみる

実際に使ってみます。

先ほどPersonクラスで定義していた処理がこんなにすっきり書くことができました。

struct Person {
    @UserDefault("person_name", defaultValue: "")
    var name: String
}

通常通り以下のように使用できます。

let a = Person()
a.name = "Taro"

ただ、nameプロパティも同時に初期化する場合、このままでは型がUserDefault<String>となっているので、明示的にイニシャライザを追加してあげる必要があります。

struct Person {
    @UserDefault("person_name", defaultValue: "")
    var name: String

    init(name: String) {
        self.name = name
    }
}

これで引数付きで初期化できるようになりました。

let a = Person(name: "Taro")

上の例は実用的でないので、実際に使う場合は以下のようにStaticで宣言して使う感じになるかと思います。

struct UserDefaultsConfig {
    @UserDefault("isLogin", defaultValue: false)
    static var isLogin: Bool

    @UserDefault("username", defaultValue: "")
    static var username: String

    @UserDefault("age", defaultValue: 1990)
    static var age: Int
}

すっきり。

    UserDefaultsConfig.isLogin = true
    UserDefaultsConfig.username = "Yamada Taro"
    UserDefaultsConfig.age = 24

さいごに

どうでしょう。思ったより簡単でした。
PropertyWrappersを使用することで、プロパティのアクセスパターンを繰り返し書く必要がなくなります。
そして宣言的にカスタム属性を足すだけで、その定義をプロパティに当てはめられるようになるので、よりシンプルに、完結的な記述をすることができるようになりますね。

良かったらみなさんも使ってみてください。

以上、UserDefaultsを使った例でしたが、まだまだ、理解できていないところもありますので、ご指摘やアドバイス等ありましたら、コメントしていただければと思います。

それではお読みいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数あるStoryboardで起動するStoryboardを選択するやり方

状況

後から新たに作成したFirst.storyboardから起動したい(Main.storyboardではなく)

ググってみると
Storyboardを複数作成した場合の起動画面(Storyboard)の選択方法はこんな感じ
・起動するStoryboardをxcodeprojで選択しましょう
is Initial ViewControllerを選択しましょう
->全部やってます、、、、でもMainで起動される
スクリーンショット 2019-10-11 21.18.20.png

Main.storyboard消してみる(referenceのみ)

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Could not find a storyboard named 'Main' in bundle NSBundle </Users/hoge/data/Containers/Bundle/Application/hoge/hoge.app> (loaded)'
*** First throw call stack:

Main.storyboardがないよー と言われる始末、、、、

対策

いったいどこでMain.storyboardを参照してやがるんだとMainでプロジェクト全体に検索をかけたところ
(ApplicationScene Manifest->Scene Configuration->Application Session Role->Item 0 ...->Storyboard Name)
↓ここでした
スクリーンショット 2019-10-11 21.18.45.png

なぜここを変えないとできないのかはよく分かりませんが
いくら調べても出てこなかったので備忘録として。

参考

アプリ起動時に呼び出すStoryboardを指定したい | moxt
[iOS] 複数のStoryboardを使って画面遷移を作成する | DevelopersIO
perhaps the designated entry point is not set? が表示された時の対処方法 - Qiita
iOSのスプラッシュ画面実装における注意点と実装方法 - Qiita

[iOS] 複数のStoryboardを使って画面遷移を作成する | DevelopersIO
->単純にStoryboardの分割の意図についてためになる記事でした(5年以上前の記事だけど)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactorKitでFirestoreをListenする方法

概要

ReactorKitを使ったプロジェクトでFirestoreを使うことになったので、Pringを用いて実装しました。

環境

実装

UserというモデルをFirestoreから取得してくるという設定です。

Reactor側

Action, Mutation, State, mutate, reduceはやるだけって感じです。
ポイントはdataSourceを以下のように用意して、

private let dataSource: DataSource<User>?

setupDataSource()内でDataSourcelisten()してやることです。
この時.onの中でself?.action.onNext(.updateUsers(users))することで、Firestoreから取得したデータをReactorのストリームにうまく取り込んでます。

private func setupDataSource() {
    dataSource = User.query.dataSource()
        .on { [weak self] _, _ in
            guard let users = self?.dataSource?.documents else { return }
            self?.action.onNext(.updateUsers(users))
        }
        .listen()
}

全実装は以下のようになります。

import ReactorKit
import RxSwift
import Pring

final class UserListReactor: Reactor {
    enum Action {
        case updateUsers([User])
    }

    enum Mutation {
        case setUsers([User])
    }

    struct State {
        var users: [User] = []
    }

    // MARK: - Variables

    let initialState = State()
    private let dataSource: DataSource<User>?

    // MARK: - Initializer

    init() {
        setupDataSource()
    }

    // MARK: - Setup Methods

    private func setupDataSource() {
        dataSource = User.query.dataSource()
            .on { [weak self] _, _ in
                guard let users = self?.dataSource?.documents else { return }
                self?.action.onNext(.updateUsers(users))
            }
            .listen()
    }

    // MARK: - Reactor Methods

    func mutate(action: Action) -> Observable<Mutation> {
        switch action {
        case let .updateUsers(users):
            return .just(Mutation.setUsers(users))
        }
    }

    func reduce(state: State, mutation: Mutation) -> State {
        var state = state
        switch mutation {
        case let .setUsers(users):
            state.users = users
        }
        return state
    }
}

ViewController側

いつも通りbind(reactor:)内でUserListReactor.State.usersbindしてやれば終わりです。

import UIKit
import RxSwift
import ReactorKit

final class UserListViewController: UIViewController, View {
    // MARK: - Variables

    var disposeBag = DisposeBag()
    ...
    // MARK: - Bind Methods

    func bind(reactor: UserListReactor) {
        // State
        reactor.state.map { $0.users }
            .distinctUntilChanged()
            .bind { users in
                // tableView or collectionViewの更新
            }
            .disposed(by: disposeBag)
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firebase めちゃ便利!Onekilo (iosアプリ)リリースしました!

Firebase めちゃ便利!! Onekiloリリースしました!

Outline

  • 自己紹介
  • Onekiloとは?
  • Firebase のおすすめ点(もうみんな知ってるか笑)
  • 最後に

 自己紹介

初めまして!
立命館大学理工学部3年の葛上です!watnowの代表をしています!!
Twitterフォローしてください!Facebookも友達になってください!

メンバー

ios 2人
UI/UXデザイナー 1人
動画作成 1人
の計4人で開発しました。

ios 開発歴はまだ2年目で、今まで開発したサービスは5つほどしかありません。
Firebase に関しては、一年前にRealtimeDatabase 少し使ってtwitterみたいなのを使ったくらいです!

Onekilo とは?

今回作成したサービスがOnekiloになります!
抽象的ではありますが、

「今すぐ」何かするって当たり前になってきてるな

と思い、足掛かりとしてマッチングアプリを作りました。
概要としては 1km圏内の人とランダム通話ができると言う事です。
マッチング界はほとんどレッドオーシャン状態になっていますが、
すんごいニッチなターゲット選定をしています笑 そこが売りであり、僕たちの強みでもあり弱みでもあります笑
IMG_642E445D70BF-1.jpeg

onekiloで使用したFirebaseの機能

(本音を言うとアプリに関係なく全て活用したかったです笑)

  • Authentication
    • ユーザー登録する際に用いました
  • Cloud Firestore
    • Database として使用しました
  • Storage
    • 画像の保存や引っ張てくるのに用いました。
  • Remote Config
    • 強制的にバージョンをアップデートを行うために用いました!(すんごい便利)
  • Cloud Messaging
    • ユーザーに通知を送るために使用しました。
  • アナリティクス(全て)
    • 実際にユーザーの反応を見るのにすごく便利です。
他にもML Kit とか使いたかったです。。。

上記の使い方などは要望があれば別で描こうと思います。

最後に

実は今年の4月くらいにOnekiloのアイデアが自分の頭の中ででていました。

こんなん誰も使わんやろと思いながら3ヶ月くらいすぎて、Cyber Agentやリクルートのインターンに参加させていただいた際にちゃんと事業戦略練ればいけるんじゃないかと期待してくれたのが今回僕が動く結果になりました。

ありがとうございます。

「マッチングサービスはレッドオーシャン」
「半径1kmのマッチングはニッチすぎる」

と指摘されることは多々ありますが、自分の事業戦略を通して勝ち筋通りに進めたらいいなと思います。
もっと書きたいことはありますが、このような話はnoteに記しますので興味ある方は読んでください。。!

最後にベータ版ではありますが、Onkilo のインストールをよろしくお願いいたします。

最後まで読んでいただきありがとうございます。

watnow
mememe
代表
葛上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSでハートのボタンアニメーションをつくる

先日「いいね!」的なハートのボタンアニメーションを作る機会がありまして。

先輩がライブラリ公開しているものを参考にしつつ、
https://qiita.com/darquro/items/c5cdf7dbcad1d5bb0188

自分はライブラリではなく、自作で、
@IBDesignable@IBInspectableを使って
ハートボタンとそのアニメーションを作ってみましたので、その備忘録。

開発環境

Mac OS Mojave 10.14.6
Xcode 11.0
Swift 5

つくりたいもの

  • ハートの画像2枚をボタン押下で切り替える
  • ボタン押下時(Highlight時)にハートをちょっと小さくする
  • ボタン押下後(Touch up inside)にハートをちょっと大きくする

ハートの画像2枚をボタン押下で切り替える

アニメーションとは付加機能。
まずアニメーションなしで、つくりたいものを実現します。

他にも方法はあると思いますが、今回はハートの画像2枚をボタン押下で切り替えます。

@IBDesignable@IBInspectableを使った汎用的なボタン

@IBDesignable@IBInspectableは非常に便利です。
(SwiftUIの世の波とは逆行してる感!)

コードである程度の設定を書いておくと、
Storyboardでカスタマイズが容易になります。

まずImageButtonという汎用的なボタンを設定します。

import UIKit

@IBDesignable
final class ImageButton: UIButton {

    @IBInspectable var unselectedImage: UIImage = UIImage()
    @IBInspectable var selectedImage: UIImage = UIImage()

    public var selectedStatus: Bool = false {
        didSet {
            setupImageView()
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        setupImageView()
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        setupImageView()
        setNeedsDisplay()
    }

    private func setupImageView() {
        self.setImage(self.selectedStatus ? self.selectedImage : self.unselectedImage, for: .normal)
        self.setImage(self.selectedStatus ? self.selectedImage : self.unselectedImage, for: .highlighted)
    }
}

@IBInspectableで指定した、unselectedImageselectedImage は、
Storyboard上でカスタマイズできるようになります。

Storyboardで設定

Storyboardでボタンを配置して、

上記のように、Custom Classに作成したImageButtonを設定すると、

このように、unselectedImageselectedImage
好きな画像を設定できるようになります。

つまりImageButtonはハートボタン以外にも利用できるので、便利感ありますね。
以下の画像を設定します。

  • unselectedImage
  • selectedImage
  • DefaultのImage <- 忘れないように

アクションを設定

あとはボタンのクリック動作をハンドリングして、処理を書きます。
※Storyboardとコードを結びつけるとこは省略。

@IBOutlet private weak var likeButton: ImageButton!
@IBAction private func clickLikeButton(_ sender: Any) {
    likeButton.selectedStatus = !likeButton.selectedStatus
}

画像切り替え 完成

これでアニメーションのない、ハートボタンが完成です。

ハート押下時にアニメーションを

今回は CASpringAnimation を利用します。
ばねっぽいAnimationを表現しやすいと聞いたので。

今回アニメーションを付けるのは2箇所あります。

  • ボタン押下時(Highlight時)にハートをちょっと小さくする
  • ボタン押下後(Touch up inside)にハートをちょっと大きくする

ボタン押下時(Highlight時)にハートをちょっと小さくする

方法は色々あると思いますが、自分は以下のコードで実現しました。
isHighlighted でボタンが押されている状態かを識別します。

override var isHighlighted: Bool {
    didSet {
        if isHighlighted {
            if imageView?.layer.animation(forKey: "reduced-size") == nil {
                let animation = CASpringAnimation(keyPath: "transform.scale")
                animation.duration = 0.05 // animation時間
                animation.fromValue = 1.0 // animation前サイズ
                animation.toValue = 0.95 // animation後サイズ
                animation.mass = 0.1 // 質量
                animation.autoreverses = false // 自動でfromの値に戻らない
                animation.initialVelocity = 40.0 // 初速度
                animation.damping = 1.0 // 硬さ
                animation.stiffness = 40.0 // バネの弾性力
                animation.isRemovedOnCompletion = false // animation動作後に完了状態としない
                animation.fillMode = .forwards // 一方向モード。fromの形状に戻らない
                imageView?.layer.add(animation, forKey: "reduced-size")
            }
        }
    }
}

ポイントは以下。

  • mass initialVelocity damping stiffness は動作を見てなんとなく値決め
  • 押下中にサイズが戻ってしまわないように、autoreverses isRemovedOnCompletion はfalse、fillMode.forward
  • animationは1回だけ実行にしたいので、animationが既に実行されているかどうかの判別式を記載 if imageView?.layer.animation(forKey: "reduced-size") == nil
  • animation実行判別のために、任意のkey名reduced-sizeを設定

これでボタン押下時のAnimationは実現できました。
しかし、これでは imageView?.layer.add(animation, …)で、追加された
animationがremoveされていません。それについては後述。

ボタン押下後(Touch up inside)にハートをちょっと大きくする

こちらも方法は色々あるかと思いますが、今回は上記で設定したsetupImageView()
funcにanimationを記載することで実現しました。

private func setupImageView() {
    self.setImage(self.selectedStatus ? self.selectedImage : self.unselectedImage, for: .normal)
    self.setImage(self.selectedStatus ? self.selectedImage : self.unselectedImage, for: .highlighted)
    let animation = CASpringAnimation(keyPath: "transform.scale")
    animation.duration = 0.3 // animation時間
    animation.fromValue = 0.95 // animation前サイズ
    animation.toValue = 1.0 // animation前サイズ
    animation.mass = 0.6 // 質量
    animation.initialVelocity = 40.0 // 初速度
    animation.damping = 3.0 // 硬さ
    animation.stiffness = 40.0 // バネの弾性力
    imageView?.layer.add(animation, forKey: nil)
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
        self.imageView?.layer.removeAllAnimations() // highlightのAnimationも含め、removeする
    }
}

既述したもの以外のポイントは以下。

  • animationが全て完了したらremoveする必要があるので、animation時間が経過したらremoveを実施 self.imageView?.layer.removeAllAnimations()

animationのremoveに関してはもっと良い方法があるかも...
指摘などあればぜひコメントお願いしますm(_ _)m

完成

これでアニメーション付きのハートが完成しました。

まとめ

CASpringAnimationについて、
@IBDesignable@IBInspectableについて、
詳細の説明は省略しました。参考資料をご確認頂ければと。

参考

https://qiita.com/kaway/items/b9e85403a4d78c11f8df
http://www.cl9.info/entry/2018/05/26/175246
https://qiita.com/h-nag/items/7af47ea665332c3ac1bf
https://qiita.com/tasaiii725/items/c70bf648242b061e0734
https://qiita.com/son_s/items/7ca2acf690d10f9fd1b7

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

@IBDesignableと@IBInspectableで汎用的なButtonを

こんな感じのボタンを作りたいとき。

普通に favoriteButton として作成しても良いですが、
「他のボタンにも同じ構成で作れる汎用的なボタンにしたい」
という時に@IBDesignable@IBInspectableが便利です。

@IBDesignable@IBInspectableとは

@IBDesignableとは?
@IBInspectableとは?
と聞かれるとなんて答えてよいか難しいのですが、

@IBDesignable@IBInspectableを使うと、
事前に作成しておいたViewを、Storyboard上で適用できる、カスタマイズできる
ものです。

例を示します。

final class RoundedCornerButton: UIButton {

    @IBInspectable var iconImage: UIImage = UIImage()
}

このようにUIButtonを継承するRoundedCornerButtonクラスを定義して、
Storyboard上に設定したボタンのCustomクラスに、

このようにRoundedCornerButtonを設定すると、

@IBInspectableで設定したiconImageをStoryboard上で設定できる、
というものです。

RoundedCornerButton

細かく書くより、コード全体を載せたいと思います。
こんな感じのボタンを作成します。

RoundedCornerButton:

import UIKit

@IBDesignable
final class RoundedCornerButton: UIButton {

    @IBInspectable var iconImage: UIImage = UIImage()
    @IBInspectable var unselectedText: String = "未選択"
    @IBInspectable var selectedText: String = "選択済み"

    @IBInspectable var unselectedBackgroundColor: UIColor = UIColor.systemTeal
    @IBInspectable var unselectedShadowColor: UIColor =  UIColor.ocean
    @IBInspectable var unselectedBorderColor: UIColor = UIColor.clear
    @IBInspectable var selectedBackgroundColor: UIColor = UIColor.baseGray
    @IBInspectable var selectedShadowColor: UIColor = UIColor.gray
    @IBInspectable var selectedBorderColor: UIColor = UIColor.clear

    // ボタンがselectされているかどうかの変数
    private(set) var selectedStatus: Bool = false

    // ボタンが凹む前のX座標
    private lazy var originalX: CGFloat = {
        return self.layer.position.x
    }()

    // ボタンが凹む前のY座標
    private lazy var originalY: CGFloat = {
        return self.layer.position.y
    }()

    private lazy var stackView: UIStackView = {
        let stackView = UIStackView(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
        stackView.translatesAutoresizingMaskIntoConstraints = false // autoLayoutをONに
        stackView.alignment = .center
        stackView.distribution = .fill
        stackView.axis = .horizontal
        stackView.spacing = 4.0
        stackView.backgroundColor = UIColor.clear
        stackView.isUserInteractionEnabled = false // stackView部分をタップしてもボタンが反映するように
        return stackView
    }()

    private lazy var iconImageView: UIImageView = {
        let iconImageView = UIImageView(image: iconImage)
        iconImageView.contentMode = .scaleAspectFit // 画像そのままに縦横比に表示
        iconImageView.widthAnchor.constraint(equalToConstant: 32.0).isActive = true
        return iconImageView
    }()

    private lazy var textLabel: UILabel = {
        let width = stackView.frame.width - iconImageView.frame.width
        let textLabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: frame.height))
        textLabel.text = unselectedText
        textLabel.textAlignment = .center
        textLabel.textColor = UIColor(hex: "#4A4A4A")
        textLabel.font = UIFont.boldSystemFont(ofSize: 16.0)
        return textLabel
    }()

    override func awakeFromNib() {
        super.awakeFromNib()
        setupLayer()
        setupViews()
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        setupLayer()
        setupViews()
        setNeedsDisplay()
    }

    // Highlight時にボタンが凹んだように見せるため、layerの位置変更・影なしに
    override var isHighlighted: Bool {
        didSet {
            layer.position = CGPoint(x: originalX, y: isHighlighted ? originalY+2.0 : originalY)
            layer.shadowOffset = isHighlighted ? CGSize(width: 0.0, height: 0.0) : CGSize(width: 0.0, height: 2.0)
        }
    }

    private func setupLayer() {
        setStatus(selectedStatus)
        layer.borderWidth = 1.0 // 枠線の長さを定義
        layer.cornerRadius = 4.0 // 角丸に
        layer.shadowOpacity = 1.0 // 影を表示する
        layer.shadowRadius = 0.0 // ぼやけ影を非表示
    }

    private func setupViews() {
        self.addSubview(stackView) // buttonのviewにstackViewを載せる
        stackView.addArrangedSubview(iconImageView) // iconImageViewをstackViewに載せる
        stackView.addArrangedSubview(textLabel) // textLabelをstackViewに載せる
        stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20.0).isActive = true // stackViewの左端をbuttonのviewの左端から20離す
        stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
    }
}

// MARK: public
extension RoundedCornerButton {
    func setStatus(_ status: Bool) {
        selectedStatus = status
        layer.backgroundColor = status ? selectedBackgroundColor.cgColor : unselectedBackgroundColor.cgColor
        layer.shadowColor = status ? selectedShadowColor.cgColor : unselectedShadowColor.cgColor
        layer.borderColor = status ? selectedBorderColor.cgColor : unselectedBorderColor.cgColor
        layer.shadowOffset = CGSize(width: 0.0, height: status ? 3.0 : 2.0) // 影の長さ
        textLabel.text = status ? selectedText : unselectedText
    }
}

ポイントは以下。

  • レイアウトの配置は、Storyboardでエラーが出ないようにAutoLayout設定する感覚で。
  • ボタンの上にViewを載せると、ボタンが押せなくなってしまうので、isUserInteractionEnabledをfalseに。

favoriteButtonにRoundedCornerButtonを適用

StoryboardでfavoriteButtonにRoundedCornerButtonをCustom Classに設定。

@IBInspectableで設けた項目をそれぞれ設定します。

favoriteButtonについて、コードで以下のようにアクションを設定。

@IBOutlet private weak var favoriteButton: RoundedCornerButton!
@IBAction private func clickFavoriteButton(_ sender: Any) {
    favoriteButton.setStatus(!favoriteButton.selectedStatus)
}

以上で完成です。

まとめ

StackViewを用いている@IBDesignable@IBInspectableの例が
あまりネット記事になかったこともあり、記事として記載してみました。ぜひ参考にしてみてください。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ios13のWKWebViewのWebRTC対応状況まとめ

はじめに

iOS12までは、iOSのWebRTC対応状況はSafariのみで、WKWebViewでは対応していませんでした。
この記事では、iOS13になって対応されているか検証を行いました。

結論

iOS12と変わらず、getUserMediaが実装されていないため動きませんでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

For Where Violation: `where` clauses are preferred over a single `if` inside a `for`. (for_where) の警告が出た時の処理

for...in...{ if ... == ...} をwhereでスッキリさせる。

for musicItem in musics {
     if musicItem.title == favoriteTitle {
         favoriteList.append(musicItem)
     }
 }

こんな感じで、配列に入った値を一つ一つに分解して、その分解した値のkeyで処理をしたい場合、whereを使えって警告が出る。
その変換

for musicItem in musics where musicItem.title == favoriteTitle {
      favoriteList.append(musicItem)
}

これでOK。
もっとスッキリさせるためには次

for musicItem in (musics.filter { $0.title == favoriteTitle }) {
  favoriteList.append(musicItem)
}

参考:
https://stackoverflow.com/questions/45590544/swiftlint-warning-for-where-violation-where-clauses-are-preferred-over-a-si

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む