- 投稿日:2020-07-28T23:13:18+09:00
UIPageViewControllerをEnumで管理する
実務でコードを書いてる時に、UIPageViewControllerをEnumでいい感じに書いているコードに出会ったので、
自分のための備忘録も兼ねて、ご紹介します実行環境
Xcode: Version 11.6
実装
PageViewController親の実装
PageViewControllerの親になるクラスには、Enumを使って、載せたいコンテンツを定義します
今回は例として以下のようなEnumを定義します
(Enumのコレクションの扱いを楽にする為に、CaseIterableを継承させています)
また、UIPageViewControllerはすでに宣言されていることとします(今回のサンプルはコードで宣言しています)BasePageViewController.swiftenum Content: CaseIterable { case first, second, third var title: String { switch self { case .first: return "1番目のViewController" case .second: return "2番目のViewController" case .third: return "3番目のViewController" } } // ここで載せたいViewControllerを定義する func instantiated() -> UIViewController() { switch self { case .first: return ContentViewController(type: self) case .second: return ContentViewController(type: self) case .third: return ContentViewController(type: self) } } }その後、このような感じでViewControllerのインスタンスを作成して、配列を作成します
BasePageViewController.swiftprivate lazy var viewControllers: [UIViewController] = { return Content.allCases.map{ $0.instantiated() } }()あとは、それを定義してあるUIPageViewControllerに渡してあげます
BasePageViewController.swift// ~~~ override func viewDidLoad() { super.viewDidLoad() // ~~~ 省略 // ~~~ // viewDidLoadが走るタイミングで表示させるViewControllerを指定する pageViewController.setViewControllers([viewControllers[0]], direction: .forward, animated: true) } // ~~~残りはUIPageViewControllerのDataSourceの実装が必要となりますが、
これだけでEnumで管理できるようになりました?PageViewController子の実装
Enumで定義した子のViewControllerでは以下のようにContentを受け取っています
ContentViewController.swiftprivate let contentType: BasePageViewController.Content init(type: BasePageViewController.Content) { self.contentType = type super.init(nibName: nil, bundle: nil) } // 以下受け取ったContentを使って実装完成
実際に上のコードを使って作ってみたものがこちら
最後
Enumで管理する方法はすごく便利ですね!
CollectionViewとかもEnumで管理すれば、またいい感じに書けますよね?最後まで読んでいただいてありがとうございました?
何か修正等がございましたら、なんなりとお申し付けください??♂️ソースコード
サンプルプロジェクトのソースコードはこちら
https://github.com/Take111/PageViewControllerSample参考にさせてもらった記事
・CaseIterableとは何か(https://dev.classmethod.jp/articles/wwdc18-swift-4-2-case-iterable/)
- 投稿日:2020-07-28T22:06:31+09:00
Swiftで楽天レシピAPIを表示させてみた。〜Tableviewを作る編〜
やること
前回の続き
Tableviewを作るTableviewを作る
①Tableviewを作るコードを記入
let tableView: UITableView = { let tv = UITableView() return tv }()②
override func viewDidLoad
の中にtableviewを表示させるコードを記入view.addSubview(tableView)③tableviewのサイズを指定
tableView.frame.size = view.frame.sizeシミュレーター
tableviewのデリゲートのメソットの設定
①
override func viewDidLoad
の中にデリゲートのメソットを使えるようにするコードを記入tableView.delegate = self tableView.dataSource = selfこれでデリゲートのメソットが使えるようになりました。
②classの一番上のところに
private let cellId = "cellId"
と記入③一番下にデリゲート使うにあたって必ず必要なメソッドを記入
extension ViewController: UITableViewDelegate,UITableViewDataSource{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 //とりあえず10にしておきました。 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) cell.backgroundColor = .blue //確認のため return cell } }④
override func viewDidLoad
の中にTableViewに上記のメソッドを反映させるコードを記入tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)タイトルをつける
①Main.storyboardに移動してViewControllerを選択し、
Editor
→Embedld
→NavigationController
を選択してNavigationControllerを設置する。
②ViewController.swiftに戻って
override func viewDidLoad
の中にタイトルを表示させるコードを入力するnavigationItem.title = "おすすめレシピ"シミュレーター
無事反映させることができました!
次はこの前のAPIを実際表示させてみたいと思います。初心者なので上記のコードがベストなのかはわからないのですが勉強の記録として書かせていただきます。
より良い方法があればご教授いただけると嬉しいです。
- 投稿日:2020-07-28T21:47:04+09:00
【Swift】テーブルビューのスクロールを滑らかにする方法【KingFisher】
Qiita初投稿です!
iOSアプリ開発をしていると、UITableViewをかなり高頻度で使います。
今日はこのUITableViewを、より滑らかにスクロールできるように改善する方法を紹介します。画像のキャッシュで滑らかに
スクロールがガクつく原因は色々考えられますが、セルに表示する画像のダウンロードに時間がかかって処理が重くなっているケースが多いです。
今回、同じ様に画像表示でアプリが重くなってしまった際にKingFisherというライブラリを使うことで、超簡単に画像をキャッシュしてサクサク動くようになりました。
UITableViewなどのアプリ内で、複数の画像を使う方にはオススメのライブラリです。インストール
Cocoapodsを使いました。
pod 'Kingfisher'
とPodfileに入れてinstallします。使い方
ViewController.swiftlet url = URL(string: "https://example.com/image.png") imageView.kf.setImage(with: url)上記のコードで画像のキャッシュを行うことができ、アプリがサクサク動くようになりました。
画像を初めて表示する際はURLからダウンロード、それ以降はキャッシュから表示するのでダウンロードを待たずに表示することが可能になります。
まとめ
画像のキャッシュと聞くとかなりハードな実装のイメージがありましたが、実際に行ってみると便利なライブラリもあり、短時間で行うことができました。
テーブルビューが滑らかに動かないと悩んでいる方は是非参考にしてみて下さい。
参考
- 投稿日:2020-07-28T20:34:18+09:00
[swift5]tableViewの基礎文法
投稿の経緯
現在独学でiOS開発を学習中。
学習教材はUdemyで人気の高かった【iOS13対応】未経験者がiPhoneアプリ開発者になるための全て iOS Boot Campを使用。(サイトは下記URL)
https://www.udemy.com/course/ios13_swift5_iphone_ios_boot_camp/学習内容をアウトプットします!
tableViewとは
要約するとリスト型のパーツ。tableViewControllerはリスト型のコントローラーということ。
tableViewのデリゲート宣言
UITableViewDelegate
,UITableViewDataSource
の2つが必要。記述するとエラーが発生するが、それはtableViewの実装に必要なメソッドが
不足しているというエラーで、Xcodeの補完に沿ってメソッドを作成すれば解消される。tableViewの構築に必要なメソッド
①セルのセクションを決めるメソッド
ViewController.swiftfunc numberOfSections(in tableView: UITableView) -> Int { }②セルの数を決めるメソッド
ViewController.swiftfunc tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { }③セルを構築する際に呼ばれるメソッド
ViewController.swiftfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { }①〜③は上から順に読み込まれる。
④セルの高さを決めるメソッド
ViewController.swiftfunc tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { }⑤セルがタップされた時に呼ばれるメソッド
ViewController.swiftfunc tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { }備考
tableViewの更新方法▼
ViewController.swifttableView.reloadData()セルのハイライトを消す方法▼
ViewController.swiftcell.selectionStyle = .none
- 投稿日:2020-07-28T18:59:32+09:00
Appleに開発者登録をしたときに、まずすること・してはいけないこと
Appleは最近セキュリティーに厳しく、開発者登録を行い、公証を受けたアプリケーションでないとこのアプリケーションは悪意を持つソフトの可能性がある旨のメッセージが出るようになりました。
そこで開発者登録し、公証を受けた野良アプリを作る際に最初にやるべき事、やってはいけない事を自分の失敗から、同じ失敗をしないように、この記事を読んで下さった方のために纏めたいと思います。
開発者アカウントをXcodeに追加する
証明書の作成はdeveloper.apple.comのサインイン後のページからもできますが、
Xcodeのアカウント環境設定から行うのが確実で良いかと思います。まずは、環境設定の
Accounts
タブへ移動し、画面一番したの+
ボタンで開発者登録したメールアドレスを追加します。
開発者証明を作成する
AppStore
のアカウント作成は色々資料があると思うので割愛します。
今回の目的は、公証を受けた野良アプリ(自サイトで配布するアプリケーション)なので、作成する証明書は、Developper ID Application
になります。
これをXcode
から作成するには、追加したアカウントの右側のペインの右下にあるManage Certificate
ボタンを押します。
すると表示されるのが下の様なウィンドウになると思います。
僕の場合は、すでにアカウントを作成しているので黒文字・グレイ文字の行がいくつかありますが最初は行が一つも存在しないかと思います。ここで、画面左下の
+ ∨
ボタンを押して、Developper ID Application
を選んで下さい。作成した開発者証明書を保存する
上の作業で、
Developer ID Application
に黒文字で証明書が一つ追加されたと思います。
ここで、まず最初にこの証明書をエクスポートして保存しておきます。
証明書の保存方法は黒地のDeveloper ID Application
を右クリックして、Export Certificate...
を選びます。
保存場所は自分がわかる場所ならどこでも構いません。パスワードも人に渡すものでもないのでそう難しいパスワードで無くても良いと思います。
パスワードと、チェック用にもう一度同じパスワードを入力したら、Save
ボタンを押すと付けた名前.p12
というファイルが作成されます。これがプライベートキー付き証明書になりますエクスポートした証明書から証明を復旧する
作成された
.p12
ファイルをダブルクリックすると、KeyChain
に証明書が追加されます。
確認は、KeyChain Access
アプリケーションのログイン
→証明書
で右側のペインに読み込んだ証明書が表示されます。
余談かつ戒め
注意すべきはこの開発者証明書、取り消しが出来なくて、しかも作成できる上限が5つと少し少なめです。
そのため、マシンをクリーンインストールした・買い換えたなどで証明書を発行しまくると、あっという間にパンクしてしまいます。
それを防ぐためにも証明書はこの、プライベートキーがある状態で保存しておき、特別な理由がない限りそれを再インポートして使うべきです。なぜ、Appleの開発者ページで証明書を作成し、ダウンロードしたものをダブルクリックでキーチェーンに追加しないか?というと、ダウンロードされた証明書にはプライベートキーが含まれていないため、ダウンロードした証明書をバックアップして安心してしまうのが危険だからです。
そこで、Xcodeでプライベートキー付きの証明書をダウンロードして、余計なファイルを作らず、それをエクスポートした「プライベートキー付き証明書」(=
.p12
ファイルで.cer
ファイルではないことに注意)を管理することを強くお勧めします。証明書の復旧が必用なときはこのファイルをダブルクリックして
KeyChain
に追加して下さい。
そして、このファイルはマシンが不慮の事態でクラッシュしたときに影響を受けないように、物理的に別なディスクやUSBメモリーカードなどにコピーを作っておくのが良いと思います。お付き合い下さりありがとうございました
- 投稿日:2020-07-28T17:39:06+09:00
iOS: gRPC(Protocol Buffers) + ReactiveSwiftのサンプル
サンプルの説明
- .protoファイルをcocoapodsを利用してビルドする方法です。(grpc-swiftではありません。)
- 生成されたObjective-Cのコードを、Swiftから利用します。
- gRPCでAPIにアクセスする処理をReactiveSwiftのSignalProducer, Signalを返すようにラップしています。
サンプルコードのリポジトリ
https://github.com/yusuke-imagawa/iOS_gRPC_ReactiveSwift_sample
.protoファイル, gRPC, ReactiveSwiftの連携部分だけを実装しています。
UIは実装していません。使い方
- pod installを実行。
cocoapodsで.protoファイルから、コードを生成するための設定
.protoファイル
user.proto
report.proto
push_notify.proto
commons.proto
chat.proto
calling.proto
block.proto
account.protoPodfile
# Uncomment the next line to define a global platform for your project platform :ios, '13.2' target 'TalkingSns' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for Talking pod 'RealmSwift', '4.4.0' # GRPC_Client pod 'RemoteClient', path: './RemoteClient' pod 'ReactiveSwift', '~> 6.1' pod 'ReactiveCocoa', '~> 10.1' pod 'CocoaLumberjack/Swift' target 'TalkingSnsTests' do inherit! :search_paths # Pods for testing end target 'TalkingSnsUITests' do # Pods for testing end endRemoteClient/RemoteClient.podspec
Pod::Spec.new do |s| s.name = "RemoteClient" # .protoファイルの更新時に、versionを変更して pod install する。 s.version = "0.0.21" s.license = "New BSD" s.authors = { 'imagawa' => 'test@example.com' } s.homepage = "http://example.com" s.summary = "grpc client" s.source = { :git => 'https://github.com/yusuke-imagawa/talking-ios.git' } s.ios.deployment_target = "7.1" s.osx.deployment_target = "10.9" # Base directory where the .proto files are. src = "./proto" # Run protoc with the Objective-C and gRPC plugins to generate protocol messages and gRPC clients. s.dependency "!ProtoCompiler-gRPCPlugin", "~> 1.0" # Pods directory corresponding to this app's Podfile, relative to the location of this podspec. pods_root = '../Pods' # Path where Cocoapods downloads protoc and the gRPC plugin. protoc_dir = "#{pods_root}/!ProtoCompiler" protoc = "#{protoc_dir}/protoc" plugin = "#{pods_root}/!ProtoCompiler-gRPCPlugin/grpc_objective_c_plugin" # Directory where the generated files will be placed. # dir = "#{pods_root}/#{s.name}" s.prepare_command = <<-CMD #{protoc} \ --plugin=protoc-gen-grpc=#{plugin} \ --objc_out="./src" \ --grpc_out="./src" \ -I #{src} \ -I #{protoc_dir} \ #{src}/*.proto CMD # Files generated by protoc s.subspec "Messages" do |ms| ms.source_files = "src/*.pbobjc.{h,m}", "src/**/*.pbobjc.{h,m}" ms.header_mappings_dir = '.' ms.requires_arc = false # The generated files depend on the protobuf runtime. ms.dependency "Protobuf" end # Files generated by the gRPC plugin s.subspec "Services" do |ss| ss.source_files = "src/*.pbrpc.{h,m}", "src/**/*.pbrpc.{h,m}" ss.header_mappings_dir = '.' ss.requires_arc = true # The generated files depend on the gRPC runtime, and on the files generated by protoc. ss.dependency "gRPC-ProtoRPC" ss.dependency "#{s.name}/Messages" end s.pod_target_xcconfig = { # This is needed by all pods that depend on Protobuf: 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1', # This is needed by all pods that depend on gRPC-RxLibrary: 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES', } end各APIのクライアント
PushNotifyApiClient.swift
AccountApiClient.swift
BlockApiClient.swift
CallingApiClient.swift
ChatApiClient.swift
ReportApiClient.swift
UsersApiService.swift例:ReportApiClient
import RemoteClient import ReactiveSwift class ReportApiClient { static let shared = ReportApiClient() private init() {} private var service: ReportService? { return GrpcServiceLoader.shared.getReportService() } func reportUser(toUserId: Int) -> SignalProducer<Bool, Error> { return SignalProducer<Bool, Error> { (observer, lifetime) in let request = PostReportRequest() request.toUserId = Int64(toUserId) self.service?.rpcToPostReport(with: request) { (response: GPBEmpty?, error: Error?) in if let error = error { observer.send(error: error) return } observer.send(value: true) observer.sendCompleted() }.startWithHeaders() } } }request時にユーザー認証用のheaderを設定
- この処理は必須ではないです。
- ユーザーの認証情報の設定箇所を共通化するため、独自に実装しています。
extension GRPCProtoCall { func startWithHeaders() { if let userId = CurrentUserService.getCurrentUserId(), let apiToken = CurrentUserService.getApiToken() { requestHeaders.addEntries(from: ["user_id":String(userId)]) requestHeaders.addEntries(from: ["api_token":apiToken]) } start() } }request時に startWithHeaders() を呼び出す。
BlockApiClient.swiftfunc block(toUserId: Int) -> SignalProducer<Bool, Error> { return SignalProducer<Bool, Error> { (observer, lifetime) in let request = BlockRequest() request.toUserId = Int64(toUserId) self.service?.rpcToBlock(with: request) { (response: GPBEmpty?, error: Error?) in if let error = error { observer.send(error: error) return } observer.send(value: true) observer.sendCompleted() }.startWithHeaders() } }参考記事
・Xcode(Swift) + CocoaPodsでgRPCのClientを生成してServiceを呼び出すまでの流れ
SwiftでGRPCを動かす方法と基本的な挙動について
公式のサンプル
- 投稿日:2020-07-28T15:29:57+09:00
Xcode11でストーリーボードなしで画面ロードする方法
概要
比較して学ぶRxSwift入門という本1がKindle Unlimtedで読めるので読み進めているところです。
とてもわかりやすく良い書籍なのですが、最新のXcode11ではつまづく点があるのでその解決方法を書きます。この書籍のサンプルコードを実行するためには「開発を加速させる設定」なる設定が必要で、それはストーリーボードをプロジェクトから削除した上でxibファイルで作成した画面をアプリ起動時にロードする設定を指すのですが、書籍に書いてある通りに設定してもXcode11ではうまく動きません。2
この原因はどうやらXcode11から導入されたSceneDelegateが関係しているようで、これに関連するファイル・コード・設定を削除することで解決できます。
この、SceneDelegateに関連するファイル・コード・設定を削除する手順を含めた「開発を加速する設定」を以下に示します。
Xcode Version 11.5 (11E608c) で確認しています。
手順
おおまかな手順は以下の通りです。
1. Single View App のプロジェクトを作成する
2.Main.storyboad
、SceneDelegate.swift
を削除する
3.Info.plist
を編集する
4.AppDelegate.swift
を編集する
5.ViewController.xib
を作成する手順ごとに具体的に見ていきます。
1.Single View Application のプロジェクトを作成する
- XCodeを起動し、File>New>Projectをクリックすると出てくるダイアログで
Single View App
を選択し、Next
をクリックします。- 次のダイアログでプロジェクト名などを設定します。この時、
Language
はSwift
、User Interface
はStoryboard
を選択しておきます。設定したらNext
をクリックします。- ファイルの保存先を指定して
Create
をクリックします。32.Main.storyboad、SceneDelegate.swiftを削除する
普通の手順です。画面左端のProject Navigaterから
Main.storyboad
と、SceneDelegate.swift
を削除します。(Move To Trashを選択します) 4
3. Info.plistを編集する
画面左端のProject Navigaterから
Info.plist
を選択し、設定リストを表示します。
次に以下のエントリを選択し、マイナス記号をクリックして削除します。
- Application Scene Manifest 5
- Main storyboard file base name
4. AppDelegate.swiftを編集する
画面左端のProject Navigaterから
AppDelegate.swift
を開き、コードを全て削除した後、以下の内容をコピー&ペーストします。import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds) let navigationController = UINavigationController(rootViewController: ViewController()) self.window?.rootViewController = navigationController self.window?.makeKeyAndVisible() return true } }ちなみに、Xcode11では初期状態ではコードは以下のようになっていますが、ストーリーボードを使用しない場合、赤枠の部分のSceneDelegate関連のメソッドは不要となるため削除します。
5. ViwController.xibを作成する
この手順は書籍と同じです。
画面左端のProject Navigaterを右クリックし、NewFile>ViewでViewを選択し、Nextをクリック、次の画面で ファイル名(Save As:)をViewController.xib
として Createをクリックします。
次にビューエディタの左側のPlaceholders>File's Owner
を選択し、ClassをViewController
に変更します。
次にOutletsのviewとViewControllerのViewを接続します。
確認
Build&Runしてシミュレータに以下の画面が出ればOKです。
念のため
ViewController.swift
のViewDidLoad()
にブレークポイントを設定して狙い通りの画面がロードされていることを確認するのも良い考えだと思います。
画面がうまく出てこない場合
上記手順で
ViewController.swift
の中で定義されたクラス名やViewController.xib
のファイル名を任意のものに変更した場合、以下のような画面が出てうまくいかないかもしれません。
*.xibのファイル名とそれに紐づくViewControlerのクラス名が一致しない場合に上記の現象が起こります。これらは一致させる必要があるようです。以上
https://www.amazon.co.jp/dp/4844398792/ この文中で「書籍」と呼ぶ場合はこれを指します。 ↩
シミュレータでのアプリ起動時に実行時エラーが発生する
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Could not find a storyboard named 'Main' in bundle NSBundle <略> (loaded)'
↩普通の手順ですが、書籍と違い、Xcode11では
User Interface
で SwiftUIが選択できてしまうので注意します。 ↩
SceneDelegate.swift
も削除するところが書籍と異なります。 ↩Xcode11から追加された項目のようです。書籍には登場しません。 ↩
- 投稿日:2020-07-28T14:42:33+09:00
Create ML Object DetectionにピッタリのAnnotation tool(アノテーションツール) IBM Cloud Annotations。
Create MLのデータ形式をサポート
物体検出(Object Detection)用の Bounding Box アノテーションツールは、
・labelImg
・VoTT(TBC)
などローカルで使うものがGitHubで人気です。でも、Create MLでトレーニングするなら、IBM Cloud Annotationsがとっても便利です。
IBM Cloud Annotations Tool eases the process of AI data labeling https://t.co/KLdwmiyeoa pic.twitter.com/ItlVG8oPhx
— DevOps.com (@devopsdotcom) January 30, 2020なぜか。
それは、Create MLの注釈データ形式(json)で出力できるからです。
labelImgやVoTTは、出力形式がxml、txt、csvなので、Create MLに食わせるにはjsonに変換する必要がありますが、IBM Cloud Annotationsを使うと出力したフォルダごとそのままCreate MLのデータとして指定できます。
(例↓)PetsAnnotation.json[{"image":"pets0.jpg","annotations":[{"label":"cat","coordinates":{"x":348,"y":380,"width":113,"height":74}},{"label":"dog","coordinates":{"x":589,"y":390,"width":122,"height":75}}]},{"image":"pets1.jpg","annotations":[{"label":"cat","coordinates":{"x":233,"y":670,"width":238,"height":169}},使い方もシンプルで直感的(しかも無料)
1、画像をアップロード(ドラッグ&ドロップ)
2、マウスドラッグで境界ボックスをつける
3、「File」から「Export As Create ML」を選ぶ。これで、画像とjsonファイルがセットになったフォルダがローカルに保存され、そのままCreate ML Object Detection のデータとして参照できます。
欠点
回線によっては画像アップロードにかかる時間にストレスがあるかもしれません。
Happy Machine Learning✨!!
MLBoys Make Your World Beautiful with ML
MLBoysのTwitterをフォローしてください。お願いします。
だいすけ https://twitter.com/JackdeS11
ゆうじ https://twitter.com/oka_yuujiCore MLやCreate ML、Visionを使ったアプリを作っています。
エッジデバイス(iOS)で機械学習のモデルを使いたい、などのありましたら、こちらのメールまでご相談ください。
rockyshikoku@gmail.comLooks Good To Me(わるくないね)おねがいします。
↓
- 投稿日:2020-07-28T12:28:28+09:00
iOS Simulator でタイムゾーンを変更して実行する方法
タイムゾーンを越えたテストなど、iOS Simulator のタイムゾーンを変更したいときってありますよね?
さっとググると、MacOSのタイムゾーンを変更しろ!なんて情報がありますけど、MacOS の他のアプリケーションにも影響しそうで積極的にはやりたくありません。
別の方法はあるのでしょうか?
いろいろと調べたところ、結論からいうと、ビルド時の環境変数 TZ を変更することで、ビルドしたアプリ限定でタイムゾーンを変更できるようです。
正確には Simulator のタイムゾーンは変更されないのですが、僕が確認したかった範囲ではこれで十分だったので有用と思いましてまとめました。手順
まずは、xcode 左上のアプリ名のところを選択して、「Edit Scheme...」を選びます。
次に、、下図のように選択します。
③ のところには、Name に
TZ
、Value にタイムゾーンを示す文字列を入力します。Value の指定は、tz database にある
Asia/Tokyo
といった文字列や、JST, PST のような略語文字列でも扱ってくれるようです。タイムゾーン指定の標準については詳しくないのですが、tz database から選ぶのが無難と思います。確認方法と注意点
まずは注意点。この方法だと、正確には 、実行しているアプリ内でのみタイムゾーンが変化し、iOS Simulator 内のタイムゾーンは変化しません。
なので、iOS 上部のステータスバーの時刻は変わりません。ですが、アプリ内の時刻は指定したタイムゾーンで処理されています。確認した方法は次のとおりです。
まずは、なにも指定無し(= MacOS 側のシステム設定)です。システム設定は、
Asia/Tokyo
です。適当なところに以下のコードをコピペします。
let today = Date.today() let df = DateFormatter() df.dateFormat = "hh:mm Z" df.timeZone = TimeZone.current print(today) print(df.timeZone!) print(df.string(from: today))実行すると、以下のようにコンソールに表示されます。日本時間で正しく表示されています。
2020-07-28 03:19:29 +0000 Asia/Tokyo (current) 12:19 +0900次に、
TZ
にAmerica/Los_Angeles
を設定して実行してみます。2020-07-28 03:19:55 +0000 America/Los_Angeles (current) 08:19 -0700適切にタイムゾーンが変更されていることを確認できました。
同様に、ちょっとマイナーな
IRST
(イラン標準時)を4文字略称で設定してみます。2020-07-28 03:20:36 +0000 Asia/Tehran (current) 07:50 +0430こちらも確認することができました。
参考文献
https://stackoverflow.com/questions/1699671/how-to-change-time-and-timezone-in-iphone-simulator
※ この記事は、xcode 11.4.1 で確認しています。
- 投稿日:2020-07-28T08:28:34+09:00
Swift オプショナル型とアンラップを理解
Swiftでは変数の宣言時に、何も値が無い状態を許すか許さないかを、データ型で指定する必要がある。「オプショナル型」はデータ型の種類のひとつです。
オプショナル型とは
値が無い状態を許すデータ型はオプショナル型で、変数宣言時のクラス名の後に「?」か「!」を指定します。「?」か「!」を付けない宣言は、値がない状態を許さない「非オプショナル型」になります。一般的なオプショナル型は「?」を利用します。 「?」も「!」は両方とも「nil」を保持することが許されてますが、違いはわかりません。。すみません。複雑らしいので。。 最初はオプショナル型を宣言する場合は「?」を利用すればいいと覚えておいて大丈夫かと。
アンラップとは
オプショナル型の変数やメソッドを、安全に取り扱う手法をアンラップといいます。 宣言時以外で「?」や「!」を見かけたら、アンラップを行っているんだなと理解できれば問題ないかと。。
- 投稿日:2020-07-28T00:49:28+09:00
【小ネタ】Realm Studioを使ってGUIでRealmをいじる
はじめに
SwiftでWebsocketのチャットを触っているときに、RealmSwiftでいちいちデータの状態をいろいろ変えたりするのが面倒だったので、ラクなやり方を探してみました。
データの登録や削除や編集がGUIで簡単にできました!Realm Studio
Realm Studioをダウンロードします。
僕はMacだったのでMac用のものをダウンロードしました。
開発元が確認できませんみたいなのが表示されたら、システム環境設定のセキュリティとプライバシーのところを確認してみるといいと思います。Realmファイルを開く
次は、Realm Studioで編集するためのRealmファイルを見つけます。
こちらの記事がとても参考になりました。print(Realm.Configuration.defaultConfiguration.fileURL!)この
print()
で出力されたパスをfinderでcommand + shift + G
で開き、そのディレクトリをRealm Studioで開きます。
これでDBの管理画面のようなものが表示されると思います。さいごに
本当はチャットの部分も載せたかったのですが、まだ公開ができないのでRealm Studioの導入の部分だけ載せておきました。
ありがとうございました。参考リンク
- 投稿日:2020-07-28T00:33:16+09:00
[Swift] var-forパターンを避けよう
はじめに
var-forパターンは既存の言葉ではありません。
このアンチパターンよく見かけるので自分でvar-forパターンと勝手に命名しました。
コードレビューとかで「これvar-forパターンだよね」という感じで使えるかもしれません。本記事の内容はSwiftの初心者向けです。
var-forパターンとは
以下のような一時変数varとfor文(forEach、while、repeat-whileなども含む)を利用したロジックです。
例題)1から10までの整数を3倍して、6の倍数のみの配列を生成
var result = [Int]() for number in 1...10 { let tripleNumber = number * 3 if tripleNumber % 6 == 0 { result += [tripleNumber] } }よくあるのが、配列から特定の条件を満たした別の配列を作る時です。
var-forパターンの置き換え
たいていのvar-forパターンは以下のようにmapやcompactMap、filter、reduceなどの高階関数で置き換えられます。
※高階関数がわからない方向けの記事:イメージで理解するSwiftの高階関数(filter, map, reduce, compactMap, flatMap)let result = (1...10) .map { $0 * 3 } .filter { $0 % 6 == 0 }var-forパターンのデメリット
可読性が低い
読み手の頭の中を想像して、var-forパターンのロジックを日本語にしてみます。
var result = [Int]() // 要素がInt型のresultという名前の配列を定義し、空で初期化する for number in 1...10 { // 1から10までループさせ let tripleNumber = number * 3 // numberを3倍したtripleNumberという名前の定数を用意する if tripleNumber % 6 == 0 { // もしもtripleNumberが6で割り切れたら、 result += [tripleNumber] // resultにtripleNumberを追加する } // tripleNumberが6で割り切れなければ何もしない }次に、高階関数を利用したパターンです。
let result = (1...10) // 1から10までの整数を .map { $0 * 3 } // 3倍して、 .filter { $0 % 6 == 0 } // 6の倍数のみにした定数resultを定義上記を比較してみます。
var-forパターン
要素がInt型のresultという名前の配列を定義し、空で初期化する、1から10までループさせ、numberを3倍したtripleNumberという名前の定数を用意する、もしもtripleNumberが6で割り切れたら、resultにtripleNumberを追加する、tripleNumberが6で割り切れなければ何もしない
高階関数パターン
1から10までの整数を3倍して、6の倍数のみにした定数resultを定義
どちらがわかりやすいでしょうか?
後者の方が例題そのものの性質をよく表現しており、より人間が読みやすく、宣言的なコーディングスタイルになっています。
(プログラミングパラダイムでいうと、前者は命令型プログラミング、後者は宣言型プログラミングと分類できます)修正がしにくい
例題にこんな仕様変更があったらどう修正するでしょうか?
1から10までの整数を3倍して、6の倍数のみにした先頭要素の2つの配列を生成
var-forパターンの場合、このような修正が思いつきますが、要素数が2個未満の場合に要素を追加するというのはあまり宣言的ではなく、
result.count =< 2
のような間違ったコードを書いてしまう可能性もあります。var result = [Int]() for number in 1...10 { let tripleNumber = number * 3 if tripleNumber % 6 == 0 && result.count < 2 { result += [tripleNumber] } }高階関数パターンであれば、以下のようにprefixを追加するだけです。
let result = (1...10) .map { $0 * 3 } .filter { $0 % 6 == 0 } .prefix(2)バグを生みやすい
修正がしにくいにも書きましたが、可読性が悪い、変更がしづらいというのはバグを生みやすいコードです。
また、let定数でなく、var変数のように、コード内に値が変化する箇所があるというのもバグを生みやすい原因になります。よりバグの少ないない安全なプログラムを作るには変化する箇所をできるだけ減らすことが有効です。
状態がなく、変化しないものはテストパターンが少なく済むので、バグを見逃す確率がぐっと減ります。
- varよりもletをなるべく使う(変化させない)
- class(参照型)よりもstruct/enum(値型)を使う(変化を伝播させない)
- プリミティブ型よりも独自型やenumを利用する(変化する値のとりうる範囲を制限する)
まとめ
- var-forパターンよりも高階関数を利用する
- var-forパターンは可読性、修正しやすさ、安全さが劣る場合がある