swift – SKNodeからSKSpriteNodeへ変換

SKViewからnodeAtPointで取得したノードはSKNodeとして受け取るため、それをSKSpriteNodeにキャストする。Objective-cの時はAnyObjectに突っ込んでからキャストしてればよかったがswiftだと型の判定が厳しくなっている模様

下記コードはSKNodeからSKSpriteNodeへ変換・・・しているというわけではない。SKSpriteNodeであると確信があるので決め打ちしてキャストしているだけ。

サンプルコード

override func touchesBegan(touches: Set, withEvent event: UIEvent?) {
        
        for touch: AnyObject in touches {
            
            var touchPos: CGPoint = touch.locationInNode(self)
            var node = self.nodeAtPoint(touchPos)

            if (node.name == "test"){
                        let sknode :SKSpriteNode = (node as? SKSpriteNode)!
            }
        }
}

swift – app内課金の実装

itunes connectやdeveloper centerで下準備は済ませているものとして。コードの実装部分のみ。課金の商品は1度きりの購入のよくある広告非表示の商品をイメージ。
処理の流れとしては、viewDidLoadにて初めの画面表示の時に製品IDで商品情報を取得して、ボタンを押したら購入処理を開始するというもの。商品が一つだけなら下記のような感じにできるが、普通は複数でテーブルビューなどに表示して配列で商品管理をやっている模様。出来るだけ簡単で少ないコードでの実装を目指す・・・

購入処理

デリゲートの追加

class ClassName: UIViewController SKProductsRequestDelegate, SKPaymentTransactionObserver{

}

必要なデリゲートメソッド

func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {

}

func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){

}

実装例

import UIKit
import StoreKit

class ClassName: UIViewController SKProductsRequestDelegate, SKPaymentTransactionObserver{

var storeBt:UIButton!
var productID = “com.sample.product100”
var vProduct: SKProduct!

override func viewDidLoad() {

storeBt = UIButton()
storeBt.frame = CGRectMake(0, 0, 200, 22)
storeBt.setTitle(“”, forState: UIControlState.Normal)
storeBt.setTitleColor(UIColor.orangeColor(), forState: UIControlState.Normal)
storeBt.setTitle(“”, forState: UIControlState.Highlighted)
storeBt.setTitleColor(UIColor.blackColor(), forState: UIControlState.Highlighted)
storeBt.titleLabel!.adjustsFontSizeToFitWidth = true
storeBt.addTarget(self, action: “byAc:”, forControlEvents: .TouchUpInside)
self.view.addSubview(storeBt)

SKPaymentQueue.defaultQueue().addTransactionObserver(self)
getProductInfo()

}

func getProductInfo(){
if SKPaymentQueue.canMakePayments() {
let request = SKProductsRequest(productIdentifiers:
NSSet(objects: self.productID) as! Set)
request.delegate = self
request.start()
} else {
print(“Please enable In App Purchase in Settings”)
}

func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {

let count : Int = response.products.count
if (count>0) {
var validProducts = response.products
var validProduct: SKProduct = response.products[0] as SKProduct
if (validProduct.productIdentifier == self.produto_value) {
print(validProduct.localizedTitle)
print(validProduct.localizedDescription)
print(validProduct.price)
vProduct = validProduct

//
storeBt.setTitle(“\(validProduct.localizedTitle) \(validProduct.price)円”, forState: UIControlState.Normal)
storeBt.setTitle(“\(validProduct.localizedTitle) \(validProduct.price)円”, forState: UIControlState.Highlighted)

} else {
print(validProduct.productIdentifier)
}

} else {
print(“not found”)
}
}

func byAc(sender: AnyObject){

if(vProduct != nil){
let payment = SKPayment(product:vProduct)
SKPaymentQueue.defaultQueue().addPayment(payment)
}
}

func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){

for transaction: SKPaymentTransaction in transactions {
switch transaction.transactionState {
case .Failed:
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
NSLog(“%@”, “失敗”)

case .Purchasing:
NSLog(“%@”, “購入中”)

case .Restored:
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
NSLog(“%@”, “リストア”)

case .Purchased:
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
NSLog(“%@”, “成功”)

// レシートを取得して検証
var receiptURL: NSURL = NSBundle.mainBundle().appStoreReceiptURL!
var receiptData: NSData = NSData(contentsOfURL: receiptURL)!
var base64receiptData: String = receiptData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
NSLog(“%@”, base64receiptData)

default:
print(transaction.transactionState.rawValue)
break
}
}
}
}

