- 投稿日:2019-11-15T16:36:11+09:00
Swift Package Managerで'No Such Module'した時の対処法
はじめに
iOSアプリ開発において、Xcode11からSwift Package Manager(以下、SPM)を公式に使えるようになりました。
導入を検討している人も多いかと思いますが、躓いた箇所があったので対処法の一例としてメモ程度に残しています。なお、今回の対処法は「Getting 'no such module' error when importing a Swift Package Manager dependency - Stack Overflow」に記述がありました。
What
新しくライブラリを導入するにあたりSPMで管理していく方針にしました。
Xcodeで
File → Swift Packages → Add Package Dependency...
と順当にライブラリを取り入れるも*.swiftファイルで
import Hoge
と書いてもbuildできず、No such Module
のエラーが出るばかりでした。How
以下のスクリプトを
Targets → Build Phases → New Run Script Phase
でCompile Sources
の手前に記述しますif [ -d "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" ] && [ "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" != "${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/" ] then cp -f -R "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" "${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/" fi今回は
Fix SPM
という名称にしました。Why
SPMは暗黙的にXcodeプロジェクトのConfigurationを利用しており現状でデフォルトの
Release
とDebug
にしか対応していないようなので、ビルドしようとしているConfigurationと異なれば、Release用をコピーすることで解決しています。Xcodeとしては既知の仕様らしいです。
参考: Custom build configuration namesまた、上記サイトでは他の解決策も紹介されていたので是非参考にしてください
参考: Swift Package Hack - Álvaro Royo - Medium現状はなんとかなりましたが、もっと最適な解決策があれば教えていただきたいです!
ありがとうございました!
- 投稿日:2019-11-15T14:52:21+09:00
R.generated.swift No such module 'プロジェット名' エラーの対処について
R.generated.swift:10:8: No such module 'プロジェット名'エラーの対処について
R.Swiftを導入して、リソース管理していましたが、Configrationをいじった後、上記エラーが出てしまう。
確かに、Configurationsにて既存のDebugとRelease以外にStagingと言うConfigurationsを追加してので、色々設定したので、その周りに問題があったか、調べたが、気になる所は見つからず...
Releaseモードでは上記エラーが無く無事にコンパイルされるが、DebugモードとStagingモードに切り替えてコンパイルすると、必ず、R.generated.swift:10:8: No such module 'プロジェット名' と言うコンパイルエラーが出てしまう。
一応、問題が発生するR.generated.swiftが生成する時、Releaseモードでは、import 'プロジェット名'が追加されないが、DebugモードとStagingモードでは自動に追加されるのだ。
ちらっとググって見ると、以下のように事例があるようですが、今度の問題と違うよう。。。
https://github.com/mac-cain13/R.swift/issues/326暫くウロウロしてから、ついに、生成されるR.generated.swiftに目を通すようにした。
まず、'プロジェット名'をキーワードをして、R.generated.swift内部を検索して見たら、
複数のクラスの前に追加されたことがわかった。R.generated.swiftimport Foundation import Rswift import UIKit import 'プロジェット名' // ... ... /// Segue identifier `toProduct`. static let homeToProduct: Rswift.StoryboardSegueIdentifier<UIKit.UIStoryboardSegue, HomeViewController, 'プロジェット名'.ProductViewController> = Rswift.StoryboardSegueIdentifier(identifier: "toProduct") // ... ...なぜ、他のViewControllerクラスにはこのようクラス名前に、こんな'プロジェット名'がついてないか、直接、ProductViewControllerのストリボードを確認。すると
問題が起こすクラスにチェックがついてないまま、つまり、ビルド時洗濯されたConfigrationより生成されるモジュール名が異なる様に設定すると、ここにも影響がでることだった。
さっさと、Inherit Module From Targetにチェックを入れ、ビルドを掛け直してみる。
Release,Staging,Debug 全部生成されるR.generated.swiftに
import 'プロジェット名'追加されなくなり、クラス前にも'プロジェット名'.が消えて、無事ビルドに通った。やった!
iOS、Androidアプリの制作なら、hq7781@gmail.comまで、
法人並みに、信頼且つ満足できる製品を納品いたします
- 投稿日:2019-11-15T00:23:24+09:00
SwiftUIで無限スクロールを実装する
はじめに
みなさんSwiftUI使っていますか?
自分も最近触り始めましたが、
- ひたすらAutoLayoutのエラーと戯れたり
- 都度ビルドして数秒放心状態になって時間を無駄にしたり
もなくなり開発効率が爆上がりしました
![]()
さて今回はそんなSwiftUIを使って、↓のような無限スクロールを実装する方法を紹介します。
Vertical Horizontal ただし先に結論を言っておくと、SwiftUI側で機能的に足りないところがあり、思ったように実装できなかった箇所も多いです。そこについても詳しく説明していきます。
ライブラリとしても公開したのでとりあえず動かしてみたいという方はこちらをご覧ください。
最近デザインも勉強してるので、テンション上げるためにロゴも作ってみましたhttps://github.com/kazuooooo/SwiftUIInfinityScroll
対象読者
- SwiftUIで無限スクロールを実装したい方
- ウォンバットが好きな方
実装の仕組み
無限に要素を生成する仕組み
無限スクロールを実装する方法…一度自分の頭でも考えてみて欲しいですが、どんな方法が思いつきますか?
自分も3つくらいアイデアを思いついたのですが、最終的に採用したのが以下の図のようなものです。
- N個の要素を用意しておき画面に配置します。ユーザーは画面をスクロールしていきます
- N - 1番目のViewがonAppearしたタイミングでN+1個目を生成します。
- 要素が1つ増えるので今見ているViewはN - 2番目になります。
- 2に戻る。無限に続く...
実装としては@ObservedObjectを使って、ViewModelを作成して状態管理を行なっています。
以下のようなものになっています。(※重要な部分だけ抜粋しています。)viewmodelopen class InfinityScrollViewData: ObservableObject { @Published var items: [G.Item] func onAppear(page: Int){ // 初期化 if(isOnInitialize(appearedPage: page)) { items.append(generator.generateItem(page: 1)) items.append(generator.generateItem(page: 2)) } // N - 1個目の要素が表示されたら要素を追加する if(needToAppendItem(appearedPage: page)) { items.append(generator.generateItem(page: lastPage + 1)) } } private func isOnInitialize(appearedPage page: Int) -> Bool { page == 0 && items.count == 1 } private func needToAppendItem(appearedPage page: Int) -> Bool { page == lastPage - 1 } }viewpublic struct InfinityScrollView: View { // 状態管理 @ObservedObject var scrollViewData: InfinityScrollViewData<G> public init(generator: G, direction: InfinityScrollDirection) { self.scrollViewData = InfinityScrollViewData<G>(generator: generator) self.scrollDirection = direction } public var body: some View { GeometryReader { geometry in VStack(alignment: .center) { List(self.scrollViewData.items) { item in item .onAppear(){ // Viewが画面上に表示されたらonAppearをコール self.scrollViewData.onAppear(page: item.page) } } } } } }横方向のスクロール
ListViewには横方向がないので、横方向のスクロールに対応するために、なんとLIstを90度に回転させてさらに中の要素を
-90度回転させるという暴挙に出ています笑... var viewRotation: Double { switch(scrollDirection){ case(.horizontal): return 90 case(.vertical): return 0 } } ... public var body: some View { GeometryReader { geometry in VStack(alignment: .center) { List(self.scrollViewData.items) { item in item // 回転させた分中身を逆方向に回転 .rotationEffect(.degrees(self.viewRotation)) } // Listを横に回転 .rotationEffect(.degrees(-self.viewRotation)) } } }ここで
「いやそもそもお前Horizontal View使えばええやんwww知らんのかいなwww てかそもそもなんでScrollView使わんの? 」
と思った関西の方。
はい、自分もそう思いました。
ここからはScrollViewで実装しようとしておきた問題点について書いていきます。ScrollViewの問題点
ScrollViewを使えば以下のようなコードで横方向のスクロールは比較的容易に実装することができます。
ScrollView (.horizontal, showsIndicators: false) { HStack { //contents } }.frame(height: 100)しかしながらこれを無限スクロールにしようとすると色々問題が起きてきます。
OnAppearが画面上になくても発火する
まず、ScrollViewに要素を追加すると、画面上に表示されている / いないに関わらずOnAppearが発火します。
なのでList Viewで使った上記の無限に要素を生成する仕組みを使うことができません。スクロール位置を取れない
じゃあ別にScroll位置を取得して、その位置に応じて要素生成すれば良いとなりますが、それも自分が探した限りは見当たりませんでした。
なので、もしやるとするならGeometryReaderをつけた見えない要素をScrollViewの端とかに配置して、Scroll位置を取得してそこから現在の表示領域を計算するという方法がありますが、なんか強引な気がしますしロジックが複雑になりそうな気がして、一旦見送りました。ListView & ScrollView共通の問題
手前のViewを消すと位置がずれる
パフォーマンスを考えて、見えなくなったViewを消す処理を入れてみたところ、手前のViewを消すと表示位置がずれてしまいました。そのため、表示されなくなったViewも特に何もせずにSwiftUIに処理を任せています。
ある程度最適化されているのか、現状そこまでパフォーマンスの問題は起きていませんが結構気持ち悪い状態です。描画位置をコントロールできない
上記の位置がずれる問題をずれた分描画位置を逆方向にずらせば、動作させられることも考えられますが、その機能も現状のSwiftUIにはありません。
また、
- フリックしたときに1ページずつぴったりのところに表示する機能
- 逆方向へのスクロール
も入れたかったのですが、同様の機能がなく今回は実装できていません。
UIKitにはcontentOffsetがあるので、この機能がSwiftUIにも入ることを期待しています。まとめ
いかがだったでしょうか?
ちょっと今回作成したライブラリをそのままプロダクトに使えるほどではないので、それを期待した方はごめんなさい ?♂️
自分のプロダクトでも使いたいのでSwiftUIのバージョンアップを待ちつつももう少しクオリティ上げていくつもりです。
(Contributeも歓迎です)
感想としては
「SwiftUiまだまだ痒いところに手が届かないなー泣」
という感じです。
とはいえ
「開発しやすくて好きだよSwiftUI」
という気持ちもかなりあるのでこれからも使っていく所存です。その他 〜SwiftUIおすすめリンク〜
日本語
SwiftUI実践入門
とても薄い本にしっかりとSwiftUIの重要な部分がまとまっています。
日本語 & わかりやすいのでシュッと入門するなら英語のチュートリアルやるよりいいかもしれません。英語
Swift公式チュートリアル
言わずもがな。Appleがかなり力を入れて作ったと思われる。むしろこっちのWebの実装がどうなってるのか気になる。HackingWithSwift
これはどうやるんだろう?というのを調べるときに短いサンプルコードがあるので、とても良い。
またState周りが若干SwiftUIの鬼門だがこの動画が非常にわかりやすく説明されている。
(※若干バージョンが古い。ただ概念は同じなので参考になる)SwiftUI使ったことない方は是非試してみてください
![]()
- 投稿日:2019-11-15T00:15:21+09:00
swift⇄objective-cのファイル間での連携
Swift⇄objective-cのファイル間でのやりとりの方法を稀に忘れてしまうので備忘録としてここに記す。
objective-cからSwiftを呼び出す方法
Swiftのクラス名に@objcを書きNSobjectを継承させる
@objc class test: NSObject { func test1() { print("テスト") } }呼び出したいメソッドに@objcを書く
@objc class test: NSObject { @objc func test1() { print("テスト") } }ちなみにクラスの頭に@objcMembersを書くとクラス全体に対して一括して@objcとして設定することが可能
@objcMembers class test: NSObject { func test1() { print("テスト") } }Swiftからobjective-cを呼び出す方法
<プロジェクト名>-Bridging-Header.h
<プロジェクト名>-Bridging-Header.hに呼び出すobjective-cのヘッダーファイルを記載してあげる
#import "ObjcTest.h"
objective-cの.m実装ファイル側でimportする
#import "名前-swift"
objective-cのヘッダーファイルに参照するメソッドを書く
- (int)test;締め
必ずビルトかけてから参照しよう!
漏れがあるかもしれないのでほかあれば教えてください。