- 投稿日:2019-10-11T23:33:22+09:00
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
を使った例でしたが、まだまだ、理解できていないところもありますので、ご指摘やアドバイス等ありましたら、コメントしていただければと思います。それではお読みいただきありがとうございました。
- 投稿日:2019-10-11T21:42:07+09:00
複数あるStoryboardで起動するStoryboardを選択するやり方
状況
後から新たに作成した
First.storyboard
から起動したい(Main.storyboard
ではなく)ググってみると
Storyboardを複数作成した場合の起動画面(Storyboard)の選択方法はこんな感じ
・起動するStoryboardをxcodeprojで選択しましょう
・is Initial ViewController
を選択しましょう
->全部やってます、、、、でもMain
で起動される
・
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
)
↓ここでした
なぜここを変えないとできないのかはよく分かりませんが
いくら調べても出てこなかったので備忘録として。参考
・アプリ起動時に呼び出すStoryboardを指定したい | moxt
・[iOS] 複数のStoryboardを使って画面遷移を作成する | DevelopersIO
・perhaps the designated entry point is not set? が表示された時の対処方法 - Qiita
・iOSのスプラッシュ画面実装における注意点と実装方法 - Qiita・[iOS] 複数のStoryboardを使って画面遷移を作成する | DevelopersIO
->単純にStoryboard
の分割の意図についてためになる記事でした(5年以上前の記事だけど)
- 投稿日:2019-10-11T21:20:46+09:00
ReactorKitでFirestoreをListenする方法
概要
ReactorKit
を使ったプロジェクトでFirestore
を使うことになったので、Pring
を用いて実装しました。環境
- Xcode 11.0
- Swift 5.1
- ReactorKit 1.2.1 (https://github.com/ReactorKit/ReactorKit)
- Pring 0.17.3 (https://github.com/1amageek/Pring)
実装
User
というモデルをFirestore
から取得してくるという設定です。Reactor側
Action
,Mutation
,State
,mutate
,reduce
はやるだけって感じです。
ポイントはdataSource
を以下のように用意して、private let dataSource: DataSource<User>?
setupDataSource()
内でDataSource
をlisten()
してやることです。
この時.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.users
をbind
してやれば終わりです。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) } }
- 投稿日:2019-10-11T20:32:27+09:00
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圏内の人とランダム通話ができると言う事です。
マッチング界はほとんどレッドオーシャン状態になっていますが、
すんごいニッチなターゲット選定をしています笑 そこが売りであり、僕たちの強みでもあり弱みでもあります笑
onekiloで使用したFirebaseの機能
(本音を言うとアプリに関係なく全て活用したかったです笑)
- Authentication
- ユーザー登録する際に用いました
- Cloud Firestore
- Database として使用しました
- Storage
- 画像の保存や引っ張てくるのに用いました。
- Remote Config
- 強制的にバージョンをアップデートを行うために用いました!(すんごい便利)
- Cloud Messaging
- ユーザーに通知を送るために使用しました。
- アナリティクス(全て)
- 実際にユーザーの反応を見るのにすごく便利です。
他にもML Kit とか使いたかったです。。。
上記の使い方などは要望があれば別で描こうと思います。
最後に
実は今年の4月くらいにOnekiloのアイデアが自分の頭の中ででていました。
こんなん誰も使わんやろと思いながら3ヶ月くらいすぎて、Cyber Agentやリクルートのインターンに参加させていただいた際にちゃんと事業戦略練ればいけるんじゃないかと期待してくれたのが今回僕が動く結果になりました。
ありがとうございます。
「マッチングサービスはレッドオーシャン」
「半径1kmのマッチングはニッチすぎる」と指摘されることは多々ありますが、自分の事業戦略を通して勝ち筋通りに進めたらいいなと思います。
もっと書きたいことはありますが、このような話はnoteに記しますので興味ある方は読んでください。。!最後にベータ版ではありますが、Onkilo のインストールをよろしくお願いいたします。
最後まで読んでいただきありがとうございます。
watnow
mememe
代表
葛上
- 投稿日:2019-10-11T15:55:00+09:00
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
で指定した、unselectedImage
とselectedImage
は、
Storyboard上でカスタマイズできるようになります。Storyboardで設定
Storyboardでボタンを配置して、
上記のように、Custom Classに作成したImageButtonを設定すると、
このように、
unselectedImage
とselectedImage
に
好きな画像を設定できるようになります。つまり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
- 投稿日:2019-10-11T15:54:03+09:00
@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の例が
あまりネット記事になかったこともあり、記事として記載してみました。ぜひ参考にしてみてください。参考
- https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/ImplementingACustomControl.html
- https://qiita.com/son_s/items/7ca2acf690d10f9fd1b7
- https://qiita.com/tasaiii725/items/c70bf648242b061e0734
- https://qiita.com/dddisk/items/8001598ea7951bcdcc30
- https://qiita.com/yucovin/items/4bebcc7a8b1088b374c9
- https://qiita.com/ladnack/items/149b4ebca7f4f45e5335
- http://kimagureneet.hatenablog.com/entry/2015/11/27/211353
- https://qiita.com/kuninori/items/7c4fbd9840c6dd07fed7
- https://qiita.com/yucovin/items/ff58fcbd60ca81de77cb#stackviewをコードで作る方法
- https://qiita.com/ika_tarou/items/e411c37b064fdd514afb
- https://qiita.com/stastaahaha/items/e45e7559255fb7666fd8
- 投稿日:2019-10-11T12:44:48+09:00
ios13のWKWebViewのWebRTC対応状況まとめ
- 投稿日:2019-10-11T08:31:37+09:00
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) }