touchesBeganとtouchesEndedの間にある遅延について

iOSでtouchesBeganイベントが呼ばれてtouchesEndedが呼ばれるまでの間に約0.4秒ほどの遅延があることがわかった。(マシンの調子が悪い時は0.5秒ほど)
touchesBeganからtouchesMovedイベントまでの遅延はほとんど気にならない程度。
この0.4秒の遅延は体感ではまあ普通のアプリでは反応悪いなと許容できる範囲の時間ではあるけれども、応答の即時性が求められるアプリではちょっと長すぎる。この時間をなんとかゼロにできないものかといろいろ調べてみたが、見つからなかった。touchesEndedイベントに必要なディレイ時間として設定されていてプログラミングでプロパティを変更とかできないものかもしれない。
例えば画面をポンッとタップしてその結果がすぐにわかる必要があるような実装をする場合、処理をtouchesEndedに書いていたら0.4秒の遅延が発生するので使えない。この操作だとtouchesMovedイベントも呼ばれない。仕方がないのでtouchesBeganだけで完結するような実装にすることにした。

四国銀行がオンラインバンキングでハードウェアトークンを始めた様子

ワンタイムパスワード(ハードウェアトークン)の取扱い開始 | 四国銀行

四国銀行が振り込みと振替をオンラインバンキングでする場合にはワンタイムパスワードを必須にする様子。ハードウェアで短時間だけ有効なパスワードを生成する仕組みで、はっきりいうとジャパンネット銀行のパクリである。ハードウェアトークンの見た目もシール張り替えただけのそのままのように見える。

ワンタイムパスワード(ハードウェアトークン)とは

パスワード生成機(ハードウェアトークン)により生成・表示される1分ごとに変化する使い捨てのパスワードです。ワンタイムパスワードは1分後には使えなくなることから、第三者に遠隔操作等で不正にログインされるリスクを低減することができます。

振り込みの時にこのワンタイムパスワードを使うらしいから不正ログインのリスクとは関係がないような気がするが・・・。個人的には四国銀行のオンラインバンキングでこんなハイテクそうなことをやって大丈夫なのかという心配がある。昔の四国銀行のオンラインバンキングのUIは素人丸出しで絶対ホームページ・ビルダーで作ってるだろこれと思いながら私は使っていた。それを知っているからか残高照会以外の取引を四国銀行のオンラインバンキングでやるのはちょっと怖い。オンラインバンキングのアドレスを見るとwww.parasol.anser.ne.jpというドメインで運用されている様子でセキュリティ証明書はNTT DATA となっている。ジャパンネット銀行みたいなオンライン専門の銀行じゃないんだからあまり無茶はしないほうがいいとおもうんだけれども。

それにしても、ジャパンネット銀行をオンライン決済で利用している私はこのトークンをすでに使っていて、さらに四国銀行のトークンも管理しなければならないというのは、なんかセキュリティのためとはいえ馬鹿っぽい気がする。私は他にゆうちょ銀行や楽天銀行の口座も持っているが、それらの銀行がもしもトークンの利用開始を始めてしまったらたまったもんではない。財布に溜まっていくコンビニや量販店のポイントカードじゃあるまいし。

メインビューとサブビューの座標の違い

メインビューでaddsubviewにてuiviewを追加して、ボタンを押したら別のビューが下からビョーンって出てくるようなものを考えていたが、座標の問題で困った。メインビューの座標とサブビューの座標はそれぞれ存在しているため、メインビューでタップしたポジションでサブビュー上のパーツを動かそうとしたら、当然おかしなことになる。このサブビューを下から少しだけ飛び出すような使い方だと座標の変換が多分必要になる。あと、サブビューのパーツを画面外のメインビューへ飛び越えるような使い方は、多分無理な予感。試してはないけれども、普通の感覚では不自然。

