
From Objective-C to Swift. Recommendations
- Transfer
Swift is Apple's new programming language, which it presented this year at WWDC. Along with the programming language, Apple has released an excellent reference on the Swift language, which I recommend reading or reading it. However, reading a book is a very long time! So if you don’t have much time and you just want to learn about the new Swift language, then this article is for you.
In this article, I would like to share some thoughts about the transition from Objective-C to Swift. I will try to give you some advice and point out the shortcomings of a different approach to both languages. Therefore, without unnecessary digressions, we turn to the article itself.
The first, most significant change worth mentioning is the abandonment of the interface.h / implementation.m structure.
I must admit that I am a supporter of this model. Getting and sharing class information simply with an interface file is safe and fast.
In Swift, the interface and implementation are not divided into two files. We just implement our class (and at the time of writing it is not even possible to add visibility modifiers).
If it is really difficult to cope with this change, then you can use the following: common sense.
We can easily increase the readability of our classes with good documentation. For example, you can move the elements you want to make “public” to the top of our file using extensions to distinguish between data accessible to everyone and personal information.
Another very common trick is to put the underscore “_” for private methods and variables.
Here is a small example of mixing them:
Although we cannot change the visibility of the elements of our classes, we can try to make access to some of the “more difficult” ones.
A non-standard solution is to use a nested class that partially hides personal data (at least in autocomplete)
Example:
We put the private implementation in a private permanent case and use the “regular” implementation in the class as a public interface. Private elements are not actually hidden, but to access them we must go through the "private" constants.
The question arises: is it worth the ultimate goal, the partial concealment of personal elements?
My suggestion is to wait for visibility modifiers (Apple is working on it), but for now, use good documentation with or without extensions.
In Objective-C, I rarely used constant keywords, even when I knew that some data would never change. In Swift, Apple developers suggest using a constant (let) instead of a variable (var). So just follow her and try to figure out the role of your variables. Ultimately, you will use more constants than you expect.
Look at the 2 lines of codes and find the difference:
During the first two weeks of working with Swift, I forced myself to remove the semicolon from each row of code. Now it’s easier (and I already forgot what it feels like in Objective-C).
Type inference makes it possible to assign a type to a variable, deriving it directly from its definition. This is another advantage that is a little difficult to master when it comes from using the detailed Objective-C language.
We should try to use sequential naming methods, otherwise it would be difficult for another developer (and for you) to determine the type deduced by the unsuccessful naming choice:
A more suitable name makes the job easier:
The next major change is the use of parentheses, which no longer need to be put:
Remember that what you write in brackets is evaluated as an expression and is not always allowed to record in this way. When linking variables, for example, we cannot use parentheses:
You don’t need to choose an interface or remove the semicolon and brackets, but you can consider these options as one way to write code in Swift. Ultimately, we increase readability and save time on typing and characters.
When working with functions that return “value” or “nothing”, have you ever wondered what is the best way to define “nothing”? I used NSNotFound, -1, 0, custom return values.
Thanks to Optionals, we have a “nothing-value” that is fully defined, we just need to add a question mark after the data type.
We can write:
In this example, the relationship "Man owns a car" is defined as Optional. This means that the “car” property can be zero, and a person could not have a car.
Then we turn to this value using an additional binding (if we let (let) the car =) or with the help of an expanded phrase (car?). If we do not define a property as optional, we must set a value for this property, otherwise the initialization function will throw an error.
The last opportunity to define a non-additional property value is within the initialization function.
Thus, we must determine how the properties of the class will interact with the rest of the class, and how they will behave during the existence of instances of the class.
These enhancements completely change the way we present our classes.
If you find it difficult to work with options, because you cannot understand why the compiler asks you to give an expanded value before using it ...
... I suggest thinking of the option additionally as a structure (it is a structure, therefore, it should not be too difficult), which does not contain the value directly, but adds a layer around it (envelops, frames-wrap). If the internal value is determined, you delete the layer (unwrap-unwrap) and get the required value, otherwise you get zero.
Forced unfolding through the “!” Sign is just a way to remove a layer without worrying about the internal size. You run the risk of trying to access the value behind the layer. If this value is zero, the application just crashes.
After several years of programming in Objective-C and Cocoa, we are dependent on the delegation pattern. However, we still use this scheme. Here is a super simple delegate usage example:
We replace the delegate existence check and use respondToSelector with an extra chain.
Please note that we must assign the protocol using the @obj keyword because we used @optional. By the way, the compiler warns us with a message in case we forgot to do this.
To implement this delegate, we implement the protocol in another class, and assign it, as well as in Objective-C:
Another common method that we use in Swift is the interactive element (target-action), in which case we use it as well as in Objective-C.
The slight difference is how we determine the segment address (selector). We simply write a prototype method using a line that will automatically be modified to:
Love it or hate it, Singleton is still one of the most accepted programming models.
Whether you like it or not, the Singleton pattern is one of the most applicable patterns. We can implement it using GDC and dispatch_once, or rely on the thread-safe nature of the let keyword.
Let's look at this code:
1. SharedReader is a static component (we can replace it with a function).
2. Static (non-component) properties are not yet allowed in the class implementation. So, thanks to nested types, we add a nested structure to the class. The structure supports static properties, so we just add a static property here.
3. The _instance property is a constant. It cannot be replaced with another value, and is thread safe.
We can access a single instance of DataReader using:
DataReader.sharedReader
In Swift, structure and computation have many characteristics that you can hardly apply in other languages.
They support:
As you can see, the structure uses the initialization function, which in this case is automatically created by Swift (we can add other input parameters for clients).
The enum syntax is slightly different from the one we used. It is defined by the keyword case:
Enum is not limited to its properties:
We can also build an enum with more complex characteristics:
In the previous code, we added a nested type (vitamin) and an additional property (mainVitamin), which assigns the initial values of the elements for this structure depending on the value of enum.
With Objective-C, we are used to immutable and mutable versions of any class. Some examples are NSArray and NSDictionary.
With Swift, we don’t need different types of data, we just use a constant or variable value in a new way.
A variable array is mutable, while with an array constant we cannot change our stored values. So just keep in mind the rule "let = immutable, var = variable" (Bug fixed: before Beta 3, you can change an immutable array).
I like the syntax of blocks, it's so clear and easy to remember!
By the way, after several years of Cocoa development, we got used to this syntax, and sometimes I prefer to replace light delegation tasks with blocks. They are meaningful, fast and well applicable.
In Swift, closure elements are similar blocks. They have many properties, and Apple has done a great job trying to simplify the way they are written. The Swift official documentation example is speechless. It begins with this definition:
and redesigned in:
Thus, there are various ways of implementing closure due to type inference, abbreviations ($ 0, $ 1) and direct functions (>).
In this article I do not describe the syntax of a closed expression, but I want to say a few words about the values of data collection within a closed expression.
In Objective-C, we define a variable as __block when we intend to change its value through Block. Using closures, in this case, becomes unnecessary.
We can access and change any value of the surrounding area. In fact, closed expressions are intelligent enough to capture external elements. The item is entered as a copy or link. If the closure changes the value of the element, it creates a link; if not, it creates a copy.
If a closure refers to an entry that contains or uses it, we may come across a circulation cycle.
Let's see an example:
The closed agePotion expression is used while maintaining a reference to the current instance. At the same time, this instance contains a link to close - and here there is a cycle of circulation.
To avoid this problem, we use the Capture list. This list associates a weak link with the instance that we want to use in Closing. The syntax is very simple - add a weak link before the Closing definition and the instance will get a weak link instead of a strong one.
We already know how weak reference works in Objective-C. It also works in Swift, there are no changes.
I really appreciated the introduction of this keyword because it is a good hint for determining the relationship between classes.
Let us describe a simple relationship between a person and his bank account:
1. A person can have a bank account (optional)
2. A bank account must belong to a person (required)
These relationships are going to create a cycle. The first solution will be to add a weak reference to the “Bank Account.owner” property. However, with the help of a weak link, we define another useful restriction: the property must always have a value, it cannot be equal to zero (thus, we satisfy item 2 from the previous list).
In fact, there is nothing more to say about the weak link. It works just like a weak link without increasing the case it points to and provides a value other than zero.
I must admit: sometimes I still work on the mistakes of the compiler. The more I use Swift, the clearer it becomes that I'm not wasting my time experimenting and studying. There are many interesting changes and things compared to Objective-C that did not exist before, and which motivate me to practice more.
This is a long-awaited novelty in the development of IOS / OSX and I am sure that you will love it!
ps The translation does not pretend to be the most correct and best translation on the Habré, if there is any comment, write in a personal, I will edit. Thank you for understanding.
In this article, I would like to share some thoughts about the transition from Objective-C to Swift. I will try to give you some advice and point out the shortcomings of a different approach to both languages. Therefore, without unnecessary digressions, we turn to the article itself.
Single file versus interface implementation file
The first, most significant change worth mentioning is the abandonment of the interface.h / implementation.m structure.
I must admit that I am a supporter of this model. Getting and sharing class information simply with an interface file is safe and fast.
In Swift, the interface and implementation are not divided into two files. We just implement our class (and at the time of writing it is not even possible to add visibility modifiers).
If it is really difficult to cope with this change, then you can use the following: common sense.
We can easily increase the readability of our classes with good documentation. For example, you can move the elements you want to make “public” to the top of our file using extensions to distinguish between data accessible to everyone and personal information.
Another very common trick is to put the underscore “_” for private methods and variables.
Here is a small example of mixing them:
// Public
extension DataReader {
var data { }
func readData(){
var data = _webserviceInteraction()
}
}
// Private implementation
class DataReader: NSObject {
let _wsURL = NSURL(string: "http://theurl.com")
func _webserviceInteraction()->String{
// ...
}
}
Although we cannot change the visibility of the elements of our classes, we can try to make access to some of the “more difficult” ones.
A non-standard solution is to use a nested class that partially hides personal data (at least in autocomplete)
Example:
import UIKit
class DataReader: NSObject {
// Public ***********************
var data:String?{
get{return private.internalData}
}
init(){
private = DataReaderPrivate()
}
func publicFunction(){
private.privateFunc()
}
// Private **********************
var private:DataReaderPrivate
class DataReaderPrivate {
var internalData:String?
init(){
internalData = "Private data!"
}
func privateFunc (){}
}
}
We put the private implementation in a private permanent case and use the “regular” implementation in the class as a public interface. Private elements are not actually hidden, but to access them we must go through the "private" constants.
let reader = DataReader()
reader.private.privateFunc()
The question arises: is it worth the ultimate goal, the partial concealment of personal elements?
My suggestion is to wait for visibility modifiers (Apple is working on it), but for now, use good documentation with or without extensions.
Constants and variables
In Objective-C, I rarely used constant keywords, even when I knew that some data would never change. In Swift, Apple developers suggest using a constant (let) instead of a variable (var). So just follow her and try to figure out the role of your variables. Ultimately, you will use more constants than you expect.
Write only what you need to write
Look at the 2 lines of codes and find the difference:
let wsURL:NSURL = NSURL(string:"http://wsurl.com");
vs
let wsURL = NSURL(string:"http://wsurl.com")
During the first two weeks of working with Swift, I forced myself to remove the semicolon from each row of code. Now it’s easier (and I already forgot what it feels like in Objective-C).
Type inference makes it possible to assign a type to a variable, deriving it directly from its definition. This is another advantage that is a little difficult to master when it comes from using the detailed Objective-C language.
We should try to use sequential naming methods, otherwise it would be difficult for another developer (and for you) to determine the type deduced by the unsuccessful naming choice:
let a = something()
A more suitable name makes the job easier:
let a = anInt()
The next major change is the use of parentheses, which no longer need to be put:
if (a > b){}
vs
if a > b {}
Remember that what you write in brackets is evaluated as an expression and is not always allowed to record in this way. When linking variables, for example, we cannot use parentheses:
if (let x = data){} // Error!
if let x = data {} // OK!
You don’t need to choose an interface or remove the semicolon and brackets, but you can consider these options as one way to write code in Swift. Ultimately, we increase readability and save time on typing and characters.
Optional
When working with functions that return “value” or “nothing”, have you ever wondered what is the best way to define “nothing”? I used NSNotFound, -1, 0, custom return values.
Thanks to Optionals, we have a “nothing-value” that is fully defined, we just need to add a question mark after the data type.
We can write:
class Person{
let name:String
let car:Car? // Optional value
init(name:String){
self.name = name
}
}
// ACCESSING THE OPTIONAL VALUE ***********
var Mark = Person(name:"mark")
// use optional binding
if let car = Mark.car {
car.accelerate()
}
// unwrap the value
Mark.car?.accelerate()
In this example, the relationship "Man owns a car" is defined as Optional. This means that the “car” property can be zero, and a person could not have a car.
Then we turn to this value using an additional binding (if we let (let) the car =) or with the help of an expanded phrase (car?). If we do not define a property as optional, we must set a value for this property, otherwise the initialization function will throw an error.
The last opportunity to define a non-additional property value is within the initialization function.
Thus, we must determine how the properties of the class will interact with the rest of the class, and how they will behave during the existence of instances of the class.
These enhancements completely change the way we present our classes.
Additional unpacking
If you find it difficult to work with options, because you cannot understand why the compiler asks you to give an expanded value before using it ...
Mark.car?
... I suggest thinking of the option additionally as a structure (it is a structure, therefore, it should not be too difficult), which does not contain the value directly, but adds a layer around it (envelops, frames-wrap). If the internal value is determined, you delete the layer (unwrap-unwrap) and get the required value, otherwise you get zero.
Forced unfolding through the “!” Sign is just a way to remove a layer without worrying about the internal size. You run the risk of trying to access the value behind the layer. If this value is zero, the application just crashes.
Delegation Template
After several years of programming in Objective-C and Cocoa, we are dependent on the delegation pattern. However, we still use this scheme. Here is a super simple delegate usage example:
@objc protocol DataReaderDelegate{
@optional func DataWillRead()
func DataDidRead()
}
class DataReader: NSObject {
var delegate:DataReaderDelegate?
var data:NSData?
func buildData(){
delegate?.DataWillRead?() // Optional method check
data = _createData()
delegate?.DataDidRead() // Required method check
}
}
We replace the delegate existence check and use respondToSelector with an extra chain.
delegate?.DataWillRead?()
Please note that we must assign the protocol using the @obj keyword because we used @optional. By the way, the compiler warns us with a message in case we forgot to do this.
To implement this delegate, we implement the protocol in another class, and assign it, as well as in Objective-C:
class ViewController: UIViewController, DataReaderDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let reader = DataReader()
reader.delegate = self
}
func DataWillRead() {...}
func DataDidRead() {...}
}
Programming Pattern - Target-action
Another common method that we use in Swift is the interactive element (target-action), in which case we use it as well as in Objective-C.
class ViewController: UIViewController {
@IBOutlet var button:UIButton
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: "buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonPressed(sender:UIButton){...}
}
The slight difference is how we determine the segment address (selector). We simply write a prototype method using a line that will automatically be modified to:
Selector("buttonPressed:")
Singleton Programming Template
Love it or hate it, Singleton is still one of the most accepted programming models.
Whether you like it or not, the Singleton pattern is one of the most applicable patterns. We can implement it using GDC and dispatch_once, or rely on the thread-safe nature of the let keyword.
class DataReader: NSObject {
class var sharedReader:DataReader {
struct Static{
static let _instance = DataReader()
}
return Static._instance
}
...
}
Let's look at this code:
1. SharedReader is a static component (we can replace it with a function).
2. Static (non-component) properties are not yet allowed in the class implementation. So, thanks to nested types, we add a nested structure to the class. The structure supports static properties, so we just add a static property here.
3. The _instance property is a constant. It cannot be replaced with another value, and is thread safe.
We can access a single instance of DataReader using:
DataReader.sharedReader
Structures and Computing
In Swift, structure and computation have many characteristics that you can hardly apply in other languages.
They support:
struct User{
// Struct properties
let name:String
let ID:Int
// Method!!!
func sayHello(){
println("I'm " + self.name + " my ID is: \(self.ID)")
}
}
let pamela = User(name: "Pamela", ID: 123456)
pamela.sayHello()
As you can see, the structure uses the initialization function, which in this case is automatically created by Swift (we can add other input parameters for clients).
The enum syntax is slightly different from the one we used. It is defined by the keyword case:
enum Fruit {
case orange
case apple
}
Enum is not limited to its properties:
enum Fruit:String {
case .orange = "Orange"
case .apple = "Apple"
}
We can also build an enum with more complex characteristics:
enum Fruit{
// Available Fruits
case orange
case apple
// Nested type
struct Vitamin{
var name:String
}
// Compound property
var mainVitamin:Vitamin {
switch self{
case .orange:
return Vitamin(name: "C")
case .apple:
return Vitamin(name: "B")
}
}
}
let Apple = Fruit.apple
var Vitamin = Apple.mainVitamin
In the previous code, we added a nested type (vitamin) and an additional property (mainVitamin), which assigns the initial values of the elements for this structure depending on the value of enum.
Mutable and immutable
With Objective-C, we are used to immutable and mutable versions of any class. Some examples are NSArray and NSDictionary.
With Swift, we don’t need different types of data, we just use a constant or variable value in a new way.
A variable array is mutable, while with an array constant we cannot change our stored values. So just keep in mind the rule "let = immutable, var = variable" (Bug fixed: before Beta 3, you can change an immutable array).
Blocks vs Closure
I like the syntax of blocks, it's so clear and easy to remember!
By the way, after several years of Cocoa development, we got used to this syntax, and sometimes I prefer to replace light delegation tasks with blocks. They are meaningful, fast and well applicable.
In Swift, closure elements are similar blocks. They have many properties, and Apple has done a great job trying to simplify the way they are written. The Swift official documentation example is speechless. It begins with this definition:
reversed = sort(names, { (s1: String, s2: String) -> Bool in
return s1 > s2
})
and redesigned in:
reversed = sort(names, >)
Thus, there are various ways of implementing closure due to type inference, abbreviations ($ 0, $ 1) and direct functions (>).
In this article I do not describe the syntax of a closed expression, but I want to say a few words about the values of data collection within a closed expression.
In Objective-C, we define a variable as __block when we intend to change its value through Block. Using closures, in this case, becomes unnecessary.
We can access and change any value of the surrounding area. In fact, closed expressions are intelligent enough to capture external elements. The item is entered as a copy or link. If the closure changes the value of the element, it creates a link; if not, it creates a copy.
If a closure refers to an entry that contains or uses it, we may come across a circulation cycle.
Let's see an example:
class Person{
var age:Int = 0
@lazy var agePotion: (Int) -> Void = {
(agex:Int)->Void in
self.age += agex
}
func modifyAge(agex:Int, modifier:(Int)->Void){
modifier(agex)
}
}
var Mark:Person? = Person()
Mark!.modifyAge(50, Mark!.agePotion)
Mark = nil // Memory Leak
The closed agePotion expression is used while maintaining a reference to the current instance. At the same time, this instance contains a link to close - and here there is a cycle of circulation.
To avoid this problem, we use the Capture list. This list associates a weak link with the instance that we want to use in Closing. The syntax is very simple - add a weak link before the Closing definition and the instance will get a weak link instead of a strong one.
@lazy var agePotion: (Int) -> Void = {
[unowned self](agex:Int)->Void in
self.age += agex
}
Unowned and Weak Links
We already know how weak reference works in Objective-C. It also works in Swift, there are no changes.
I really appreciated the introduction of this keyword because it is a good hint for determining the relationship between classes.
Let us describe a simple relationship between a person and his bank account:
1. A person can have a bank account (optional)
2. A bank account must belong to a person (required)
We can describe this relation with code:
class Person{
let name:String
let account:BankAccount!
init(name:String){
self.name = name
self.account = BankAccount(owner: self)
}
}
class BankAccount{
let owner:Person
init(owner:Person){
self.owner = owner
}
}
These relationships are going to create a cycle. The first solution will be to add a weak reference to the “Bank Account.owner” property. However, with the help of a weak link, we define another useful restriction: the property must always have a value, it cannot be equal to zero (thus, we satisfy item 2 from the previous list).
In fact, there is nothing more to say about the weak link. It works just like a weak link without increasing the case it points to and provides a value other than zero.
Conclusion
I must admit: sometimes I still work on the mistakes of the compiler. The more I use Swift, the clearer it becomes that I'm not wasting my time experimenting and studying. There are many interesting changes and things compared to Objective-C that did not exist before, and which motivate me to practice more.
This is a long-awaited novelty in the development of IOS / OSX and I am sure that you will love it!
ps The translation does not pretend to be the most correct and best translation on the Habré, if there is any comment, write in a personal, I will edit. Thank you for understanding.