20210815のSwiftに関する記事は7件です。

Stringが使えるメソッド(今日使用した4つ)

今回の内容 今日、使用したStringが使用できるメソッドについて コードと簡単解説 まずは、メソッドを使うために変数(String型)を宣言しておきます。 var uppercasedString = "abcdefg" var lowercasedString = "ABCDEFG" var containString = "0123456789" var replacingOccurrencesString = "1111111" 小文字の値を大文字に変換 print(uppercasedString.uppercased()) Print結果 ABCDEFG 大文字の値を小文字に変換 print(lowercasedString.lowercased()) Print結果 abcdefg 特定の値が含まれてるかを判定する Bool値で結果を判定します print(containString.contains("0")) Print結果 true 特定の値を希望の値に変更 .replacingOccurrences(of: "1", with: "2")は、1の値を全て2に変更しています。 print(replacingOccurrencesString.replacingOccurrences(of: "1", with: "2")) Print結果 2222222 終わり ご指摘、ご質問などありましたら、コメントまでお願い致します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

XCoordinatorのREADMEを和訳してみた

Coordinatorパターンを採用する際に便利なライブラリがあると言うことなので使おうとしましたが、和訳記事が見当たらなかったのでREADMEをできる限り和訳してみました。 Google翻訳をベースにしているので、もしかしたら意味が通ってない部分があるかと思いますがご容赦いただきたいです。 XCoordinatorを使用する理由 ナビゲーションコードはすでに記述されている。 関心事の分離 Coordinator:一連のルート Route:ナビゲーション先 Transition:遷移の種類とアニメーション Coordinator、Router、Transitionをさまざまな組み合わせで再利用できる。 CustomTransition/animationの完全サポート。 子View/コンテナビューの埋め込みのサポート。 BasicCoordinatorが多くの使用ケースに適したジェネリッククラスであるため、独自のCoordinatorを作成する必要が少なくなる。 導入 アプリの起動からXCoordinatorを使用する Info.plistのStoryboard file base nameからMainを削除 下のコードをAppDelegate.swiftに追加 @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { let window: UIWindow! = UIWindow() let router = AppCoordinator().strongRouter func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { router.setRoot(for: window) return true } } XCoordinatorを使用する 接続したいViewを含む列挙型を作成します。 enum UserListRoute: Route { case home case users case user(String) case registerUsersPeek(from: Container) case logout } Routeに書かれている経路は、どのRouteにtriggerできるかを表します。 CoordinatorはtriggerされたRouteに基づいて、遷移の準備を担当します。 class UserListCoordinator: NavigationCoordinator<UserListRoute> { init() { super.init(initialRoute: .home) } override func prepareTransition(for route: UserListRoute) -> NavigationTransition { switch route { case .home: let viewController = HomeViewController.instantiateFromNib() let viewModel = HomeViewModelImpl(router: unownedRouter) viewController.bind(to: viewModel) return .push(viewController) case .users: let viewController = UsersViewController.instantiateFromNib() let viewModel = UsersViewModelImpl(router: unownedRouter) viewController.bind(to: viewModel) return .push(viewController, animation: .interactiveFade) case .user(let username): let coordinator = UserCoordinator(user: username) return .present(coordinator, animation: .default) case .registerUsersPeek(let source): return registerPeek(for: source, route: .users) case .logout: return .dismiss() } } } UserListRouteは、フローのtriggerを定義する列挙型を作成します。 例えば.homeがtriggerされるとHomeViewControllerが表示されます。 Routeは、CoordinatorまたはViewModel内からtriggerされます。 下記のコードはViewModel内からRouteをtriggerする方法です。 class HomeViewModel { let router: UnownedRouter<HomeRoute> init(router: UnownedRouter<HomeRoute>) { self.router = router } /* ... */ func usersButtonPressed() { router.trigger(.users) } } コンポーネント Route 関連するナビゲーションパス Coordinator/ルーター Viewを読み込み、triggerされたRouteに基づいてviewModelsを作成するオブジェクト。 StrongRouter 元のCoordinatorへの強い参照を保持しています。子Coordinatorを保持したり、AppDelegateで特定のルーターを指定したりできます。 WeakRouter 元のCoordinatorへの弱い参照を保持しています。viewControllerまたはviewModelにCoordinatorを保持できます。また、兄弟または親のCoordinatorへの参照を保持するために使用することもできます。 UnownedRouter 元のCoordinatorへの所有されていない参照を保持します。viewControllerまたはviewModelにCoordinatorを保持できます。また、兄弟または親のCoordinatorへの参照を保持するために使用することもできます。 Transition 遷移方法を指定できる。使用中のrootViewControllerのタイプに基づいて利用できます。 例:ViewTransitionはすべてのrootViewControllerがサポートする基本的なTransitionのみをサポートしますが、NavigationTransitionはNavigationController固有のTransitionを追加します。 使用可能な遷移タイプは次のとおりです。 present View階層の最上位にViewControllerを提示します (rootViewControllerからする場合は、presentOnRootを使用します) embed ViewControllerをコンテナビューに埋め込みます dismiss 最も上位に表示されているViewControllerを却下します (dismissToRootを使用してrootViewControllerでdismissを呼び出します) none 何もしません。Routeを無視したり、テスト目的で使用したりできます。 push ViewControllerをNavigationStackにpushします (NavigationTransition内のみ) pop NavigationStackからトップビューコントローラーをpopします (NavigationTransition内のみ) popToRoot rootViewControllerを除くNavigationStack上のすべてのViewControllerをpopします (NavigationTransition内のみ) Coordinatorパターン参考記事 間違い等あればご指摘いただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

