Swift 2.2 から Swift 3.0 に自動変換した場合に、うまくいった場合と手動で直さなければいけなかった場合のまとめ。自分のプロジェクト内で、実際に発生した内容をメモにのしていたものです。生々しい名前は編集してあります。また、メモを取る時に操作ミスなどが混在している場合もあるので、ご留意くださいませ。
2017.12 加筆†
Foundation
// before
private let _gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
// after
private let _gregorian = Calendar(identifier: Calendar.Identifier.gregorian)
NSCalendar(calendarIdentifier:)
のイニシャライザは nil
を返す可能性があるが、Calendar(identifier:)
はSwiftの文法的に不正な値を渡せなくなり、戻り値はオプショナルではない。
// before
let defaults = NSUserDefaults.standardUserDefaults()
if let data = defaults.valueForKey("peerID") as? NSData {
}
// after
let defaults = UserDefaults.standard
if let data = defaults.value(forKey: "peerID") as? Data {
}
standardUserDefaults()
→ standard
// before
let scanner = NSScanner(string: item)
// after
let scanner = Scanner(string: item)
NSScanner
-> Scanner
Core Graphics
// before
return CGSizeMake(self.cornerRadius, self.cornerRadius)
// after
return CGSize(width: self.cornerRadius, height: self.cornerRadius)
CGSizeMake
→ CGSize
// before
var bounds = self.bounds
var bottomRect = CGRectZero
CGRectDivide(bounds, &bottomRect, &bounds, buttonHeight, .MaxYEdge)
// after
(bottomRect, bounds) = bounds.divided(atDistance: buttonHeight, from: .maxYEdge)
CGRectDivide
→ bounds.divided()
。 bounds.divided()
は Tuple を返す。自動変換はまず失敗。
// before
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context, UIColor(white: 0, alpha: 0.5).CGColor)
// after
let context = UIGraphicsGetCurrentContext()!
context.setStrokeColor(UIColor(white: 0, alpha: 0.5).cgColor)
CGContextSetStrokeColorWithColor()
-> context.setStrokeColor()
// before
CGContextMoveToPoint(context, x, y)
CGContextAddLineToPoint(context, x, CGRectGetMaxY(self.bounds))
CGContextStrokePath(context)
// after
context.move(to: CGPoint(x: x, y: y))
context.addLine(to: CGPoint(x: x, y: (self.bounds).maxY))
context.strokePath()
CGContext
はまるでオブジェクトかのような書き方が可能。
// before
extension CGRect {
var width: CGFloat { return CGRectGetWidth(self) }
var height: CGFloat { return CGRectGetHeight(self) }
}
// after
extension CGRect {
//var width: CGFloat { return self.width }
//var height: CGFloat { return self.height }
}
width
, height
は Swift 3 で用意されるようになりました。width
, height
は以前からあった模様。Swift 3 でエラーになるようになりました。
// before
if let document = CGPDFDocumentCreateWithURL(NSURL.fileURLWithPath(fullpath)) {
}
// after
if let document = CGPDFDocument(URL(fileURLWithPath: fullpath) as NSURL) {
}
// before
let objects = array.reverse()
// after
let objects = array.reversed()
// before
class ZCache<T: AnyObject> {
private var cache = NSCache()
...
}
// after
/*
class ZCache<T: AnyObject> {
private var cache = NSCache()
...
}
*/
NSCache
を Generics 見たいに使えるようにする NSCache
のラッパークラスを開発したが、Swift 3 では NSCache
自体が Generics なので、ラッパーの存在意義がなくなった?
// before
if let hashValue = self.object { return unsafeAddressOf(object).hashValue }
// after
if let hashValue = self.object { return Unmanaged.passUnretained(object).toOpaque().hashValue }
なぜこうなったか…
2017.12 加筆†:多分この方がいいかな。
let hashValue = ObjectIdentifier(object).hashValue
// before
let value = object.integerValue
let integerLength = sizeof(value.dynamicType)
// after
let value = object.integerValue
let integerLength = MemoryLayout<UInt32>.size
integerValue
の戻り値を将来にわたって、UInt32
4バイト である事を確認したいが、同様の書き方はむづかしそう。誰かが、object の integer の戻り値の型を変更しても、ランタイム時などに捕まえる事はむづかしそう。
// before
let data: NSData = ...
let string = String(bytes: data.bytes, length: data.length, encoding: NSWindowsCP1252StringEncoding)
// after
let data: Data = ...
let string = String(data: data, encoding: String.Encoding.windowsCP1252)
data.bytes
→ data
そのまま渡す。type
// before
CGPathAddArcToPoint(pathRef, nil, l, t, l, t+radius, radius)
// after
pathRef.addArc(tangent1End: CGPoint(x: l, y: t), tangent2End: CGPoint(x: l, y: t+radius), radius: radius)
x
, y
が CGPoint
になったり、tangent1End
, tangent2End
なんて名前がついたり…
// before
var isDir: ObjCBool = false
if NSFileManager.defaultManager().fileExistsAtPath(directory, isDirectory: &isDir) && isDir {
}
// after
var isDir: ObjCBool = false
if FileManager.default.fileExists(atPath: directory, isDirectory: &isDir) && isDir.boolValue {
}
NSFileManager
→ FileManager
, isDir
→ isDir.boolValue
// before
let MyNotification = "MyNotification"
NSNotificationCenter.defaultCenter().postNotificationName(MyNotification, object: nil)
// after
extension Notification.Name {
static let redrawPageNotification = Notification.Name("MyNotification")
}
NotificationCenter.default.removeObserver(self, name: .redrawPageNotification, object: nil)
// alternative - auto
NotificationCenter.default.post(name: NSNotification.Name(rawValue: MyNotification), object: nil)
GCD
// before
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
}
// after
DispatchQueue.global(qos: .default).async {
}
GCD の変わり方も大胆です。
Core Data
// before
let request = NSFetchRequest(entityName: entityItem)
request.predicate = NSPredicate(format: "(url == %@) AND (feed == %@)", link, feed)
let objects = try! self..managedObjectContext.executeFetchRequest(request)
return objects.first as? RSSItem
// after
let request = NSFetchRequest<RSSItem>(entityName: entityItem)
request.predicate = NSPredicate(format: "(url == %@) AND (feed == %@)", link, feed)
let objects = try! self.managedObjectContext.fetch(request)
return objects.first
NSFetchRequest()
→ NSFetchRequest<RSSItem>()
、Core Data も Generics で扱えるようになりました。
AppKit
// before
let mask: NSEventModifierFlags = [.ShiftKeyMask, .ControlKeyMask, .AlternateKeyMask, .CommandKeyMask]
// after
let mask: NSEventModifierFlags = [.shift, .control, .option, .command]
// auto
let mask: NSEventModifierFlags = [.shift, .control, .AlternateKeyMask, .command]
自動変換で、AlternateKeyMask
はなぜか変換してくれません。
UIKit
// before
override func drawRect(rect: CGRect) {
}
// after
override func draw(_ rect: CGRect) {
}
drawRect
→ draw
// before
self.backgroundColor = UIColor.clearColor()
self.layer.shadowColor = UIColor.blackColor().CGColor
// after
self.backgroundColor = UIColor.clear
self.layer.shadowColor = UIColor.black.cgColor
// before
@IBAction func playAction(sender: AnyObject) {
}
// after
@IBAction func playAction(_ sender: AnyObject) {
}
// before
let myView: MyView? = myViews.filter { CGRectContainsPoint($0.bounds, gesture.locationInView($0)) }.first
// after
let myView: MyView? = myViews.filter { $0.bounds.contains(gesture.location(in: $0)) }.first
// before
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
// after
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
registerClass
→ register
// before
self.dismissViewControllerAnimated(true, completion: nil)
// after
self.dismiss(animated: true, completion: nil)
ViewController
が消失。
// before
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
}
// after
override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
}
メソッド(関数)から、プロパティへ
// before
currentViewController.willMoveToParentViewController(nil)
// after
currentViewController.willMove(toParentViewController: nil)
willMoveToParentViewController()
→ willMove(toParentViewController:)
// before
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
if let touches = touches {
for touch in touches {
}
}
}
// after
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
}
}
Set<UITouch>?
→ Set<UITouch>
なので、optional binding が不要になった。
まとめ
必要があれば、順次追加していこうと思います。
Xcode Version 8.0 (8A218a)
Apple Swift version 3.0 (swiftlang-800.0.46.2 clang-800.0.38)