実録! 2年半ぶりのSwiftアプリバージョンアップ 2日目 その2

続きです。今日中に終わるかなぁ・・・

【エラーメッセージ】
‘utf16Count’ is unavailable: Take the count of a UTF-16 view instead, i.e. str.utf16.count
【修正前】
if 0 < newXStr.utf16Count && 0 < newYStr.utf16Count && Util.checkNum(newXStr) && Util.checkNum(newYStr) {
【修正後】
if 0 < newXStr.utf16.count && 0 < newYStr.utf16.count && Util.checkNum(newXStr) && Util.checkNum(newYStr) {
【解説】
newYStr.utf16Count を newYStr.utf16.count に変更。

【エラーメッセージ】
Value of type ‘String’ has no member ‘toInt’
【修正前】
var newX : Int = newXStr.toInt()!
【修正後】
var newX : Int = Int(newXStr)!
【解説】
StringからIntへの変換は、Intイニシャライザを利用するようになった(Swift2で)そうです。2箇所修正。

【エラーメッセージ】
Type ‘String’ does not conform to protocol ‘Sequence’
【修正前】
for c in str
【修正後】
for c in str.characters
【解説】
「String型はSequenceプロトコルに準拠していません」とのことです。文字列を1文字ずつ取り出してforループで処理したい場合は、charactersを使うと良いみたいです。
2箇所修正。

【エラーメッセージ】
Cannot call value of non-function type ‘UIColor’
【修正前】
UIColor.lightGrayColor()
【修正後】
UIColor.lightGray
【解説】
lightGrayColor() がなくなって、lightGrayというconvenience methodで呼べるようになりました。

これでエラー全部直りました。さあ、実行してみます。

・・・

・・

Thread 1: EXC_BAD_INSTRUCTION code=EXC_I386_INVOP, subcode=0x0)

おやすみなさい。
さようなら2017年7月16日。

さようなら土日。

・・・続く

実録! 2年半ぶりのSwiftアプリバージョンアップ 2日目 その1

おはようございます。続きです。

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var y = newYStart; y <= newYEnd; y += 1 {
【修正後】
for y in newYStart … newYEnd { // 3.1
【解説】
Swift 3.0で for ループの構文が変わりました。影響はとても大きいですが、記述が簡潔になりました。英断でしたね。個人的にはそのままにして欲しかった・・・

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var xi = 1; xi <= xNum; xi += 1 {
【修正後】
for xi in 1 … xNum { // 3.1
【解説】
同じ

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var yi = 1; yi <= yNum; yi += 1 {
【修正後】
for yi in 1 … yNum { // 3.1
【解説】
同じ

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var i = 0; i < gs.m_touches.count; i++ {
【修正後】
for i in 0 ..< gs.m_touches.count {
【解説】
“以下”の場合は … で、”未満”の場合は ..< です。 【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var xi = gs.m_glass.enable() ? 8 – gs.m_glass.xV % 8 : 0; xi <= xNum; xi += 8 {
【修正後】
for xi in stride(from: (gs.m_glass.enable() ? 8 – gs.m_glass.xV % 8 : 0), through: xNum, by: 8) { // 3.1
【解説】
飛び石パターンです。throughが”以下”、toが”未満”です。このパターンはもう1箇所ありました。

【エラーメッセージ】
C-style for statement has been removed in Swift 3
【修正前】
for var x:CGFloat = (bPlusX ? start.x + unitX : start.x – unitX);
(bPlusX ? x < end.x : end.x < x);
x += (bPlusX ? unitX : -unitX) {
// 処理
}
【修正後】
if (bPlusX) {
for x : CGFloat in stride(from: start.x + unitX, to: end.x, by: unitX) {
// 処理
}
} else {
for x : CGFloat in stride(from: end.x, to: start.x – unitX, by: unitX).reversed() {
// 処理
}
}
【解説】
時間かかりました・・・
事前に条件分岐して2つのforループに分けるしかない・・・よね?全体として記述が長くなってしまったパターンです。複雑なforループは書くべきじゃないということか・・・このパターンはもう1箇所ありました。

【エラーメッセージ】
Use of unresolved identifier ‘countElements’
【修正前】
countElements(target)
【修正後】
target.characters.count
【解説】
変数targetはString型です。文字の長さ取得だけで、結構手間ががかります。charactersは、見た目の文字数を取得します。

【エラーメッセージ】
‘Default’ has been renamed to ‘default’
【修正前】
UIAlertActionStyle.Default
【修正後】
UIAlertActionStyle.default
【解説】
Defaultの先頭大文字が小文字に変わっただけ。合計3箇所ありました。

【エラーメッセージ】
Cannot convert value of type ‘String’ to expected argument type ‘UnsafeMutableRawPointer’
【修正前】
if let path = Bundle.main.path(forResource: file as String, ofType: “sks”) {
var sceneData = Data(bytesNoCopy: path, count: .DataReadingMappedIfSafe, deallocator: nil)!
【修正後】
if let path = Bundle.main.path(forResource: file as String, ofType: “sks”) {
let pathURL : URL = URL(fileURLWithPath: path)
var sceneData : Data
do {
sceneData = try Data(contentsOf: pathURL, options: .mappedIfSafe)
} catch {
print(“unarchiveFromFile error.”)
}
【解説】
結構変わるなあ・・・

【エラーメッセージ】
‘init(forReadingWithData:)’ has been renamed to ‘init(forReadingWith:)’
【修正前】
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
【修正後】
var archiver = NSKeyedUnarchiver(forReadingWith: sceneData)
【解説】
ラベル名が変わっただけ。

【エラーメッセージ】
‘Any?’ is not convertible to ‘GameScene’; did you mean to use ‘as!’ to force downcast?
【修正前】
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as GameScene
【修正後】
let scene = archiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! GameScene
【解説】
ダウンキャストは必ず成功する想定なので、as! で強制的にキャストして対処します、が、いつからこのエラー出るようになったんですかね?

【エラーメッセージ】
Argument labels ‘(data:)’ do not match any available overloads
【修正前】
var data: Data = Data(data:UIImagePNGRepresentation(image))
【修正後】
var data: Data = UIImagePNGRepresentation(image)!
【解説】
UIImagePNGRepresentationから直接Dataを取得可能になったようです。いつ頃変わったんだろう・・・

・・・続く

実録! 2年半ぶりのSwiftアプリバージョンアップ 1日目 その4

続きです。

【エラーメッセージ】
Extra argument ‘error’ in call
【修正前】
fileManager.removeItemAtPath(pngPath, error: nil)
【修正後】
do { // 3.1
try fileManager.removeItem(atPath: pngPath) // 3.1
} catch { // 3.1
print(“removeItem error”) // 3.1
} // 3.1
【解説】
エラー処理が変わりました。今後はtry catchで統一するんですね。また、removeItemAtPathはremoveItemに名称変更となり、第一引数のラベル名atPathが必須になりました。

【エラーメッセージ】
‘++’ is unavailable: it has been removed in Swift 3
【修正前】
if MAX_HISTORY <= ++gs.m_hisIdx {
【修正後】
gs.m_hisIdx += 1 // 3.1
if MAX_HISTORY <= gs.m_hisIdx { // 3.1
【解説】
++や–の演算子は廃止だそうです。わかりにくくなるからかしら・・・
変数の前の++は該当行より前で、変数の後の++は該当行より後でインクリメントします。デクリメントも同様です。

【エラーメッセージ】
‘bytes’ is unavailable: use withUnsafeBytes instead
【修正前】
var buffer : UnsafePointer = CFDataGetBytePtr(dataRef)
memcpy(&gs.m_buffer, Data(bytes: UnsafePointer(buffer), count : newX * newY * 4).bytes, newX * newY * 4)
【修正後】
var buffer : UnsafePointer = CFDataGetBytePtr(dataRef)
memcpy(&gs.m_buffer, buffer, newX * newY * 4)
【解説】
エラーの直接の解説ではないです。withUnsafeBytesを使う以前に、なんで一旦Data型に変換してたんだろう・・・
直接 UnsafePointer をmemcpyの引数に指定することでエラーを回避。動くかなぁ・・・

【エラーメッセージ】
NSString’ is not implicitly convertible to ‘String’; did you mean to use ‘as’ to explicitly convert?
【修正前】
let path = paths1[0].stringByAppendingPathComponent(NSString(format:”slot%02d.png”,index))
【修正後】
let path = paths1[0].stringByAppendingPathComponent(String(format:”slot%02d.png”,index)) // 3.1
【解説】
エラーの意味は「NSStringは暗黙的にStringへ変換できないよ?’as’使って明示的に変換したら?」です。stringByAppendingPathComponentの引数はStringですが、そこにNSStringを指定したのでこのエラーが発生しています。Stringにもformatがあるのでそれを使うようにし、NSStringを使わないように対処しました。

【エラーメッセージ】
GameViewController.swift:109:19: Method does not override any method from its superclass
【修正前】
override func supportedInterfaceOrientations() -> Int {
if UIDevice.current.userInterfaceIdiom == .phone {
return Int(UIInterfaceOrientationMask.allButUpsideDown.rawValue)
} else {
return Int(UIInterfaceOrientationMask.all.rawValue)
}
}
【修正後】
override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return UIInterfaceOrientationMask.allButUpsideDown
} else {
return UIInterfaceOrientationMask.all
}
}
【解説】
サポートしている画面の向きを返すメソッド supportedInterfaceOrientations の定義が変わったようです。戻り値も変わってるので、それに合わせてreturnの内容も調整しました。

【エラーメッセージ】
‘CGContextSetLineDash’ is unavailable: Use setLineDash(self:phase:lengths:)
【修正前】
CGContextSetLineDash(UIGraphicsGetCurrentContext(), 0, len, len.count)
【修正後】
UIGraphicsGetCurrentContext()?.setLineDash(phase: 0.0, lengths: len) // 3.1
【解説】
そろそろ疲れてきました ←解説になってない
2年半ぶりのSwiftなので、オプショナル型とかアンラップとかの知識を付け直しました。

今日はもう寝ます。さようなら2017年7月15日。

・・・続く

実録! 2年半ぶりのSwiftアプリバージョンアップ 1日目 その3

コンバートしたらエラーが増えた!とか言っててもエラーが無くなる訳ではないので、諦めてエラー箇所を直していきます。

以下、修正したエラーです。

【エラーメッセージ】
GameScene.swift:218:19: Method does not override any method from its superclass
【修正前】
override func touchesBegan(_ touches: NSSet, with event: UIEvent) {
【修正後】
override func touchesBegan(_ touches: Set, with event: UIEvent?) { // 3.1
【解説】
メソッドの定義が変わったようです。overrideを指示しているにもかかわらず、該当のメソッドがスーパークラスに見つからないので怒られています。最新のメソッド定義に直しました。NSSetがSetになりました。Swift1.2でコレクションクラスSetが追加されたため、Objective-CからあったNSSetを置き換えたのでしょうね。UIEvent?は、オプショナル型というやつですね。nilを許容するようになりましたが、なぜ許容するようにしたんですかね?

【エラーメッセージ】
GameScene.swift:221:19: Method does not override any method from its superclass
【修正前】
override func touchesMoved(_ touches: NSSet, with event: UIEvent) {
【修正後】
override func touchesMoved(_ touches: Set, with event: UIEvent?) { // 3.1
【解説】
同じ

【エラーメッセージ】
GameScene.swift:224:19: Method does not override any method from its superclass
【修正前】
override func touchesEnded(_ touches: NSSet, with event: UIEvent) {
【修正後】
override func touchesEnded(_ touches: Set, with event: UIEvent?) { // 3.1
【解説】
同じ

【エラーメッセージ】
GameScene.swift:227:19: Method does not override any method from its superclass
【修正前】
override func touchesCancelled(_ touches: NSSet!, with event: UIEvent!) {
【修正後】
override func touchesCancelled(_ touches: Set, with event: UIEvent?) { // 3.1
【解説】
同じ

【エラーメッセージ】
Missing argument label ‘rawValue:’ in call
【修正前】
fileprivate let m_bitmapInfo : CGBitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedLast.rawValue)
【修正後】
fileprivate let m_bitmapInfo : CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue) // 3.1
【解説】
Swift3.0から、1つ目の引数にもラベル名が必要になったみたいです。今までは2つめ以降のみラベル名が必要だったので、統一感が出て良いのではないでしょうか。

【エラーメッセージ】
‘PremultipliedLast’ has been renamed to ‘premultipliedLast’
【修正前】
fileprivate let m_bitmapInfo : CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue)
【修正後】
fileprivate let m_bitmapInfo : CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) // 3.1
【解説】
‘PremultipliedLast’の先頭が小文字に変更になったとのことです。

【エラーメッセージ】
Value of type ‘String’ has no member ‘stringByAppendingPathComponent’
【エラー箇所】
let paths1 = NSSearchPathForDirectoriesInDomains(
FileManager.SearchPathDirectory.documentDirectory,
FileManager.SearchPathDomainMask.userDomainMask, true)
let path = paths1[0].stringByAppendingPathComponent(“settings.dat”)
【対策】
extension String {
func stringByAppendingPathComponent(_ path: String) -> String {
let nsSt = self as NSString
return nsSt.appendingPathComponent(path)
}
}
【解説】
NSSearchPathForDirectoriesInDomains の戻り値が、NSArray* から [String]に変わってしまったようです。で、stringByAppendingPathComponentはNSStringのメンバなので、呼べないと怒られています。対策として、エクステンションにより既存のStringクラスにstringByAppendingPathComponentを追加し、内部的にNSStringの該当メソッドを呼ぶようにしました。エクステンション素敵。なお、NSStringのメソッドstringByAppendingPathComponentは名前がappendingPathComponentに変更となったようです。

・・・続く

実録! 2年半ぶりのSwiftアプリバージョンアップ 1日目 その2

二度寝から目覚めたら、コンバートが終わっていました!
よかった!

Apple「変換しといたったで。ちゃんとどこ変わったか確認してな」

Apple「ちゃんと見てな。何かトラブっても知らんで」

いや変更箇所多すぎます。いちいち全部見てられないです・・・
「Save」ボタンを押下。

ワーニング2個、エラー51個

エラー増えました。

・・・続く

実録! 2年半ぶりのSwiftアプリバージョンアップ 1日目

2年半前にSwiftで作ったアプリのXcodeプロジェクトを開きます。

上記画像の通り、最終日付は2015年2月18日となっています。
果たして無事に開けるのでしょうか?

気合を入れてもうまく開ける訳ではありません。
無の心境で「xcodeproj」ファイルをダブルクリックします。

出ました!開いた途端にワーニング115個とエラー30個ですっ!
しかしそこはApple様、自動でコンバートしてくれるようです。
コンバートダイアログが出ましたっ!

「Swiftの構文を変換しますか?
ターゲット”Dotter”と”DotterTests”は以前のバージョンのSwiftコードを含みます。
これらのターゲットをSwift3に変換するには”Convert”を選択してください。
この操作は、後でエディットメニューの”Convert to Current Swift Syntax”
から実行できます。」

超気合を入れて”Convert”ボタンを押します!

・・・

・・

8時7分:”Convert”ボタンを押下
8時8分:あれ?これ動いてるのかな?不安になる
8時12分:眠くなってきた
8時19分:二度寝に突入

・・・続く

アプリ「Dotter」の改良

新作アプリ「My Tiny Planet」の制作にあたって、テクスチャ作りに自作アプリ「Dotter」を使っています。

「Dotter」はPNG専用ドット絵エディタです。

久しぶりに使ったら、当時気付かなかった使いにくい点がいくつかあったので改良しようと思っています。

・ペンや消しゴムの”幅”を 1 もしくは 3 もしくは 5 ドットから選べますが、変更するのにいちいち別画面を開かなくてはならないため、ドット絵を描くのと同じ画面で変更出来るようにします。

・グリッド線の表示/非表記も別画面を開かなくてはならないため、これも同じ画面で切り替えられるようにします。

・思った位置にドットが打てない場合があるので、右手で”カーソル”を動かし、左手(二点以上画面をタップ)でそのカーソル位置にドットを打つような、思った位置に超打ち易い描画モードを追加します。

この内容で近々バージョンアップするかも知れません。