Xcode7でシミュレーターのOSバージョンが表示されない不具合解消方法
Xcode7を使っていたところ、下の図のようにシミュレーター一覧を見ると、OSのバージョンが表示されず、どのOSなのかさっぱりわからなくなりました。。
解決方法
$ rm -rf ~/Library/Developer/CoreSimulator/Devices
そしてmacを再起動します
Swift Bond v4
Swift BondがSwift2.0に対応し、更にインターフェースが大きくかわりv4としてmasterに統合されました。
主な変更点としては、
クラス名を刷新
- これまではBond、Dynamicといった意味不明なクラス名がObservable, EventProducerに変わりわかりやすくなりました。
オペレーターの非推奨・廃止
- ->>や<<-といったオペレータの利用が非推奨となり、bind onlyの->|が廃止されました
と言った風に、いままで個人的に微妙だなと思っていた独自オペレーターや、謎のクラス名が改善されてよくなったなと思ってます。
基本的な使い方
値の変化の監視
以下のようなusernameの値の変化を検知したい場合、
var username: String = "Steve"
以下のようにObservable
class LoginViewModel { let username = Observable<String?>("Steve") }
そして値の変化のイベントを受け取る場合は、上記で定義したObservable
class LoginViewController: UIViewController { let viewModel = LoginViewModel() override func viewDidLoad() { viewModel.username.observe { event in print(event.value) } }
observe()は呼ぶとすぐに、引数で渡したコールバックが呼ばれます。 これを避けたい場合はobserveNew()を使えば登録時はコールバックが呼ばれません。
値の変化の監視とバインディング
監視対象とViewの要素をバインディングして、監視対象が変更されたらViewの要素を更新するといった場合、 bindTo()メソッドを使います。
class LoginViewController: UIViewController { @IBOutlet weak var usernameTextField: UITextField! let viewModel = LoginViewModel() override func viewDidLoad() { viewModel.username.bindTo(usernameTextField.bnd_text) }
UITextFieldのtextとバインディングする場合はbnd_textというObservableがBond側でUITextFieldのextensionとして用意されています。 この他にも基本的なViewは用意されているのでExtensions以下を見るか、bnd_で予測変換をだせば確認できます。
また、bindTo()もobserve()と同様に、登録時に即時バインディングが行われます。 これを避けたい場合はskip()を使います。
viewModel.username.skip(1).bindTo(usernameTextField.bnd_text)
skip(1)とやると最初はusernameの値がTextFieldには反映されず、次にusernameが変化したときから反映されるようになります。
双方向バインディング
bindTo()は監視対象→監視側への一方通行のバインディングでしたが、双方向にすることも可能です。
viewModel.username.bidirectionalBindTo(usernameTextField.bnd_text)
イベントのフィルタリング
特定の条件の変化があった際だけ、コールバックを受け取りたい場合は、fileterを使います。
filterはEventProducer型(Observableのsuper class)を返しますので、filterの戻り値をそのままチェーンしてやることでObservableと同じように扱えます。
@IBOutlet weak var loginButton: UIButton! let viewModel = LoginViewModel() override func viewDidLoad() { loginButton.bnd_controlEvent .filter { $0 == UIControlEvents.TouchUpInside } .observeNew { [unowned self] event in self.viewModel.login() } }
バインディングの解除
Observableが破棄されると自動的にそれに関わるバインディングは解消されるようになっていますので、普通に使っているぶんには解除を気にする必要はあまりありません。
解除方法
ObservableやbindToは戻り値としてDisposableTypeを返します。 DisposableTypeのdispose()メソッドを呼べば解除できます。
let disposer = viewModel.username.bindTo(usernameTextField.bnd_text) disposer.dispose()
Cellについて
普通はあまりバインディングの解除を気にする必要はありませんが、UITableViewCellなど再利用されるものについて使う場合は手動で解除してやる必要があります。(Cellが再利用されると解除しないと、他のバインディングが残ってしまうため)
やり方としては、cellにbindする際に、cellのbnd_bagにdisposeInしておき
let cell = (self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as? ListCellView)! let viewModel = dataSource[indexPath.section][indexPath.row] viewModel.name.bindTo(cell.nameLabel.bnd_text).disposeIn(cell.bnd_bag) return cell
prepareForReuse()でbnd_bagをdispose()します
override func prepareForReuse() { super.prepareForReuse() bnd_bag.dispose() }
Swift2でsubstring
Swift2(beta6以降)でadvance()が廃止され、BidirectionalIndexTypeなどにadvancedBy()が追加されたました。 書き方は以下の様に変わります
Swift1.2
var text = "123456" text = text.substringFromIndex(advance(text.startIndex, 3)) // "456"
Swift2
var text = "123456" text = text.substringFromIndex(text.startIndex.advancedBy(3)) // "456"
Swift2でArrayにArrayをinsertする
これまではsplice:atIndexでしたが
items.splice(insertItems, atIndex: items.endIndex)
Swift2(beta6以降)からinsertContentsOf:atに変わりました
items.insertContentsOf(insertItems, at: 1)
例
var items = ["a", "b", "c"] let insertItems = ["1", "2"] items.insertContentsOf(insertItems, at: 1) // ["a", "1", "2", "b", "c"]
iPadのマルチタスク(SlideOver, SplitView)対応
iOS9からiPadでmultitaskingが使えるようになります。 SlideOverはiPad Air以降、SplitViewはiPad Air2以降で利用できます。
SlideOver, SplitViewに対応したアプリの作成方法
新規で作成する場合
Xcode7で新しいプロジェクトを作ると特に何もしなくてもよいです
既存のアプリを対応させる場合
下記の条件をすべて満たすことが必要
- Xcode7(iOS9SDK)でビルド
- launch screen storyboardを使う
- iPadですべての回転方向をサポートする
ドキュメント
Adopting Multitasking Enhancements on iPad: Slide Over and Split View Quick Start
Swift2でStringを指定した文字で分割
beta5でsplitの仕様がちょっと変わり、以下のようにすることで文字列を分割できます。
let string1 = "hoge" let string2 = string1.characters.split("o").map{ String($0) } // [h, ge]
Swift2からStringはCollectionTypeではなくなり、Stringの保持するcharactorsがCollectionTypeになっています
Strings in Swift 2 - Swift Blog - Apple Developer
Swift1系
struct String { init() } extension String : CollectionType {
Swift2
extension String { /// A `String`'s collection of `Character`s ([extended grapheme /// clusters](http://www.unicode.org/glossary/#extended_grapheme_cluster)) /// elements. public struct CharacterView { /// Create a view of the `Character`s in `text`. public init(_ text: String) } /// A collection of `Characters` representing the `String`'s /// [extended grapheme /// clusters](http://www.unicode.org/glossary/#extended_grapheme_cluster). public var characters: String.CharacterView { get } ... extension String.CharacterView : CollectionType, Indexable, SequenceType { /// A character position.
splitはCollectionTypeのメソッドで仕様は以下のようになっています。
CollectionType Protocol Reference
charactors.split('separator') だけだと結果はCharacterViewのArrayとなってしまうので、map{ String($0) } でStringに変換しています。
SwiftでArray内のOptionalをunwrapする
なにを言っているのかよくわからないタイトルになってますが、
Array<T?>をArray
いろいろやり方はありますが、一番手短にかける方法は このようにflatMapにかけるだけ
let array1: [String?] = ["1", nil, "2"] let array2 = array1.flatMap{ $0 }
というのも、mapとflatMapはinterfaceにも微妙な違いがあるため
func map<T>(@noescape transform: (Self.Generator.Element) -> T) -> [T]
func flatMap<T>(@noescape transform: (Self.Generator.Element) -> T?) -> [T]
iOS9 ATSの設定方法
β4現在
Xcode7でビルドするとiOS9以降でApp Transport Security (ATS) が利用可能となります
App Transport Security Technote: App Transport Security Technote
デフォルトでhttps通信が必須となるので、この挙動を変更するには上の仕様に書いてあるようにInfo.plistに設定を記載する必要があります。
ATS設定パターン
1. 全ての通信にApp Transport Securityを適用
設定不要
2. 全ての通信にApp Transport Securityを適用しない
以下のように設定することでhttpsでなくても通信可能となります
3. 指定したドメインのみ適用する
指定したドメインのみhttps通信をmustにしたい場合は以下のように設定します サブドメインも含めたい場合は、 NSIncludesSubdomainsをYESにします。
この例では、yahoo.co.jpとそのサブドメインに対して通信する場合はhttpsが必須となり、 その他のサイトでは任意となります。
4. 指定したドメイン以外に適用する
指定したドメインのみhttps通信をmustにしたく”ない”場合は以下のように設定します
この例では、yahoo.co.jpのみhttpで通信可能となり、それ以外はhttpsが必須となります。
これ以外の細かい設定はAppTransportSecurityTechnoteに色々とパラメータが用意されているので、それらを参照ください
Xcode7 betaでiOS8.4端末にインストールできない場合
Xcode7 beta3で、iOS8.4の端末をつなげても"ineligible devices"となり、端末が選択できず実機にインストールできない。。 (追記:beta4ではiOS8.4に対応してます)
調べてみると8.4が入ってないです
$ ls /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/ 6.0/ 7.0/ 8.1/ 8.3/ 6.1/ 7.1/ 8.2/ 9.0 (13A4254u)/
解決方法としては、Xcode6.4から8.4をコピーしてくればとりあえず動きました
$ cp -r /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/8.4\ \(12H141\) /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/
XcodeのSwiftのFile Templateを変更する
Xcodeでデフォルトで生成される以下のようなtemplateを変更したい場合は
// // File.swift // Project_name // // Created by xxxxxxxxxxx on xxxx/xx/xx. // Copyright © 2015年 xxxxxxxxx xxxxxxx. All rights reserved. // import Foundation
このようにユーザーのHomeディレクトリ配下にTemplateファイルを配置してやり、
$ mkdir -p ~/Library/Developer/Xcode/Templates/File\ Templates/User\ Templates $ cd ~/Library/Developer/Xcode/Templates/File\ Templates/User\ Templates $ cp -r /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File\ Templates/Source/Swift\ File.xctemplate .
FILEBASENAME_.swiftを変更すれば、Xcodeでファイルを作る際に、このようにUser Templateが現れるので、ここのSwift Fileを選択すれば独自のtemplateを使うことができます。
AppCodeだったらこんな面倒なことしなくてもtemplate変更できるんですが・・
Swift2でのbitmask
Swift2からRawOptionSetTypeがOptionSetType (OptionSetType Protocol Reference) に変わったため、
例えばUIViewAutoresizingの場合、 これまでは
view?.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
だったのが
self.view?.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
になります。
OptionSetTypeのprotocol extensionでいくつかメソッドが用意されているので、これらを使って、and, or, exclusive orなどのbit演算を行ったり、containsを使ってbitが立っているかチェックできます
extension OptionSetType { /// Returns the set of elements contained in `self`, in `other`, or in /// both `self` and `other`. func union(other: Self) -> Self /// Returns the set of elements contained in both `self` and `other`. func intersect(other: Self) -> Self /// Returns the set of elements contained in `self` or in `other`, /// but not in both `self` and `other`. func exclusiveOr(other: Self) -> Self } extension OptionSetType where Element == Self { /// Returns `true` if `self` contains `member`. /// /// - Equivalent to `self.intersect([member]) == [member]` func contains(member: Self) -> Bool /// If `member` is not already contained in `self`, insert it. /// /// - Equivalent to `self.unionInPlace([member])` /// - Postcondition: `self.contains(member)` mutating func insert(member: Self) /// If `member` is contained in `self`, remove and return it. /// Otherwise, return `nil`. /// /// - Postcondition: `self.intersect([member]).isEmpty` mutating func remove(member: Self) -> Self? }
Vagrantでjavascriptやcssのファイルを変更しても反映されない
Vagrantでjavascriptやcssを変更しても反映されないという問題にはまりました。
ブラウザのキャッシュの問題かと思ったのですが、Chrome Developer ToolのSettingで"Disable cache"にしても変化なし。
ならばサーバー側だということでnginxのキャッシュとかを調べていたら、nginxとかの問題ではなく、VirtualBoxのBugのようで、VirtualBox(Vagrant)とshareしているフォルダにおいてるとだめぽい。
http://docs.vagrantup.com/v2/synced-folders/virtualbox.html
ここに書いてあるようにsendfileをoffにすればなおります。
nginx
sendfile off;
EnableSendFile off
Ansibleでユーザーにグループを追加してもすぐに反映されない
Ansibleでユーザーにグループを追加するのは以下のようなやり方でできます
user - Manage user accounts — Ansible Documentation
- user: name=hoge group=test append=yes
が、この後に、hogeユーザーで、testのグループパーミッションのついたファイルやディレクトリを操作しようとするとなぜかpermission deniedになってしまいます。
調べたところissueにのっていて、 どうもflush the connectionを実装してもらわないと無理ぽい Impossible to add oneself to a group to use this group permission · Issue #921 · ansible/ansible-modules-core · GitHub
なので、グループパーミッションを利用して制限かけるのは諦めて他の方法でやるしかない
AnsibleでVagrantのprovisioningする際のgitの認証を通すには
ここに書いてあるやり方でいけました。
AnsibleからVagrantにssh経由でgit cloneする時の注意点 - Qiita
が、あまり関係ないと思っていた
sudo: no
が結構重要だったりしました。
gitを実行するTaskをsudoで実行してしまうと、userが変わってしまうのでssh forward agentが効かなくなってしまうっぽいので、gitなどのtaskはsudoをnoにしておく必要があります。
VagrantとAnsibleでFuelPHP開発環境構築(Ubuntu 14.04, nginx)
事前準備
- Vagrantのインストール
- Ansibleのインストール
ここではこれらのインストール方法は割愛
環境構築
とりあえず開発環境作りたい場合は、以下を実行し、しばらくすると環境が構築されます。
$ git clone https://github.com/yanamura3/FuelEnv $ cd FuelEnv $ vagrant up
localhost:3334をブラウザで開いてFuelPHPの画面がでればOKです。 FuelPHPでの開発はFuelEnv以下にfuelというフォルダができているのでそれを使えばよいです。
詳細
いじったところなどのメモ
Vagrant
VagrantFile
# -*- mode: ruby -*- # vi: set ft=ruby : VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "ubuntu/trusty64" config.vm.provider "virtualbox" do |vb| # Customize the amount of memory on the VM: vb.memory = "2048" end config.vm.network "forwarded_port", guest: 80, host: 3334 config.vm.synced_folder "./", "/var/www" config.vm.provision "ansible" do |ansible| ansible.playbook = "provisionings/site.yml" ansible.limit = "default" ansible.groups = { "vagrant" => ["default"], } end end
config.vm.synced_folderでカレントディレクトリとVM側の/var/www以下を同期するようにしています。 これはあとでFuelPHPを/var/www以下にcloneしてきて、PHPStormで開発しやすくするためにこうしました。
config.vm.provision "ansible" do |ansible| ansible.playbook = "provisionings/site.yml"
このように書くことで簡単にVagrantからansibleのplaybookを実行することができます。(これによってinvetoryの設定など面倒なことをVagrant側がやってくれます)
ansible.limit = "default" ansible.groups = { "vagrant" => ["default"], }
この部分は、VagrantからAnsibleを実行されたときのみで条件をわけるためにこうしています。host名はVagrantではdefaultになっていました。
Ansible
Ansibleというか、Fuelを使う上での設定周りではまったところで、主にnginx周りです。 Ansibleに関してはansible-example(ansible/ansible-examples · GitHub)をベースにやるとやりやすかったです。
Why Ansible
サーバー側に何もいれなくてよいので、knife-soloでやるよりはこっちでいいかなくらいのノリです。
nginx
nginx.confのほうはsites-enableのconfigファイルを参照するように変更
/etc/nginx/nginx.conf
include /etc/nginx/sites-enable/*.conf
virtualhostの設定自体はsites-available以下において、sites-enable以下にそれに対するシンボリックリンクを置くようにしました。(不要なときにシンボリックリンクだけ消せばよいので
/etc/nginx/sites-available/virtual.conf
server { listen 80 default_server; server_name localhost; root /var/www/fuel/public; location / { try_files $uri /index.php?$uri&$args; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }
rootを/var/www/fuel/publicとpublicフォルダ以下になるように設定。
とくにlocationのところが重要で、localhost:xxxxでwelcomeページは開けるけど、localhost:xxxx/helloが開けない という場合はここの記述が間違っている可能性が高いです。
fastcgiを動かすために、fastcgi_passを127.0.0.1:9000にして、fpmの設定で9000番ポートをlistenするように修正しています。
/etc/php/fpm/pool.d/www.conf
listen = 9000
また普通にnginxをインストールするとバージョンが1.4とかになるので最新のstableをとってくるように変更しました
- name: get nginx signing key apt_key: url=http://nginx.org/keys/nginx_signing.key - name: update nginx sources1 apt_repository: repo="deb http://nginx.org/packages/ubuntu/ trusty nginx" - name: update nginx sources2 apt_repository: repo="deb-src http://nginx.org/packages/ubuntu/ trusty nginx" - name: install nginx apt: pkg=nginx state=present update_cache=yes