下からビューを飛び出してきてその上のパーツをいろいろ動かすのは、擬似的にすることにした。つまりビューだけ下から表示して、その上のパーツはメインビューにaddsubview()するということ。つまり見た目の上ではテクニカルなことをなんかやっているように見えるだけ。たいていの場合こういうのは設計が悪くて他にもっとスマートな解決方法があるはず。なんか無理やり実装してる感が出てきたら経験上よろしくない。後々のメンテナンス性が劣悪になったりとか。

Ubuntu ServerでBusyBoxとか出てフリーズ、起動できなくなった

mount: mounting /sys on /root/sys failed: No such file or directory
mount: mounting /proc on /root/proc failed: No such file or directory
Target filesystem doesn't have requested /sbin/init.
No init found. Try passing init = bootarg.

BusyBox v1.1.3 (Ubuntu 1:1.1.3-1ubuntu11) built-in shell (ash)
Enter 'help' for a list o built-in commands.

(initranfs)

エラーが出るまでは普通に稼働していたUbuntu Server12.04が突然フリーズして上記エラーを起動時に表示するようになり、そこから進まなくなった。No such file or directoryの部分を見て直感でこのエラーはかなりまずいかなと思ってググってみたら、LiveCDでfsckを実行すれば治るとかあったので、参考に自分もやってみることにした。まずはHDDが壊れていないことを祈って今取り付けているポンコツより高性能なマシンに載せかえる(intel atomという低スペcpuでサーバー稼働していて、復旧作業が長くなりそうな気がしたので)

で、載せ替えた後に念のためもう一度起動してみたところ、起動時にHDDのアクセスランプが長いこと点灯したままになってなかなか画面が表示されない現象が発生。しばらく待ってたら画面が表示されて、さっきとは違う画面が表示されて、一瞬だったから覚えていないがblockがなんとかあってfsckが実行されたような感じになった。(この時はまだLiveCDは挿入していない)

その後は、ログイン画面が表示されて前と変わらないように稼働できるようになった。まるでさっきのエラーがなかったかのように。かなり古いHDDを使っていたので壊れちゃったかなと完全にあきらめモードで作業していたが、実際にやったことといえばHDDを別の高性能なマシンに載せ替えて起動してみただけ。もしかしたら、HDDが熱暴走気味になっていて、交換作業中に冷めて正常になったという可能性も、なくはない。(排熱が十分とはいえないマシンを使っていたのは事実)

上記のBusyBoxというようなエラーが出た時、あまりごちゃごちゃいじらない方がいいかもしれない。一旦落ち着いて(あきらめモードで)、HDDの熱が冷めるのを待ってもう一度起動するなど気長にやる。どのみち完全に故障なら復旧作業はそれなりに時間がかかるのだから。

それにしてもfailed: No such file or directoryというエラー文は怖すぎる。サーバー管理者を絶望させるに十分なインパクトがある。

翌日

また不具合が起こった

fsck.ext4: bad magic number in super-block while trying to re-ope
e2fsck: io manager magic bad! 

こんどは上記のようなエラーが発生した。昨日はfsckっぽいものを走らせたら直ったから今回もfsckを実行してみようかと思ってコンソールに入力すると、

Error reading block **** (Attempt to read block from filesystem resulted in short read) while getting next inode from scan.  Ignore error? yes

Force rewrite? yes

みたいな確認が膨大な量出てきた。なんかわからないがyesにするしかないよなぁ・・・と思いながらエンターキーを連打していてちょっと多すぎるからキャンセルしてやり直すかと画面みたら/varとか/etcとかのフォルダに対してdeleteとかrewriteとか実行してしまっていた。

で、再起動するも昨日のエラー画面のno such file or directoryが出て進まなくなった。→OSの再インストールを決断

私の致命的なミスは、昨日、幸運にもエラーから復旧できた時にHDD内の必要なデータをバックアップするなどの作業を怠ったこと。買ったばかりのHDDはともかく、古いHDD使ってて上記みたいなエラーが出だしたらもう交換の目安かもしれない。いや、すぐ交換すべき。この種エラーが出たらもう長くないと思ってHDDが生きている内にデータバックアップとリプレース作業を済ましておく(私が使っていたHDDはもう7,8年は使っていた)

