Can be written in a single line if they have relevance.
let row: Int, column: Int
In case of initialization by writing value is on the right-hand side, do not write data type on the left-hand side.
// ◯
let text = "Hoge Fuga"
// ☓
let text: String = "Hoge Fuga"
// ◯
let flg = false
// ☓
let flg: Bool = false
// ◯
let frame = CGRectZero
// ☓
let frame: CGRect = CGRectZero
Howerver, in case data type is implicit, specification is necessary.
// ◯ Int / UInt / NSInteger / NSUInteger these data types should be distinguished so specification is necessary
let i: Int = 0
// ◯ Float / CGFloat / Double these data types should be distinguished so specification is necessary
let p: Float = 3.14
In case of initialization using New, do not write data type on the left-hand side since it is explicit already.
// ◯
let image = UIImage(named: "Hoge")
// ☓
let image: UIImage = UIImage(named: "Hoge")
// ◯
let size = CGSize(10.0, 10.0)
// ☓
let size: CGSize = CGRectZero
In case of initializing return value of class method using its class data types, do not write data type on the left-hand side since it can be analogized.
let device = UIDevice.currentDevice()
let app = UIApplication.sharedApplication()
In case of casting assignment, data type of variable definition is omitted.
// ◯
let text = data["text"] as String
// ☓
let text: String = data["text"] as String
There are many ways to write definition of empty array, associative array along with their initialization so we should refer to Apple standard only.
@see Collection Types
// Array
// ◯
var elements = [Int]()
// ☓
var elements: [Int] = []
// ☓
var elements: [Int] = [Int]()
// ☓
var elements = Array<Int>()
// Associative array
// ◯
var namesOfIntegers = [Int: String]()
// ☓
var namesOfIntegers: [Int: String] = [Int: String]()
// ☓
var namesOfIntegers = Dictionary<Int, String>()
Do not declare global variables.
// ☓
var globalValue: String = "init value"
class Hoge {
}
In case of variable definition, consider whether it can be defined as a constant or not. Unless it have to be defined as a variable no matter what, define it as a constant.
let notChangeString = "Hoge"
let notChangeArray = ["Hoge", "Fuga"]
let notChangeDictinary = ["Hoge": "Fuga"]
Write a lower-cased “k” prefix for constants of global scope, class scope.
// Swift
let kAnimationDuration = 0.3
// Objective-C
#define kAnimationDuration 0.3
Definition of expression can be written on right-hand side.
let kGoogleAnalyticsTrackingID = NSBundle.mainBundle().objectForInfoDictionaryKey("GoogleAnalyticsTrackingID") as String
To format the year part of a date as yyyy
, use yyyy
.
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd"
Swift / String and ObjC / NSString are two different classes. Use Swift / String for string.
// ◯
var text: String?
// ☓
var text: NSString?
If you want to use NSString, convert it into NSString.
// ◯
("Swift text" as NSString).lowercaseString
// ☓
"Swift text".bridgeToObjectiveC().lowercaseString
Use countElements for string count. countElements counts based on number of bytes of individual character. Length of NSString is counted by assuming UTF-16 is 2-byte code.
// ◯
countElements("Swift text")
// ☓
("Swift text" as NSString).length
Use isEmpty for zero length check of string.
// ◯
if "Hoge".isEmpty { }
// ☓
if countElements("Swift text") == 0 { }
// ☓
if ("Swift text" as NSString).length == 0 { }
Use equivalence operator to compare strings.
let flg: Bool = "hoge" == "fuga"
Use Swift notation for string format. However,
// ◯
"\(hoge) is \(fuga)"
// ☓
NSString(format: "%@ is %@", hoge, fuga)
// ◯
let someCGFloat: CGFloat = 0.25
String(format: "%.01f", Float(someCGFloat))
// ☓ Must be converted to Float, or it will become 0.0 which is incorrect
let someCGFloat: CGFloat = 0.25
NSString(format: "%.01f", Float(someCGFloat))
Use Swift’s numerical type for Integer. In iOS SDK > API > ObjC, NSInteger, NSUInteger have been replaced by Int, UInt.
// Example
// UITableViewDatasource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
Use Swift’s decimal type for decimal. However, do not use Swift’s Float for ObjC’s CGFloat. CGFloat may become Float or Double, according to 64-bit or 32-bit CPU.
// Example
// UITableViewDelegate
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
In case you want to use Float as CGFloat or return value as CGFloat, conversion is necessary.
let f: Float = 1.0
let c: CGFloat = 1.0
let r = CGFloat(f) + c
In Swift, Bool can be nil so it is often used. In ObjC, Scalar cannot be nil so NSNumber must be used in case nil is required.
var flg: Bool?
var index: Int?
Nil in Swift is nil, nil in ObjC is an object pointer which does not exist. That is the difference. You can assign nil to any data type by unwrapping variable.
var hoge: AnyObject? = nil
You also can assign nil value to associative array.
var hoge: String?
let hash: [String: String?] = ["hoge": hoge, "fuga": "not nil"]
println(hash)
// -> [hoge: nil, fuga: Optional("not nil")]
In case return value’s data type is void, data type is omitted.
// ◯
func chnageWorld(text: String) { }
// ☓
func chnageWorld(text: String) -> () { }
// ☓
func chnageWorld(text: String) -> Void { }
Consider the readability of first argument to use external argument name if necessary.
// ◯
func dateFromString(dateString: String) -> NSDate
dateFromString("2014-03-14") // It is possible to analogize that argument can be string
// ☓
func dateFromString(#dateString: String) -> NSDate
dateFromString(dateString: "2014-03-14") // Redundancy
// ◯
func convertPointAt(#column: Int, #row: Int) -> CGPoint
convertPointAt(column: 42, row: 13)
// ☓
func convertPointAt(column: Int, #row: Int) -> CGPoint
convertPointAt(42, row: 13) // First argument is implicit
Incase first argument’s name can be omitted and, setting the default value, write _.
func log(_ message: String = "default message")
log("this is message") // -> this is message
log() // -> default message
Use default argument.
// ◯
func stringFromDate(date NSDate = NSDate()) -> String
let text = stringFromDate()
// ☓
let date = NSDate()
func stringFromDate(date NSDate) -> String
let text = stringFromDate(date)
Do not abbreviate closure argument.
// ◯
let funcClosure = { (text: String) in
println(text)
}
// ☓
let funcClosure = { text in
println(text)
}
In case first row is too long, line break before the argument.
let funcClosure = {
(text: String) in
println(text)
}
In case define closure type then initializing it.
// Initialize closure
let funcClosure = { (text: String) in
println(text)
}
funcClosure("Hoge")
In case define closure type but assign value to it later.
// Assign value to closure later
var funcClosure: ((text: String) -> ())?
funcClosure = { (text: String) in
println(text)
}
funcClosure!(text: "Hoge")
Use trailing closure if possible.
In case argument data type can be anoligized, use simple argument name.
In case there is only one line of code inside closure and it request return value, ‘return’ statement is omitted.
// ◯
["Chris", "Alex", "Ewa", "Barry", "Daniella"].map {
($0 as NSString).lowercaseString
}
// ☓
["Chris", "Alex", "Ewa", "Barry", "Daniella"].map({
($0 as NSString).lowercaseString
})
// ☓
["Chris", "Alex", "Ewa", "Barry", "Daniella"].map { (value: String) -> String in
(value as NSString).lowercaseString
}
// ☓
["Chris", "Alex", "Ewa", "Barry", "Daniella"].map {
return ($0 as NSString).lowercaseString
}
In case there is only one line of code inside closure and it does not request return value, write only ‘return’ in the end. This is to prevent the return value returned eventhough it is void.
let funcClosure = { (text: String) -> String in
println(text)
return
}
Do not use ( ) for the evaluation formula after if.
// ◯
if a == b { }
// ☓
if (a == b) { }
Use for-in statement to state loop process.
// ◯
for index in 0..<3 {
println("index is \(index)")
}
// ☓
for var index = 0; index < 3; ++index {
println("index is \(index)")
}
// ◯
for value in array {
println("index is \(index)")
println("value is \(value)")
}
// ☓
for var index = 0; index < array.count; index++ {
let value = array[index]
println("value is \(value)")
}
Do not cast for-statement to each element of array, cast to the array itself.
// ◯
for view in self.view.subviews as [UIView] {
}
// ☓
for temp in self.view.subviews {
let view = temp as UIView
}
Write break when there is no process in case-process.
switch section {
case .ProfileSection:
println("HOGE")
case .SettingSection:
break // do nothing
default:
break // do nothing
}
Write open bracket in the same line with method or control statements’ bracket.
// ◯
func changeWorld() {
//Do something
}
// ☓
func changeWorld()
{
//Do something
}
// ◯
if user.isHappy {
//Do something
} else {
//Do something else
}
// ☓
if user.isHappy
{
//Do something
}
else {
//Do something else
}
Do not write semicolon at the end of statement.
// ◯
println("hoge")
// ☓
println("hoge");
Ending file by inserting line break.
Write enum and case by pascal case.
When case is int and sequence begins from 0, do not write data type of enum and case.
// ◯
enum CustomResult {
case Success, Error
}
// ☓
enum CustomResult: Int {
case Success = 0, Error
}
When each case have a value, insert break line for each case.
// ◯
enum CustomResult: String {
case Success = "success"
case Error = "error"
}
Omit the enum name in case data type is assigned to an explicit variable.
// ◯
button.setTitle("title", forState: .Normal)
var state: UIControlState = .Normal
// ☓
button.setTitle("title", forState: UIControlState.Normal)
var state: UIControlState = UIControlState.Normal
When define a global enum, consider if class scope, struct scope can be written or not.
// ◯
class NetworkManager {
enum CustomResult: String {
case Success = "success",
case Error = "error"
}
var customResult: CustomResult?
}
// ☓
enum CustomResult: String {
case Success = "success",
case Error = "error"
}
class NetworkManager {
var customResult: CustomResult?
}
In case inheriting from NSObject, state override for int, call super.init().
class ArticleData: NSObject {
override init() {
super.init()
}
}
In case not inheriting from NSObject, do as below.
class ArticleData {
init() {
}
}
TODO:
Do not use Optional Type or Implicitly Unwrapped Optional Type as property when using init to initialize.
By whether or not there is a change of property, use let or var to define.
However, if there is a possibility nil will be assigned, use optional to define.
// ◯
class ArticleData {
let createdAt: NSDate
init() {
self.createdAt = NSDate()
}
}
// ☓
class ArticleData {
let createdAt: NSDate!
init() {
self.createdAt = NSDate()
}
}
In case there is only get property, get() is omitted.
// ◯
class ArticleData {
var createdAt: NSDate { return NSDate() }
}
// ☓
class ArticleData {
var createdAt: NSDate {
get {
return NSDate()
}
}
}
TODO: didSet
TODO:
In case class inherited from NSObject, override description property.
class DataModel: NSObject {
override var description: String { return "something debug message" }
}
In case class not inherited from NSObject, conform to printable protocol.
class DataModel {
}
extension DataModel: Printable {
var description: String { return "something debug message" }
}
TODO
TODO
TODO
TODO
TODO
TODO
TODO
TODO
Outlet is defined by weak and unwrap. If you want to automatically generate the property from the street board, add private modifier.
In case remove view from parent view and you want to use this view inherently, state strong instead of weak. In case you want to use this property from external class, do not add private modifier.
// ◯
@IBOutlet private weak var button: UIButton!
// ☓
@IBOutlet weak var button: UIButton!
// ☓
@IBOutlet var button: UIButton!
// ☓
@IBOutlet weak var button: UIButton?
The type of action’s argument defines action’s original class.
// ◯
@IBAction func buttonTapped(sender: UIButton) {
println("button tapped!")
}
// ☓
@IBAction func buttonTapped(sender: AnyObject) {
println("button tapped!")
}
TODO
Swift
// MARK: - View life cycle
Objective-C
#pragma mark -
#pragma mark View life cycle
Swift
// WARNING: Clean up this code after testing
Objective-C
#warning Clean up this code after testing
Build setting -> Swift Complier Custom Flags -> -DEBUG (yes, including the –D prefix)
// Objective-C and Swift
#if DEBUG
// DEBUG CODE
#else
// PRODUCTION CODE
#endif
Use NSLog for exporting log due to these standpoints.
- NSLog is thread safe, println is not.
- NSlog is exported to device console, println is not.
// ◯
NSLog("%@ is %@", hoge, fuga)
// ☓
println("\(hoge) is \(fuga)")
However, println has following merits against NSLog.
-
Fast export.
-
Export appropriate string even when variable is not formatted as string.
Singleton is defined by static constant of struct.
// ◯
class Singleton {
class var sharedInstance : Singleton {
struct Static {
static let instance : Singleton = Singleton()
}
return Static.instance
}
}
Do not write as below.
Define by global constant
// ☓
let _SingletonSharedInstance = Singleton()
class Singleton {
class var sharedInstance : Singleton {
return _SingletonSharedInstance
}
}
Implement by dispatch_once
// ☓
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!
}
}