KMMを触ってみて思ったこと

はじめに KMM(Kotlin Multiplatform Mobile)とは、Kotlin/NativeというKotlinのコードをさまざまなOSのネイティブコード(マシン語)に変換する技術を使って、モバイル(iOSとAndroid)に特化したクロスプラットフォーム開発をできるようにするSDKです。 僕はKMMってなんか響きがかっこ良いんでKotlinでiOSアプリが作れるっていうところが凄いなと思って勉強を始めてみました。勉強を始めて一番最初に浮かんだ疑問として、Kotlinで開発するのであればただのAndroid開発と何が違うのかというのがありました。その疑問について色々調べてみました。 仕組みの違い そもそもAndroidのネイティブ開発ではKotlinやJavaを使うことができます。それぞれのコードがART(Android RunTime)という実行環境でAndroidOS上で動くようなネイティブコードにコンパイルされることで動いています。 対してKMMはKotlin/NativeがKotlinを各OSのネイティブコードに変換するための諸々をやってくれているので動くそうです。Kotlin/Nativeの公式ドキュメントの文章を見てみると、 Kotlin/Native is a technology for compiling Kotlin code to native binaries, which can run without a virtual machine. It is an LLVM based backend for the Kotlin compiler and native implementation of the Kotlin standard library. ということらしいです。全然わけがわからないです。色々調べてみたところ、この部分の詳しい説明として↓の記事がありました。 https://developers.cyberagent.co.jp/blog/archives/23149/ 要約すると、Kotlin/Nativeは ・ 標準ライブラリ ・ メモリ管理 ・ プログラムランチャー の3つの機能を提供してくれていることで動くそうです。 なので普通のAndroid開発とKMMでは、最終的にAndroidOS上で動くネイティブコードに変換している点は共通していてそのプロセスが違うということらしいです。 開発する上での具体的な違い まずKMMはマルチモジュール構成となっていて、共有モジュールと各OSのモジュールの三つの構成になっています。各OSのモジュールではそれぞれのOSでしか使えないライブラリ等が使えます。僕はとりあえずサンプルアプリはMVVMで、ViewModelとViewをそれぞれのOS毎に分岐して書くというやり方で作りました。普通のAndroid開発とKMMで開発するときの具体的な実装時の違いとして使えるライブラリの違いがありました。ここがかなり重要な違いかなと思いました。例えば、 HTTPクライアント ・Android -> Retrofit(+ OkHttp) ・KMM -> Ktor client Coroutines ・Android -> coroutines-android ・KMM -> native-mt AAC(Android Architecture Components) ・Android -> 使える ・KMM -> 共有部分のコードでは使えない 他にもあると思いますがこの辺りが違いを実感しました。特にiOSにも対応しないといけないので当たり前ではあるんですが、共有部分のコードでAACが使えないのが辛かったです。ViewModelScope等のAndroidのライフサイクル対応のCoroutineが使えなかったので結局ViewModelを分ける以外にやり方がわかりませんでした。それに関係する話ですが、アーキテクチャに関しても、 ・Android -> MVVMが推奨 ・KMM -> 何が正解か全くわからない といった感じでKMMは何が正解なのかよくわかりませんでした。KMMはそれぞれのOSの違いに対応する時に、どうしてもコードを共通化できない部分が出てきてしまってそれぞれ分岐させなければいけないところがあると思うんですが、その分け方のベストプラクティスみたいなものがわからないです。 感想 とりあえずKotlinでiOSのアプリが作れたのはめちゃくちゃ感動しました。ただ結局ViewModel以降はそれぞれのOS毎に書いているのであんまりクロスプラットフォームで開発している!みたいな実感は湧きませんでした。アーキテクチャ等のベストプラクティスみたいなものがわかればもう少し共通化できる部分が増やせるのかなと思いました。またKMMでの開発を通してAndroid自体の開発に関して少し理解が深まる部分があったのでそれもよかったなと思います。KMM開発においてiOS側からKotlinのflowをobserveする時などもっと具体的な壁もあったのでその辺りもいつか記事に書きたいです。 拙い理解と変な文章の記事をここまで読んでくださってありがとうございました。 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