busyboxのエラーが出たHDDその後、取り外してcrystaldiskinfoで調べてみた。健康状態は当然、注意の表示が出た。使用時間は51000時間で約6年間稼働している。もうこのHDDは引退。よく頑張ったと思う。

レイアウトの位置設定でframeで設定する場合とpositionで設定する場合の違い

iosのUIButtonの配置で気になったのでメモ。


var myButton: UIButton!

// Buttonを生成する.
myButton = UIButton()
        
// サイズを設定する.
myButton.frame = CGRectMake(0,0,200,40)

// ボタンの位置を指定する.
myButton.layer.position = CGPoint(x: self.view.frame.width/2, y:200)

ポジションの設定はCGRectMakeでサイズとポジションを設定する方法と、layer.positionにCGPointで設定する方法がある。この二つの違いは、ポジションの原点の位置が異なる模様。CGRectMakeで設定する場合は原点はビューの左上になり、上記の0,0というポジションなら画面の左上にピタリと配置されることになる。CGPointで設定した場合は、原点はビューの中心となり、CGPoint(x: 0, y:0)と設定すると画面左上に配置されるがビューの中心を基準に配置されるので右下4分の1だけ表示されることになる。

wordpressのパーマリンク設定で404になる問題

ワードプレスのパーマリンク設定をするとページ遷移先が404not foundになる場合。
チェックポイントは下記の3つ。

  • mod_rewriteの有効化
  • .htaccessの設置
  • 管理画面にてパーマリンクの設定・保存

でカスタム構造などのパーマリンクは使うことができるようになる。

私の場合はトップページはなんとか表示されたが(記事一覧は表示しなかった)、個別の記事のページを表示すると404エラーが表示されるという現象だった。mod_rewriteはウェブサーバーのapacheの設定で、レンタルサーバを利用している場合は大抵の場合は有効になっているので気にしなくていい。.htaccessファイルはパーマリンクの設定に不可欠なファイルで、内容はカスタム構造のリンクを変換するものになっている。このファイルをワードプレスの設置しているディレクトに置くことで有効になる。管理画面からパーマリンクの設定を実行すると.htaccessファイルは自動で作られる。対象ディレクトリに書き込み権限がない場合は自分でファイルを作成して置くか、権限を付与してから再度実行する。

mod_rewriteが有効化どうか確認するには、例えば下記のような内容に.htaccessを書き換えてみて、ページを表示してみたらヤフーのトップページが表示されるようならmod_rewriteは有効になっているといえる。

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteRule ^.*$ http://yahoo.co.jp [L]
</IfModule>

他には、.htaccessのファイル権限とかもあるが、特には気にすることはなく標準のファイル権限を設定しておけばいい。レンタルサーバの場合は上記の点をチェックしておけばおそらく表示されるようになる。それでも表示されない場合は、サーバの設定(バーチャルホストやリダイレクト設定)に問題がある可能性が高い。

自宅サーバの場合も大体同じだが、httpd.confファイルでバーチャルホストを設定したりしている場合には設定の仕方によっては上記だけでは表示出来ない場合もある(私がそれでかなりハマった)
で、その時はウェブサーバーのログを見る。ログなんかめったに見ない主義だったが、今回ばかりはログを見ることで原因がわかった。そもそも404エラーはアクセスしたアドレスにファイルが見つからないというエラーで、ログにはアクセスしたディレクトリとともに404エラーが確認できる。バーチャルホストの設定が間違っている場合はおかしなディレクトリにアクセスしようとしてだから404なのだということがわかる。

ワードプレスとはあまり関係がないがapacheのhttpd.confでバーチャルホストの設定をする場合、一致したものから順に振り分けるという仕様になっているらしい。一致したものからというのはファイルの上から順にということで、バーチャルホストホストの設定で一番上にアスタリスク(*)で設定した場合はそれ以降は読まれないということになる。

windows 7 service pack 1の更新プログラムを確認していますが終わらない

windows7 pro 32bitを新規インストールしてからwindows update をかけると、更新プログラムを確認しています・・・とゲージが現れて何時間経っても終わらない問題。

