- 投稿日:2021-03-25T23:22:08+09:00
iosのsafariで電話番号がリンクになってしまうのを1行で無効化
どうも7noteです。safariの自動電話リンクを無効化させる方法
iosのsafariには自動的に電話番号をaタグのリンクに変更して表示する機能がありますが、優秀過ぎて逆に電話番号でないものまでリンクにさせてしまったり、リンクが不必要なところでもリンクを設置してデザインを損なってしまうようなことがあります。
最悪の場合、要素はあるのになぜか文字だけが消えてしまってタップしたら電話かけられるけど見えない、なんてことになる場合もあるようです。
対策方法
たった一行、
<head>
要素の中に以下を追加するだけ!<head> <!-- 以下の1行を追加 --> <meta name="format-detection" content="telephone=no"> </head>チョー簡単!
逆に電話をかけたいものには「tel:」
<a href="tel:09012345678">電話をかける</a>ちゃんと指定しているものは無効にならないので安心。
まとめ
チェックは全デバイス・全ブラウザでやるべきですね。予期せぬ不具合やレイアウト崩れに繋がるので。チェックの時間は入念にとっておきましょう。
おそまつ!
~ Qiitaで毎日投稿中!! ~
【初心者向け】WEB制作のちょいテク詰め合わせ
- 投稿日:2021-03-25T22:10:52+09:00
Flutter アプリ内課金(サブスクリプリプション)の実装
内容
今回初めて、アプリ内課金(サブスクリプション、以下サブスク)の実装をしたので、忘却録として書きます。
2021/3/12 時点のサンプルコードを使用。
サブスク導入でつまずいた部分のみを記載(2021/03/26時点ではiOSのつまずき集のみ)。実装は気が向いたら書く予定。テスト中に何回も購入処理をして、トランザクションが残っていると色々と大変らしいので、YuKiOさんの記事を先に読んでおくことをおすすめします。
前提条件
●App Store, Google Play側の準備ができている(サブスクアイテム作成)
●今回はサブスクのみなので、consumable(消耗品)関係は未対応以下のような感じに複数のアイテムをセットしています
Set<String> _kProductIds = { Platform.isIOS ? 'ios.subscription01' : 'android.subscription01', Platform.isIOS ? 'ios.subscription02' : 'android.subscription02', Platform.isIOS ? 'ios.subscription03' : 'android.subscription03', };環境
PC:MacBook Pro
エディター:Visual Studio CodeFlutter:1.22.6
in_app_purchase:0.3.5+1
つまずき集
サンプルコードを修正しながら実行してみて動きを見ていた時のつまずき集です
サンプルコードはこちら
つまずきその1
シミュレータでデバッグしたら、return されてしまう
productDetailResponse = await _connection.queryProductDetails(_kProductIds); if (productDetailResponse.error != null) { setState( () { _queryProductError = productDetailResponse.error.message; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; _consumables = []; _purchasePending = false; _loading = false; }, ); return; }解決方法:シミュレータで実行していることが原因らしく、実機をつないで、デバッグしたらここで、returnしないらしいです(iPhone11で確認しました)
こちらを参考にしましたつまずきその2
サブスク用に各プラットフォームで作成したアイテムを_kProductIdsにセットしたが、見つからないと画面に表示されてしまう
画像のように[ios.subscription01,・・・] not foundと表示されるproductDetailResponse = await _connection.queryProductDetails(_kProductIds); if (productDetailResponse.productDetails.isEmpty) { setState( () { _queryProductError = null; _isAvailable = isAvailable; _products = productDetailResponse.productDetails; _purchases = []; _notFoundIds = productDetailResponse.notFoundIDs; _consumables = []; _purchasePending = false; _loading = false; }, ); return; }解決方法:こちらもシミュレータではproductDetailResponse.productDetails.isEmptyがtrueになってしまうので、実機に接続することで、解決できました
プラグインの公式ページにも以下のように記載がありましたUse flutter run to install the app and test it. Note that you need to test it on a real device instead of a simulator, and signing into any production service (including iTunes!) with the test account will permanently invalidate it. Sign in to the test account in the example app following the steps in the In-App Purchase Programming Guide.つまずきその3
SANDBOXアカウントの変更ができない
サブスク購入ボタンをクリック時にアカウントを入力するダイアログが表示され、一度、入力したユーザーが変更できなかった
解決方法:こちらの記事を参考にすると、SANDBOXアカウントの変更ができるつまずきその4
SANDBOXアカウントの変更をしてもアイテムの購入ができない
購入しても、purchaseDetails.status が purchased にならず、購入後の処理が実行されない解決方法:SANDBOXアカウントのアドレスを存在するアドレスにする
(たまたまそうなっただけで、正しいかどうかは未確認)つまずきその5
購入フォームが2回表示される
解決方法:つまずき4が解決したら表示されなくなった
githubにも上がってたけど、解決はしていない?
原因は不明のままつまずき6
保護者へのリクエストが表示され、購入が進まない
解決方法:githubに書いてありますが、PurchaseParamメソッドの引数のsandboxTestingをfalseにする。
PurchaseParam purchaseParam = PurchaseParam( sandboxTesting: false, productDetails: productDetails);参考
●各プラットフォームのアイテムの作成方法
・iOS
Qiita・Android
Qiita
- 投稿日:2021-03-25T17:23:27+09:00
【iOS】Core Audioでシンセサイザーを作る
iOSのCore Audioを使って、波形を生成するシンセサイザーを作ってみました。あわせてエフェクターもいくつか実装しています。
その作り方を解説します。
できたもの
音のサンプルをSoundCloudにアップしています。アナログシンセのような感じが出ていると思います。
画面はこんな感じです。鍵盤ではなく、スライダーで音程を調整します。
ソースコードはこちらです。複数のサンプルがありますが、今回解説するのは「Synthesizer」になります。
https://github.com/TokyoYoshida/CoreAudioExamples作り方の概要
Core Audioの構成要素であるAVAudioEngineを使います。波形を生成するレンダー関数を作り、これをソースノードとしてミキサーノードにつなぎ、出力ノードから出力することで、任意の波形を音声出力することができます。
作り方
本稿は「Synthesizer」の解説記事ですが、波形の作り方は「AudioEngeneGenerateWave」の方がシンプルで分かりやすいので、まずはこれをベースに解説していきます。
1. AVAudioEngineを生成する
AVAudioEngineを生成して各種の情報を取得します。
AudioEngeneWaveGenerator.swiftclass AudioEngeneWaveGenerator { var audioEngine: AVAudioEngine = AVAudioEngine() var sampleRate: Float = 44100.0 var deltaTime: Float = 0 var mainMixer: AVAudioMixerNode? var outputNode: AVAudioOutputNode? var format: AVAudioFormat? func initAudioEngene() { mainMixer = audioEngine.mainMixerNode outputNode = audioEngine.outputNode format = outputNode!.inputFormat(forBus: 0) sampleRate = Float(format!.sampleRate) deltaTime = 1 / Float(sampleRate) } // 〜略〜2. レンダー関数を作る
波形をレンダーする関数を作ります。ここではサイン波を生成しています。
sin関数に周波数(currentTone)と、2π(単位時間で1周する)、時間を与えて生成します。currentToneに、440.0を与えればラの音(A)になります。
AudioEngeneWaveGenerator.swift# class AudioEngeneWaveGeneratorの一部 static let toneA: Float = 440.0 var time: Float = 0 lazy var sourceNode = AVAudioSourceNode { [self] (_, _, frameCount, audioBufferList) -> OSStatus in let abl = UnsafeMutableAudioBufferListPointer(audioBufferList) for frame in 0..<Int(frameCount) { let sampleVal: Float = sin(AudioEngeneWaveGenerator.toneA * 2.0 * Float(Double.pi) * self.time) self.time += self.deltaTime for buffer in abl { let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer) buf[frame] = sampleVal } } return noErr }3. 音を鳴らす
実際に音を鳴らします。
AVAudioEngineに、AVAudioSourceNodeやAVAudioOutputNodeをコネクトしていくことで、音の生成から出力までをルーティングすることができます。合わせて、AVAudioSourceNodeのフォーマットを指定したり、AVAudioOutputNodeのボリュームを設定します。
準備ができたら、AVAudioEngineのstartメソッドで再生開始です。
AudioEngeneWaveGenerator.swift# class AudioEngeneWaveGeneratorの一部 func start() { refData.frame = 0 let inputFormat = AVAudioFormat(commonFormat: format!.commonFormat, sampleRate: Double(sampleRate), channels: 1, interleaved: format!.isInterleaved) audioEngine.attach(sourceNode) audioEngine.connect(sourceNode, to: mainMixer!, format: inputFormat!) audioEngine.connect(mainMixer!, to: outputNode!, format: nil) mainMixer?.outputVolume = 0 do { try audioEngine.start() } catch { fatalError("Coud not start engine: \(error.localizedDescription)") } }これで音が鳴るようになりました。
4. 音の再生を止める
音の再生を止めるメソッドも作っておきます。
AudioEngeneWaveGenerator.swift# class AudioEngeneWaveGeneratorの一部 func stop() { audioEngine.stop() }5. 音を差し替えられるようにする
ここからは、サンプル「Synthesizer」の説明になります。
ここまではAudioEngeneWaveGeneratorクラスを実装していましたが、ここからはSynthesizerクラスになります。実装はほぼ同じなので、追加したところだけを説明します。
まず、音を差し替えたり、エフェクターをつなげたりできるようにします。
これは、波形の生成をする関数をprotocolで抽象化し、それぞれの部品をクラスにすることで実現できます。
今回は次のような構成で作ってみました。protocol AudioSourceは、音程(tone)と、時間を与えるとその時点の波形を出力するメソッド(signal)を持ちます。
Synthesizer.swiftprotocol AudioSource { var tone: Float {get set} func signal(time: Float) -> Float }そして先程説明したレンダー関数の波形生成部分は、AudioSourceに委譲するようにします。
Synthesizer.swift// class Synthesizerの一部 private var audioSource: AudioSource? lazy var sourceNode = AVAudioSourceNode { [self] (_, _, frameCount, audioBufferList) -> OSStatus in let abl = UnsafeMutableAudioBufferListPointer(audioBufferList) guard let oscillator = self.audioSource else {fatalError("Oscillator is nil")} for frame in 0..<Int(frameCount) { let sampleVal: Float = oscillator.signal(time: self.time) // この部分 self.time += self.deltaTime for buffer in abl { let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer) buf[frame] = sampleVal } } return noErr }6. オシレーターを作る
サイン波を発生させるオシレーターを作ります。先ほど紹介したサイン波の数式をclassでくるんでいるだけです。
updateToneメソッドの詳細は割愛しますが、音色を急に変化させてもゆっくりなめらかにcurrentToneを変化させるための処理です。これがなくても動作しますが、音色を変化させたときにプチプチとノイズが入ります。
Synthesizer.swiftclass SinOscillator: ToneAdjuster { // ToneAdjusterはupdateToneの実体を定義するためのもので、それ以外はAudioSourceプロトコルと同じ override func signal(time: Float) -> Float { updateTone() return sin(currentTone * 2.0 * Float(Double.pi) * time) } }7. ミキサーを作る
ミキサーは、AudioSouceを継承しつつ、エフェクターを加えることができるプロトコルとして宣言します。
Synthesizer.swiftprotocol Mixer: AudioSource { func addEffector(index: Int, effector: Effector) func removeEffector(at index: Int) }ミキサーの実装であるAudioMixerは、オシレータを1つ、エフェクターを複数保持しています。
Synthesizer.swiftclass AudioMixer: Mixer { private var oscillator: Oscillator private var effectors: [Int:Effector] = [:] // 〜略〜ミキサーのsignalメソッドが、実際にレンダー関数から呼ばれるところです。
オシレーターに時間を渡して波形を生成させ、エフェクターに波形を渡し、その出力をさらに別のエフェクターに渡すということを繰り返しています。
セマフォによる排他制御をしているのは、波形の処理中に画面側でオシレーターやエフェクターを変更したときの競合を避けるためです。
Synthesizer.swift// class AudioMixerの一部 let semaphore = DispatchSemaphore(value: 1) func signal(time: Float) -> Float { semaphore.wait() var waveValue = oscillator.signal(time: time) for (_,effector) in effectors { waveValue = effector.signal(waveValue: waveValue, time: time) } semaphore.signal() return waveValue }8. エフェクターを作る
エフェクターを作ります。これが一番楽しい作業でした。
エフェクターは、入力した波形に変化を加えて、出力します。
例えばディレイエフェクターは、入力した波形に、バッファにためた過去の波形を少し音量を下げて加えます。そしてその結果をバッファにためます。このようにすると残響効果が現れて、面白い音が作り出せます。
Synthesizer.swiftclass DelayEffector: Effector { var delayCount = 22_100 lazy var buffer = RingBuffer<Float>(delayCount + 1) var index: Int = 0 func signal(waveValue: Float, time: Float) -> Float { func enqueue(_ value: Float) { if !buffer.enqueue(value) { fatalError("Cannot enqueue buffer.") } } if delayCount > 0 { delayCount -= 1 enqueue(waveValue) return waveValue } if let delayValue = buffer.dequeue() { let ret = waveValue + delayValue*0.4 enqueue(ret) return ret } fatalError("Cannot dequeue buffer.") } }なお、各エフェクターの厳密な言葉の定義は違っているかもしれないのでご了承下さい。(ある程度のイメージは合っていると思います)
9. 画面を作る
最後に画面を作ります。鍵盤を付けるのもいいですが、アナログシンセっぽく使えるといいかなと思い、音程はスライダーにしてみました。
参考資料
Building a Synthesizer in Swift
https://betterprogramming.pub/building-a-synthesizer-in-swift-866cd15b731最後に
iOSを使ったARやML、音声処理などの作品やサンプル、技術情報を発信しています。
作品ができたらTwitterで発信していきますのでフォローをお願いします?
https://twitter.com/jugemjugemjugemQiitaは、iOS開発、とくにARや機械学習、グラフィックス処理、音声処理について発信しています。
https://qiita.com/TokyoYoshidaNoteでは、連載記事を書いています。
https://note.com/tokyoyoshidaZennは機械学習が多めです。
https://zenn.dev/tokyoyoshida
- 投稿日:2021-03-25T17:01:49+09:00
SwiftUIでAdMob対応(バナー編)
要約
UIViewControllerRepresentableを使ってSwiftUIでいい感じにバナーを含んだ画面を表示します。
はじめに
SwiftUIでAdMobのバナーが表示したかったので、実装してみました。
AdMobの導入が済んでいる事が前提となります。
https://developers.google.com/admob/ios/quick-start?hl=ja結果
こんな感じでプレビューでバナーが表示できました。
Repository
概要
SwiftUIでAdMob対応をします。
手始めにバナーを表示します。Life Cycle SwiftUI App対応
リポジトリではプロジェクトはSwiftUI Appでのプロジェクトになります。
AdMobはAppDelegateでGADMobileAds.sharedInstance().start(completionHandler: nil)
の初期化を行う必要があるので、ひと手間必要です。AppDelegateを作成し、プロジェクトのAppに
UIApplicationDelegateAdaptor
を追加して、AppDelegate内で初期化するようにします。@main struct SwiftUIAdMobApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } }class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { GADMobileAds.sharedInstance().start(completionHandler: nil) return true } }UIViewControllerRepresentableで広告表示用UIViewControllerを作成
AdMobのバナーには、広告の処理を行うためのフルスクリーンなrootViewControllerが必要です。
SwiftUI単体ではUIViewControllerを用意することができないため、これをUIViewControllerRepresentable経由で用意します。ContainedAdViewControllerでUIViewControllerRepresentableに適合する処理を書きます。
makeUIViewController
で提供するUIViewControllerはStoryboardで用意しました。struct ContainedAdViewController<Content: View>: UIViewControllerRepresentable { let rootView: Content func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIViewController(context: Context) -> UIViewController { let adViewController: AdViewController = UIStoryboard(name: "AdViewController", bundle: nil).instantiateInitialViewController()! adViewController.rootView = AnyView(rootView) return adViewController } func updateUIViewController(_ uiViewController: UIViewController, context: Context) { } typealias UIViewControllerType = UIViewController class Coordinator: NSObject { var parent: ContainedAdViewController init(_ containedAdViewController: ContainedAdViewController) { parent = containedAdViewController } } }Storyboardの初期表示はAdViewControllerというUIViewController継承クラスで、その内部ではSwiftUIを表示するUIHostingControllerコンテナとその下で表示を行うバナーを格納しています。
広告バナーに対するrootViewControllerはAdViewControllerになります。SwiftUI側の表示
SwiftUI側はContainedAdViewControllerのrootViewに表示したいViewを実装すればOKです。
struct ContentView: View { var body: some View { ContainedAdViewController(rootView: Text("Hello, world!").padding() ) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }動作
上記のRepositoryのコードはSceneもサポートしているので、複数画面でもばっちりアダプティブ広告が表示できます。
- 投稿日:2021-03-25T17:00:41+09:00
iOSで5Gかどうか判断する(罠付き)
前提
Xcode Version 12.4 (12D4e)で動作確認しています。
概要
iOSでCoreTelephonyを使って、今使っているキャリアが5Gかどうかを判別します。
また、その際の罠について紹介します。結論
iOS 14.1以上で、5Gかどうかの判断をするようにしましょう。
今使っているキャリアを取得
まず、
CTTelephonyNetworkInfo
を生成します。
CTTelephonyNetworkInfoからserviceSubscriberCellularProviders
を使って端末のキャリアを取得します。
keyとともにCTCarrier
を取得できます。let info = CTTelephonyNetworkInfo() info.serviceSubscriberCellularProviders?.forEach({ (key, value) in print(value.carrierName ?? "") })無線テクノロジーを取得
先ほど得たキーで、
serviceCurrentRadioAccessTechnology
より無線のテクノロジーが何であるかを取得可能です。
CoreTelephonyのドキュメントにRadio Access Technology Constants
があるので、switchを使って目的の無線テクノロジーを検出できます。
CTRadioAccessTechnologyNR
,CTRadioAccessTechnologyNRNSA
は5Gになります。
二つの違いはスタンドアローン型か非スタンドアローン型かの違いです。詳しくはWikipediaを貼っておきます。
https://ja.wikipedia.org/wiki/5G_NR#展開方式let info = CTTelephonyNetworkInfo() info.serviceSubscriberCellularProviders?.forEach({ (key, value) in if let radioAccessTechnology = info.serviceCurrentRadioAccessTechnology?[key] { switch radioAccessTechnology { case CTRadioAccessTechnologyLTE: print("これはLTEです") case CTRadioAccessTechnologyNRNSA: print("これは非スタンドアローンモード 5Gです") case CTRadioAccessTechnologyNR: print("スタンドアローンモード 5Gです") default: print(radioAccessTechnology) } } })iOS 13以下への対応
しかしながら、開発中のアプリがiOS13以降にも対応するとなると、このコードだとエラーが起きます。
'CTRadioAccessTechnologyNRNSA' is only available in iOS 14.0 or newer
ということなので、version checkのコードを追加します。let info = CTTelephonyNetworkInfo() info.serviceSubscriberCellularProviders?.forEach({ (key, value) in if let radioAccessTechnology = info.serviceCurrentRadioAccessTechnology?[key] { switch radioAccessTechnology { case CTRadioAccessTechnologyLTE: print("これはLTEです") default: if #available(iOS 14.0, *) { switch radioAccessTechnology { case CTRadioAccessTechnologyNRNSA: print("これは非スタンドアローンモード 5Gです") case CTRadioAccessTechnologyNR: print("スタンドアローンモード 5Gです") default: print(radioAccessTechnology) } } else { print(radioAccessTechnology) } } } })ここから罠
実はこのコード、iOS 14.0系のOSで実行するとクラッシュします。
理由はCTRadioAccessTechnologyNRNSAとCTRadioAccessTechnologyNRの部分で、
実はこの二つの定数はiOS 14.1からのAPIであるとの記載がWebのドキュメントにあります。
しかしながら、Xcodeで定義にジャンプするとこれらのAPIは
@available(iOS 14.0, *)
でマークされています。
このため、対応OSと定義で不整合が起きて、iOS 14.0に本来無いAPIに触ろうとしてクラッシュするわけです。対策
#available
の部分を iOS 14.1に変更しましょう。
これで本来APIが使えるOSだけがAPIに触ることになります。let info = CTTelephonyNetworkInfo() info.serviceSubscriberCellularProviders?.forEach({ (key, value) in if let radioAccessTechnology = info.serviceCurrentRadioAccessTechnology?[key] { switch radioAccessTechnology { case CTRadioAccessTechnologyLTE: print("これはLTEです") default: if #available(iOS 14.1, *) { switch radioAccessTechnology { case CTRadioAccessTechnologyNRNSA: print("これは非スタンドアローンモード 5Gです") case CTRadioAccessTechnologyNR: print("スタンドアローンモード 5Gです") default: print(radioAccessTechnology) } } else { print(radioAccessTechnology) } } } })ちなみに
Xcode Version 12.5 beta 3 (12E5244e)だと、この問題は解決されていて、CTRadioAccessTechnologyNRNSAとCTRadioAccessTechnologyNRは
@available(iOS 14.1, *)
でマークされています。
- 投稿日:2021-03-25T05:12:32+09:00
Swift メモリリークのつまずきポイント
「メモリリークが発生していることはわかるが、その原因がわからない。」
このような質問を、聞くことが多々あります。
行き詰まる方の助けになれればと思い、
今回、そのポイントをまとめてみました。
知ってる方は「あるある!!」と思っていただければ幸いです。メモリリークって何?
何かが邪魔をして、画面を閉じてもインスタンスが残ったまま消えないという現象。
これでは、メモリが解放されません。つまり、これを繰り返していると どんどん動作が重くなり、最悪の場合 アプリが落ちます。
それだけは避けたいですね。
ということで…
早速、見落としやすいポイントを見てみましょう。メモリリークのパターン・解消
メモリリークの発生箇所は、決まって2つ。
- クロージャー (
closure
)- デリゲート(
delegate
)では、何が原因?
全て 強参照 が原因です。
1. クロージャー (closure)
クロージャー とは、処理(関数)そのものを「値」として 変数に格納できる もの。
引数として使えば 、特定の処理を設定して…呼び出した後に実行したり…ムフフ…
とにかく便利なんです。彼。ただ、そのままにしておくとメモリリークを引き起こします。
ご安心ください。弱参照
[weak self]
で全て解決します。class TestClass { var text = "test" var testClosure: () -> () = { [weak self] in // <- ここ print(self?.text) } }クロージャー を生成した際、クラス内の変数・関数を呼び出していないか(
self
を普通に、または暗黙的に使っていないか)をチェックしてください。もし使っていたら、
[weak self]
を使いましょう。…ああ、あともう一つ。
DispatchQueue
DispatchQueue.main.async
を使う際も注意してください。DispatchQueue.main.async { [weak self] in // <- ココ self?.image = image }
DispatchQueue
もまた、クロージャーみたいなものです。
初見の方は、ここを見落としやすいです。2. デリゲート (delegate)
外側にあるクラスから処理を呼び出したい時に使う、アレですね。
その点は、もうご存知かと思います。こちらも強参照によるもの。
デリゲートを自作する場合、そのままだとメモリリークが発生します。ここでの ポイントは2つ です。
- delegate を格納する場合、弱参照
weak
にする。- delegate の
protocol
は、AnyObject
を継承しておく。class TestClass { weak var delegate: SampleDelegate? ... }protocol SampleDelegate: AnyObject { ... }これだけ。
URLSession
URLSession
を使用する際にも注意。元々、デリゲート自体が強参照されています。
流石に、ここを弱参照にすることはできませんね。ここでは、対応策が2つ ほどあります。
URLSession.shared
を使う(こちらを使っているなら、そもそも発生しない。)invalidateAndCancel()
等の 専用メソッド で、セッションを明示的に無効化する。前者は
URLSession.shared
が使える場合ですね。
細かい設定は使えませんが、大抵はこちらで良いかと。後者の専用メソッドについては、こちらの記事が参考になります。
[weak self]
は必須ではない。要は、「違った書き方もある」という意味ですね。
[weak self]
が必須ではないだけ です。代わりになるような 処理は必ず必要 です。その他、参考になりそうな記事
【メモリリークの仕組み】
本記事は例の紹介です。
極力、別の話題は省いています。そのため、強参照が原因である理由は説明していません。(循環参照)
メモリリークの仕組みに関しては、以下の記事が参考になります。ARC(Automatic Reference Counting) と呼ばれる機能を、swiftは有しています。
今回の問題も、全てはこれに基づいていますね。【メモリリークが発生しているか、チェックしたい】
また、
メモリリークが発生しているか、チェックしたい 場合はこちら。
- iOSアプリのメモリリーク (Memory Leak) の検出とデバッグの方法を、ネコに関するプログラムで学びましょう。 //qiita
- [iOS] メモリリークをXcodeでチェックして、リークしないようにしたい! //DevelopersIO
ということでね
メモリリークのパターン(例)をまとめました!!
(パターン紹介以外はほぼ記事のリンク紹介ですが)原因に関する詳しい事柄は、多くの方々が記事にまとめてくれていると思います。
そのため、本記事は「メモリリークから原因につなげるための記事」として書きました。原因さえわかれば、それらの記事にたどり着けると思います。
思い当たる内容はあげましたが、その他にもある場合は教えていただけると助かります!
- 投稿日:2021-03-25T02:18:42+09:00
iOSネイティブアプリのhttp通信の内容を確認する
はじめに
iOSネイティブアプリのhttp通信の内容を確認したい方向けです
こんな時に便利
- webViewで表示されているページのurlが知りたい時
- ビルド済みのアプリでAPIのレスポンスを確認したい時
- 人のアプリがちょっと気になる時
いるもの
- mac
- iPhone
- Fiddler Everywhere
- 確認したいアプリ
目次
FiddlerEverywhereのインストール
FiddlerEverywhereをインストールしてください
FiddlerEverywhereの設定
- HTTPS -> Capture HTTPS traffic にチェックを入れる
- Connentions -> Act as system proxy on startupとAllow remote computers to connectにチェックを入れる
iPhoneの設定
- macとiPhoneを同じWi-Fiに接続する
- プライベートIPアドレスを調べる
$ networksetup -getinfo Wi-Fi
で調べられます- 設定 -> Wi-Fi から接続中のWi-Fiを選択しプロキシを構成
サーバのところはプライベートIPアドレスを入力してください
- iPhoneのsafariから
ipv4.fiddler:8866
にアクセスしFiddlerRoot certificateからプロファイルをダウンロード
- 設定 -> 一般 -> プロファイル からダウンロードしたプロファイルを許可
確認