swiftのアプリ開発でハマったこと
DESCRIPTION
アジェンダ ・Swiftのアプリ開発でハマったこと 10個 ・Xcode6とSwiftのアプリ開発で踏んだ地雷たち8個 WantedlyではiOS, Android, Railsエンジニアを募集しています! 興味がある方はこちからご連絡ください [email protected] こちらからでもOKです 新しい技術にどんどん挑戦したいモバイルエンジニア・ウォンテッド!! https://www.wantedly.com/projects/7755TRANSCRIPT
Swift LT @Wantedly
2014-07-17 yohei sugigami
杉上 洋平 (スギガミ ヨウヘイ) GitHub: @susieyy 先月JOINしたばかりの新米プログラマー !!!
Wantedlyでは高いQiita力が求められる :)
Swiftで2位
・Swiftのアプリ開発でハマった
こと 10個
!
・Xcode6とSwiftのアプリ開発で 踏んだ地雷たち8個
アジェンダ
Xcode6で新規のアプリを開発した時に気づいたことをまとめます。アプリの仕様は以下のとおり。
!
- 開発言語はSwift
- Cocoapodsで既存Objcのライブラリを利用
- デプロイターゲットはiOS8以上
- Storyboardを利用
- AutolayoutとSizeClassesを利用
開発環境は以下のとおり。
!
- Xcode6はBeta2, 3を利用
- 検証実機はiOS8 Beta2, 3をインストールのiPhone5
!
Beta2で開発していたら、Beta3が出ました \(^o^)/
Swiftのアプリ開発でハマったこと
以下の様なObjCメソッドにSwiftのクロージャを渡す方法
@interface HogeFuga : NSObject+ (void) hogeUsingBlock:(id)bock;@end
ObjC id = Swift AnyObject
HogeFuga.hogeUsingBlock( { () -> () in println("Fuga")})
ERROR Type '() -> ()' does not conform to protocol 'AnyObject'
var closures: AnyObject = { () in println("Fuga")}
ERROR Type '() -> ()' does not conform to protocol 'AnyObject'
typedef void (^Block)();!@interface HogeFuga (Wrapper)+ (void) hogeUsingBlockWrapper:(Block)block;@end!@implementation HogeFuga (Wrapper)+ (void) hogeUsingBlockWrapper:(Block)block;{ [HogeFuga hogeUsingBlock:block];}@end
ObjCで型を明示したブロックを 持つメソッドでラップする
NSObject#descriptionをオーバーライドする
②
class Hoge: NSObject { var name: String? var note: String? override func description() -> String { return "Name = \(self.name), Note = \(self.note)" }}
ERROR Method does not override any method from its superclass
class Hoge: NSObject { var name: String? var note: String? override var description: String! { get { return "Name = \(self.name), Note = \(self.note)" } }}
プロパティのオーバーライドする
ObjCのenum値をSwift記法で記載する方法がわからない
③
SwiftUIViewAutoresizing.FlexibleLeftMargin
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) { UIViewAutoresizingNone = 0, UIViewAutoresizingFlexibleLeftMargin = 1 << 0, …}
ObjCUIViewAutoresizingFlexibleLeftMargin
わかりやすい例
SwiftUIViewAnimationOptions.LayoutSubviews
typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions) { UIViewAnimationOptionLayoutSubviews = 1 << 0, UIViewAnimationOptionAllowUserInteraction = 1 << 1, …}
ObjCUIViewAnimationOptionLayoutSubviews
許容範囲な例
SwiftHogeStyle.FugaValue1
typedef NS_ENUM(NSInteger, HogeStyle) { HogeFugaDefault, HogeFugaValue1, HogeFugaValue2, HogeFugaSubtitle};
ObjCHogeFugaValue1
複雑な例(Prefixが一致していない)
enum型のメソッド引数や変数にenumで定義された定数以外を設定するとエラーになる
④
typedef NS_ENUM(NSInteger, UIViewContentMode) { UIViewContentModeScaleToFill, ...}!self.view.contentMode = 0!// => ERROR Cannot convert the expression's type '()' to type 'UIViewContentMode'
Swiftの型は厳しいですね
スカラー型のキャストはコンバージョンを使う
⑤
let i: Int = Int(1.0)let n: NSInteger = NSInteger(1.0)!
let f: Float = Float(100)let d: Double = Double(100)let g: CGFloat = CGFloat(100)
Swiftではスカラー型のキャストはない
代わりにコンバージョンを利用
let index: NSInteger = 1let width: CGFloat = 100let r = width * (CGFloat)index!
// => ERROR Consecutive statements on a line must be separated by ';'
ObjCライクなキャストはできない
let someObjects: [AnyObject] = [ Movie(name: "2001: A Space", director: "Stan"), Movie(name: "Moon", director: "Duncan Jones"), Movie(name: "Alien", director: "Ridley Scott")]!for object in someObjects { let movie = object as Movie // Type Casting ! println("Movie: '\(movie.name)', dir. \(movie.director)") }
補足オブジェクト型はキャスト可能
for movie in someObjects as [Movie] { println("Movie: '\(movie.name)', dir. \(movie.director)") }
配列をキャストするとコンパクトに書ける
CGFloatとFloatまたはDoubleの演算
⑥
import CoreGraphics!
typealias CGFloat = Float
CoreGraphicsにはCGFloatはFloatとエイリアスされている
let index: NSInteger = 1let width: CGFloat = 100let r1 = width * Float(index)!
// => ERROR Could not find an overload for '*' that accepts the supplied arguments
CGFloatとFloatの演算
一見正しく動作しそうだがエラーになる
なぜなら iPhone5s(arm64)をターゲットにビルドしているから
このときCGFloatはSwiftのDoubleにエイリアスされる
let index: NSInteger = 1let width: CGFloat = 100let r2 = width * Double(index)// => OK
iPhone5s(arm64)の場合はDoubleだとコンパイルできる
let index: NSInteger = 1let width: CGFloat = 100let r = width * CGFloat(index)// => OK
CGFloatとの演算はCGFloatにコンバージョンしましょう
ObjCのNSIntegerとSwiftのIntの演算
⑦
typealias NSInteger = Int!
!
let i: Int = 1let n: NSInteger = 1let x: CGFloat = 3let y: Float = 3let w = i + Int(x) + Int(y) // => OKlet q = n + Int(x) + Int(y) // => OK!
if n == i { println("HERE!") // => OK}
let u: NSUInteger = 1!
// ERROR => Use of undeclared type 'NSUInteger'; did you mean to use 'Int'?
NSUInteger は利用できません
クロージャ型プロパティのオプショナルな書き方
⑧
class Fuga { var completion: ( () -> () )?}
丸括弧で括ります
ObjCのClass型をパラメータにとるメソッドにSwiftのClassを渡す方法
⑨
@interface RKObjectMapping : RKMapping {+(instancetype)mappingForClass:(Class)objectClass}!!@implementation RKObjectMappingOp…{ return [mapping.objectClass new];}
渡したClass型がObjC側でnewされるケース
(例: Restkit)
Swiftからどういう clazz を渡せばいいのか
class HogeModel: NSObject {}!!var clazz = ??? let mapping: RKObjectMapping = RKObjectMapping(forClass: clazz)
var clazz: AnyClass = NSClassFromString(“WTDHogeModel”)!
// ERROR => EXC_BAD_INSTRUNCTION
失敗① NSClassFromStringで渡す
var clazz: AnyClass = object_getClass(WTDHogeModel())!
!
// newでERROR[mapping.objectClass new];
失敗② object_getClassで渡す
var clazz: NSObject.Type = WTDHogeModel.self let mapping: RKObjectMapping = RKObjectMapping(forClass: clazz) !!
// => OK
結論 NSObject.Typeを渡す
シングルトンパターンを実装する3つの方法
⑩
!
!
遅延初期化(lazy initialization)とスレッドセーフ(thread safety)が考慮された実装
class Singleton { class var sharedInstance : Singleton { struct Static { static var onceToken : dispatch_once_t = 0 static var instance : Singleton? = nil } dispatch_once(&Static.onceToken) { Static.instance = Singleton() } return Static.instance! }}
dispatch_onceで定義
ObjCと同様の実装方法毎回これを書くにのは大変
let _SingletonSharedInstance = Singleton()!class Singleton { class var sharedInstance : Singleton { return _SingletonSharedInstance }}
グローバル定数で定義
今後Swiftの言語仕様が変更され局所的グローバル変数が定義できるようになれば、この実装方法がもっともシンプル
class Singleton { class var sharedInstance : Singleton { struct Static { static let instance : Singleton = Singleton() } return Static.instance }}
structのstatic定数で定義 (推奨)
Swiftのclassはstatic定数を利用できないので、structでネストしてstatic定数を保持
Xcode6とSwift&iOS8のアプリ開発で踏んだ地雷たち
コンパイルエラー XXXHeaderファイル has been
modified since the precompiled header
①
fatal error: file ‘…/UIKit.framework/Headers /UIVisualEffectView.h’ has been modified since the precompiled header …
対応方法 毎回なぜかなおる対応方法がまちまち !
- Xcode6再起動 - ビルドをクリーンする - DerivedDataを削除する - OSXを再起動する - Xcode6を入れなおす orz
② IBOutletのStoryboardとのコネクションありが表示されない場合がある
コンパイル後のアプリの挙動は正しく接続されている状態で動作していました
③ Storyboardのファイルを開いてもXMLで表示される
XML…
明示的に開きましょう
④ ソースコードハイライトと入力補完が効かなくなる
たまに復活もしますw
⑤ シミュレーターでアプリを起動しようとするとエラーになる
Xcode6とシミュレーターを再起動したらなおりました。
⑥ Xcode5とXcode6のシミュレーターで同時にアプリを起動できない
どちらかのシミュレーターを終了しましょう
⑦ switchと書くとXcodeがフリーズする
Beta3ではまだ出ていないです
⑧ 誤ってプロジェクトをXcode5で開いてStroyboardを開くとXcode6でStroyboardがおかしくなっている
Xcode5で誤って開いたら何やらエラーが
ファイルが破壊されました。 git reset などでファイルを戻しましょう。
Finderからプロジェクトファイルをダブルクリックして開くとXcode5で開かれることもあります。
Xcode5を起動すると、Xcode6で作業していたプロジェクトを勝手に開く場合があります。
予防策 !
1. gitなどで細かくコミットしてロールバックできるように !
2. Xcode6でプロジェクトを開いたらストーリボードを最終編集画面にして閉じない !
3. Xcode6を終了するときはプロジェクトを閉じてから終了する !
4. Xcode5を捨てるw
iOS,Android Rails エンジニア
募集中
[email protected]興味がある方はこちらからご連絡ください
新しい技術にどんどん挑戦したい モバイルエンジニア・ウォンテッド!!
こちらからでもOKです
おわり