LoginSignup
67

More than 5 years have passed since last update.

Swift2.2 から Swift3.0 への変換実例メモ

Last updated at Posted at 2016-09-29

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, yCGPoint になったり、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, isDirisDir.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) {
}

drawRectdraw


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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
67