解決方法は、不明。待つしか無い。windows updateを実行してから電源管理のスリープとか電源を自動で切る設定をオフにして一晩つけっぱなしにしておく。さすがに翌朝までには対象のアップデート数がいくつかはわかるようになるのでそこからダウンロード、インストールという流れになる。

だが、またしてもインストールしています・・・という画面から進まないという現象が発生。更新プログラムがダウンロードしている状態なら、電源をシャットダウンをする操作でインストールへと進むことができる。

とりあえず時間がかかる。新規インストールの場合は更新プログラムの数が200個とかになるのでダウンロードとインストールで時間を取られて1~2時間では絶対に終わらない。焦らず慌てないでやるしかない。

中古車を現車確認せずに通販で注文してみた

車検切れと同時に車を手放すことになったのでこの機会にと思って中古車を通信販売で買ってみた。ちなみに私は車のことは全然詳しくない。今まで買ったこともない。運転はするがそれは家族の車をたまに使う程度。つまり初めて買う車を、インターネットの通信販売で、車の画像数枚確認して、年式とか走行距離とかプロフィールを確認して、あと適当に電話で店の人に車の状態(修復歴とか気になるところとか)を聞いてみて、それだけで中古車を買ってしまった。

中古車の買い方として現車確認も試乗もせずに買うのは絶対間違っているだろうが、こういう無謀な買い方をする人は意外といるんじゃないだろうか。例えば遠方の自動車屋さんで車を買う場合など。近所の車屋さんで売っている車の中から選ぶというのなら堅実だが、自分が好きな乗りたい車を選んでからそれはどこに売っているか・・・と検討すると現車確認できない程遠くにあるというケース。私の場合がそれだが。

車を通信販売で電話一本で購入する流れは以下のような感じ。普通車と軽自動車とでやりとりに違いが出るようだが、今回は中古の軽自動車を注文する場合。

  • 電話で買う意思を伝える。そのとき店の人に無保証ですよと釘をさされてちょっとビビる。
  • 住民票と免許証のコピーをその自動車屋に郵送で送る。
  • お金を支払う。内訳は車両代金と車検代と名義変更などの事務手数料と陸送代。
  • 車検や名義変更、陸送の手配などを店の人にやってもらう。
  • 車検終わって名義変更も終わってナンバーもつけてもらって陸送してもらって受け取る←今ここ

注文してから陸送で納車までの手続きは約2週間ほど。郵送で書類を送ったり名義変更をしたりとするのでどうしても時間がかかる。普通車の場合は車庫証明を取得したりともっと時間がかかるかもしれない。

車が来たら追記します。

車が来た

陸送にて注文した中古車が来た。ここで初めての現車確認になる。正直なところ、注文する前に現車確認していたら注文するかどうかためらうぐらいの状態だった。うーん、ひどいのが来たなぁ・・・が、第一印象。状態が悪いのは覚悟していたが、予想よりも3割増しで悪いような気がする。

悪かった点

  • 車内のタバコの臭い
  • マフラーの騒音
  • モールの劣化
  • 可動すべきところの故障
  • 画像で確認できなかった部分に相当なカスタマイズが施されていた
  • パワーウインドウの動きが左右で違いなんか怪しい。運転席側の窓ガラスの一部が溶けてた
  • 車体の継ぎ目にあるゴム素材のモールは大体劣化および欠損していた
  • ビニールテープで雨漏り対策されてた(は?)
  • 洗濯バサミが落っこちてた(は?)
  • その他いろいろ

良かった点

  • 古い年式の車だったがエアコンは生きてた
  • 一応エンジンかかって自走できた(車検通ってきたから当たり前かもしれないが)
  • シートの破れは覚悟していたが奇跡的になかった

値段なりの車が届いたというのが客観的な現実だろうと思う。店の人が現状渡し、無保証ですと言った理由が
よく分かる気がした(現状渡しは承知だったが煙草の灰皿に吸い殻が残ったままだったのはさすがに閉口した)。それプラス現車確認を怠ったことによるいくつもの不安要素。現車確認せずに電話一本で中古車を購入した結果は悪いところばかりだが、自分が欲しいと思った車種の車が手に入ったという一点は達成できたので意外と満足感はある。これが自分の興味のない車種で買った理由が安かっただけというのなら最悪だったが。