protocolの基本と種類(swift)

protocolとは 直訳すると議定書・手順。 swiftでは、クラス・構造体などに対してプロパティとメソッドを定義する機能。 プロトコルを適用したクラス・構造体は、プロトコルに定義されているプロパティ・メソッドを必ず定義しなければならない。 つまり、「クラス作成のルールを定めて、それを守らせる」という認識であっているでしょうか。 protocolを定義する sample protocol プロトコル名 { var プロパティ名 : 型 {set get} func メソッド名(引数名 : 型) -> 戻り値の型 } プロパティ名の横には{set get}もしくは{get} を記入する。 {set get} 読み書き可能 {get} 読み込み専用 代表的なプロトコルの種類 CustomStringConvertible インスタンスの文字表現を自由にカスタマイズできるプロトコル。 CustomStringConvertible class Shoe: CustomStringConvertible { let color: String let size: Int let hasLaces: Bool   init(color: String, size: Int, hasLaces: Bool) { self.color = color self.size = size self.hasLaces = hasLaces }    //ここでインスタンスの文字表現をカスタマイズする   var description: String { return "Shoe(color: \(color), size: \(size), hasLaces: \ (hasLaces))" } } let myShoe = Shoe(color: "Black", size: 12, hasLaces: true) let yourShoe = Shoe(color: "Red", size: 8, hasLaces: false) print(myShoe) print(yourShoe) 出力結果 Shoe(color: Black, size: 12, hasLaces: true) Shoe(color: Red, size: 8, hasLaces: false) もしCustomStringConvertibleを使わずに、descriptionも定義しないで出力すると 出力結果(プロトコルなし) Shoe Shoe Equatable 対象が一致するかどうか(イコール)の確認を可能にするプロトコル。 Equable struct Person: Equatable{ var name: String var age: Int //年齢が同じかどうかをBoolで返すメソッド static func == (lhs: Person, rhs: Person) -> Bool{ return (lhs.age == rhs.age) } } //インスタンス化 let yamada = Person(name: "YAMADA", age: 20) let Takahashi = Person(name: "TAKAHASHI", age: 30) //年齢が同じかどうかの確認が可能になる! yamada == takahashi //false Comparable 対象の大小を確認するためのプロトコル。 Equable struct Person: Equatable: Comparable{ var name: String var age: Int //年齢が同じかどうかをBoolで返すメソッド static func == (lhs: Person, rhs: Person) -> Bool{ return (lhs.age == rhs.age) } //年齢が小さいかどうかをBoolで返すメソッド static func < (lhs: Person, rhs: Person) -> Bool{ return (lhs.age < rhs.age) } } //インスタンス化 let yamada = Person(name: "YAMADA", age: 20) let Takahashi = Person(name: "TAKAHASHI", age: 30) //年齢の比較が可能になる! yamada < takahashi //true Codable jsonなどのデータをデコード、エンコードするプロトコル。 Codable struct Person: Codable{ let name = String let height = Int let weight = Int } これだけで、エンコード、デコードともに可能な状態になります。 インスタンス化後に、JsonDecoder/Encoder()などで変換します。 参考文献 App Development With Swift - Unit4 Lesson4.1 _ *本記事は初心者による作成です。細心の注意は払っていますが内容に誤りが含まれる場合がございます。 ご訂正・ご指摘いただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】コールバックを使ってAPIを叩いた結果毎に処理を分岐させる

業務の中で使われていた手法を理解するために自分なりに単純化してみました。 擬似API処理 // APIを叩くメソッド func api(closure: (ApiResult) -> Void) { // APIを叩く処理 // APIから返ってきた結果 closure(.SUCCESS) } // APIの通信結果 enum ApiResult { // 成功 case SUCCESS // 失敗 case ERROR } enumを使って発生しうる結果分岐を用意しておきます。作成した分岐経路を擬似的に作ったAPIを叩くメソッドのclosureに渡し、結果がどのcaseに当てはまるかを返してもらいます。 メソッドの呼び出し メソッドを呼び出して、switchを使い結果毎にやってほしい処理を追加します。 api { result in // resultにapi()の結果が入っている  switch result { // 成功だった時の処理 case .SUCCESS: print("SUCCESS") // 失敗だった時の処理 case .ERROR: print("ERROR") } // 出力結果 "SUCCESS" api()で.SUCCESSを返しており、resultの中には.SUCCESSが入っているので、 結果として"SUCCESS"が返されます。 参考記事 Swiftのクロージャによるコールバックの説明 - Qiita 間違っている点等あればご指摘いただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS14】UITableViewをUICollectionViewに書き換える練習

UITableViewが非推奨の方向らしい? 資料を参考にすれど、む・・・むずかしい。 こういうときは、簡単なサンプルを書いてみようと思い そういう事をされている方の記事を見つけました。 こちらの記事を参考させて頂き、今回はセクションを使わない UICollectionLayoutListConfiguration.Appearance.plain なものを作りたかったので、改変した結果がこちらです。 自分の分かる範囲では、Sectionは完全に消せなかったのでIntにして layoutConfig.headerMode = .none としてみました。 【新しい疑問点】 dataSourceは新しくなったけどdelegateはそのまま? => そのままらしい import UIKit class ViewController: UIViewController { @IBOutlet weak var collectionView: UICollectionView! let data = ["A", "B", "C"] var collectionViewDataSource: UICollectionViewDiffableDataSource<Int, String>! override func viewDidLoad() { super.viewDidLoad() var layoutConfig = UICollectionLayoutListConfiguration(appearance: .plain) layoutConfig.headerMode = .none let layout = UICollectionViewCompositionalLayout.list(using: layoutConfig) collectionView.collectionViewLayout = layout collectionViewDataSource = createDataSource() reloadList() } func createDataSource() -> UICollectionViewDiffableDataSource<Int, String> { let normalCell = UICollectionView.CellRegistration<UICollectionViewListCell, String> { (cell, indexPath, text) in var content = cell.defaultContentConfiguration() content.text = text cell.contentConfiguration = content } return UICollectionViewDiffableDataSource<Int, String>(collectionView: collectionView) { (collectionView, indexPath, text) -> UICollectionViewCell? in return collectionView.dequeueConfiguredReusableCell(using: normalCell, for: indexPath, item: text) } } func reloadList() { var snapshot = NSDiffableDataSourceSnapshot<Int, String>() snapshot.appendSections([0]) snapshot.appendItems(data) collectionViewDataSource.apply(snapshot) } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift NSAttributedString

NSAttributedString storyboardやUILabelのプロパティを操作して装飾をすることができるが、NSAttributedStringを使用することでより細かい装飾をすることができる。 辞書型で指定した文字列の装飾をNSAttibutedStringの引数にいれることによって描画できる var dictionary:[NSAttributedString.Key: Any] = [ NSAttributedString.Key.font: NSFont.systemFont(ofSize: 40), NSAttributedString.Key.foregroundColor: NSColor.orange, NSAttributedString.Key.strokeColor: NSColor.black, NSAttributedString.Key.strokeWidth: -3 ] //辞書型を引数(attributeds)に入れる。 let attrString = NSAttributedString(string: "Swift", attributes: dic) storyboardだとどうしてもうまく表現されないことも多々あったり、動的に文字列をうまく変更したい場合はよく使うものかな それぞれのvalueは色が入ったり、サイズが入ったり、セットで描画されるのもあるので拡張機能として、再利用可能な形にしてから使い回す使用方法が多い。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む