SwiftでObjCも利用したアプリ開発のときにハマったこと


![192_　体力のない　_-_いなげな言葉いじり.jpg](https://qiita-image-store.s3.amazonaws.com/0/4943/3c6b79a3-2ffb-236d-e731-e207c5493ca0.jpeg "192_　体力のない　_-_いなげな言葉いじり.jpg")


## スカラー型のキャストはコンバージョンを使う

Swiftではスカラー型のキャスト(`Casting`)はありません。その代わりにコンバージョン(`Conversion`)を利用します。

- 参考 [The Basics / Integer and Floating-Point Conversion (The Swift Programming Language)](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_399)


```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)
```


### ObjCライクなキャストはできない

ObjCのようなキャスト構文は文法エラーになります。

```Swift
let index: NSInteger = 1
let width: CGFloat = 100
let r = width * (CGFloat)index
// => ERROR Consecutive statements on a line must be separated by ';'
```

### 補足
 
オブジェクト型はキャスト可能です。

- 参考 [Type Casting (The Swift Programming Language)](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/TypeCasting.html)



## CGFloatとFloatまたはDoubleの演算でエラーになる場合がある

Swiftで扱う`CGFloat`はビルドするアーキテクチャによってエイリアスが異なります。
シミュレーターが`iPhone5s`（arm64）と`iPhone5`,`iPhone4s`（armv7, armv7s）では挙動が変わります。


|アーキテクチャ|CGflotのエイリアス|
|-----------|------------|
|arm64|Double|
|armv7, armv7s|Float|


一見、コンパイルが通りそうですが`iPhone5s`シミュレーターでコンパイルするとエラーになります。

```Swift
let index: NSInteger = 1
let width: CGFloat = 100
let r1 = width * Float(index)
// => ERROR Could not find an overload for '*' that accepts the supplied arguments        
```

この場合は、`Double`でコンバージョンすると演算可能です。

```Swift
let index: NSInteger = 1
let width: CGFloat = 100
let r2 = width * Double(index)
// => OK
```

`CGFloat`との演算は常にに`CGFloat`にコンバージョンして演算することで、この問題を回避できます。

```Swift
let index: NSInteger = 1
let width: CGFloat = 100
let r = width * CGFloat(index)
// => OK
```


## ObjCのNSIntegerとSwiftのIntの演算

`NSInteger`とSwiftの`Int`は`typealias`された同じ型です。

```
// typealias NSInteger = Int
let x: CGFloat = 3
let y: Float = 3
let i: Int = 1
let n: NSInteger = 1
let w = i + Int(x) + Int(y) // => OK
let q = n + Int(x) + Int(y) // => OK

if n == i {
    NSLog("HERE!") // => OK
}
```

Swiftでは`NSUInteger`を利用できません。

```Swift
let u: NSUInteger = 1

// ERROR => Use of undeclared type 'NSUInteger'; did you mean to use 'Int'?
```

## ObjCのid型をパラメータにとるメソッドにSwiftのクロージャを渡す方法

以下の様なObjCメソッドにSwiftのクロージャを渡す方法です。

```ObjC
@interface HogeFuga : NSObject 
+ (void) hogeUsingBlock:(id)bock;
@end
```

ObjCの`id`型はSwiftの`AnyObject?`型と同義です。
クロージャは対応した型を有するので`AnyObject`型にはなりません。
よって`id`型にクロージャを代入できない問題が発生します。

```Swift
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'
```


解決策はObjCで型を明示したブロックを持つメソッドでラップします。

```ObjC
typedef void (^BlockHogeWrapper)();

@interface HogeFuga (Wrapper)
+ (void) hogeUsingBlockWrapper:(BlockHogeWrapper)block;
@end

@implementation HogeFuga (Wrapper)
+ (void) hogeUsingBlockWrapper:(BlockHogeWrapper)block;
{
	[HogeFuga hogeUsingBlock:block];
}
@end
```

## NSObject#descriptionをオーバーライドする

`NSObject#description`のオーバーライドはメソッドをオーバーライドするかと思いきや、プロパティのオーバーライドになります。

```Swift
class Hoge: NSObject {
	var name: String?
	var note: String?

	override var description: String! {
		get {
			return "Name = \(self.name), Note = \(self.note)"
		}
	}
}
```

メソッドをオーバーライドしようするとエラーになります。

```Swift
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
```


### 参考
- [Swift クラスから NSObject の description メソッドをオーバーライドする](http://program.station.ez-net.jp/special/handbook/swift/class/description-override.asp)


## クロージャ型プロパティのオプショナルな書き方

クロージャ型を丸括弧で括ります。

```Swift
class Fuga {
  var completion: ( () -> () )?
}
```


## ObjCのenum値をSwift記法で記載する方法がわからない

Swiftではenum値を記述するにはObjCと異りなり、`enum型 ドット enum値`の文法になります。
この記述


### 記述方法がわかりやすい例
enum型とPrefixに値を分割できます。

```ObjC
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
  UIViewAutoresizingNone                 = 0,
  UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
  …
}

// ObjC
UIViewAutoresizingFlexibleLeftMargin
```

```Swift
// Swift
UIViewAutoresizing.FlexibleLeftMargin
```

### 記述方法の差異が少ない例
enum型と値のPrefixが一部（複数形のs)異なります。

```ObjC
typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions) {
  UIViewAnimationOptionLayoutSubviews        = 1 <<  0,
  UIViewAnimationOptionAllowUserInteraction  = 1 <<  1,
  …
}

// ObjC
UIViewAnimationOptionLayoutSubviews
```

```Swift
// Swift
UIViewAnimationOptions.LayoutSubviews
```


### 記述方法がわかりにくい例

enum型と値のPrefixがPrefixが一致していない場合です。
一致している部分のPrefixで分割しています。

```ObjC
typedef NS_ENUM(NSInteger, HogeStyle) {
    HogeFugaDefault,
    HogeFugaValue1,
    HogeFugaValue2,
    HogeFugaSubtitle
};

// ObjC
HogeFugaValue1
```

```Swift
// Swift
HogeStyle.FugaValue1
```

## enum型のメソッド引数や変数にenumで定義された定数以外を設定するとエラーになる

NSIntegerで定義されたenum型にNSInteger型の値（数値）を直接代入するとコンパイルエラーになります。

```Swift
typedef NS_ENUM(NSInteger, UIViewContentMode) {
    UIViewContentModeScaleToFill,
	...
}

self.view.contentMode = 0
// => ERROR Cannot convert the expression's type '()' to type 'UIViewContentMode'
```

## ObjCのClass型をパラメータにとるメソッドにSwiftのClassを渡す方法 (Restkit)

Class型をパラメータにとるメソッドにSwiftのClassを代入し、そのClassがObjC側でnewされるケースです。

``` ObjC
@interface RKObjectMapping : RKMapping
{
// クラス型を引数にとる
+ (instancetype)mappingForClass:(Class)objectClass
}

...

@implementation RKObjectMappingOp …
{
	// 渡したクラス型でnewされる
    return [mapping.objectClass new];
}
```

`NSObject.Type`型を代入すれば、うまくObjC側でもnewできました。

```Swift
class HogeModel: NSObject {
}

var clazz: NSObject.Type = WTDHogeModel.self
let mapping: RKObjectMapping = RKObjectMapping(forClass: clazz) 
```

以下のObjC由来のClassの取得方法で代入するとエラーになり,
うまくできませんでした。

```Swift
// newするとERROR
var clazz: AnyClass = object_getClass(WTDHogeModel())

// ERROR => EXC_BAD_INSTRUNCTION
var clazz: AnyClass = NSClassFromString("WTDHogeModel") 
```