Friday, April 26, 2024
HomeProgrammingCool Swift Options That Are Hardly ever Used | by Alex Nekrasov...

Cool Swift Options That Are Hardly ever Used | by Alex Nekrasov | Oct, 2022


Reviewing 10 Swift language constructs

Photograph by Michał Kubalczyk on Unsplash

Swift is without doubt one of the strongest programming languages on the market. On the similar time, it’s comparatively straightforward to be taught. Realizing Goal-C I may change to it in lower than every week. Mixing traditional C-style directions like if and for, fashionable ideas like ARC and extensions and highly effective frameworks makes it top-of-the-line programming languages for each freshmen and professionals.

This simplicity creates an issue: many builders, once they begin writing apps, cease going deeper into the language options. I wrote my first app in Swift in 2014, however I carry on studying new hidden options of Swift. On this article, I’d prefer to evaluate 10 Swift language constructs that builders not often use of their first app.

In case your operate is messy, has many returns or throws and makes use of unmanaged assets or different code that wants cleanup ultimately, then defer is a good alternative for you.

openResource()

defer { freeResource() }

// Do no matter you want. Do not free the useful resource

Take a look at the code above. To start with we open a useful resource. It may be a file, database or community socket. We all know that by the top of this operate the useful resource must be launched, in any other case it’ll trigger a reminiscence leak or different issues.

Name defer proper after opening the useful resource. The deinitialisation code must be contained in the defer closure. Then use your file, database or socket with out worrying about releasing it. Regardless of what number of return statements you have got, you’ll be able to make sure that your unmanaged useful resource will probably be correctly… managed.

Lazy variables are initialised once they’re requested. It’s a terrific syntactic sugar, which turns this:

non-public var _innerVariable: SomeComplexType?

var visibleVariable: SomeComplexType {
get() {
if _innerVariable == nil {
_innerVariable = SomeComplexType()
}
return _innerVariable!
}
}

into this:

lazy var visibleVariable = SomeComplexType()

That is helpful if you want a variable to be initialised solely at a sure level. On the similar time you need it to be initialised as soon as solely, not recreated each time.

For instance, this code can be utilized to create a UI aspect:

lazy var titleLabel: UILabel = {
let label = UILabel()
label.textual content = "Display screen title"
return label
}()

func createLayout() {
addSubview(titleLabel)
}

The guard assertion is definitely generally utilized in Swift, however I need to point out it right here as a result of it is distinctive for mentioned programming language. After switching from one other language like C++, Python and even Goal-C, you might discover it unusual.

Typically talking, guard is much like the if assemble, however with inverted logic. The principle distinction is that guard ought to all the time finish with a circulate termination assertion (return, throw, break…).

The most typical use case is getting non-optional variables. Let’s say that we’re writing a technique utilizing an non-compulsory class property. However this technique is smart solely when the property shouldn’t be nil.

Right here’s the trick:

guard let nonOptional = optionalVariable else {
// Present a message
return
}

// Use nonOptional

Equally to theif assertion, guard might have a number of situations separated with commas, and eradicating optionality shouldn’t be the one factor it will probably do. For instance, that is one other legitimate use case:

guard let field1 = field1,
!field1.isEmpty,
let field2 = field2,
!field2.isEmpty
else {
return
}

As you’ll be able to see within the instance above, the guard assertion can redefine constants and variables, making them non-optional.

Some previous languages like C++ have an fascinating characteristic referred to as a number of inheritance. It implies that one class may have two or extra base (or tremendous) courses. For instance, if class A has operate a(), class B has operate b(), class C extending A and B could have each capabilities a() and b(). The identical applies to class variables, constants and every part else.

This characteristic was certainly extraordinarily highly effective, nevertheless it created so many points and uncertainties that IT specialists included it into an inventory of dangerous practices. Like goto.

All fashionable languages enable to inherit from one base (or tremendous) class solely, and added interfaces or protocols in Swift terminology. The drawback of protocols is that they will’t have their very own performance. Or…can they?

A protocol actually can’t comprise any knowledge. And there’s no stroll round it. However you’ll be able to add a operate, which will probably be out there for all courses inheriting the protocol.

Let’s say you’re writing a File class, which provides entry to a file system. And we’ve a operate open(entry: Entry). Entry defines the requested entry rights to a file.

We would like ReadableFile and WritableFile. ReadableFile could have a operate open(), which calls open(entry: .learn). It will likely be an analogous operate for WritableFile.

Now we want a file, which is each readable and writable. And right here’s the place a number of inheritance would work. However Swift doesn’t have it. We’ll have to make use of protocols:

MyFile from the instance above now has entry to all capabilities: get() -> String, readText() -> String, set(textual content: String), write(textual content: String), and so on. These capabilities can name one another so long as the referred to as operate is asserted in the identical protocol or base protocol.

If any of the capabilities shouldn’t be outlined (doesn’t have code), it should be outlined within the class which adopts the protocol.

Asynchronous capabilities exist in lots of languages, however they didn’t seem in Swift till model 5.5. You should use them solely since iOS 13, iPadOS 13, macOS 10.15, tvOS 13 and watchOS 6. In case your app must run in iOS 12 or macOS 10.14, you gained’t have the ability to use concurrency.

The way it seems:

func asyncTask() async throws -> Outcome {
...
}

Job {
do {
let end result = await asyncTask()
print(end result)
} catch {
print(error.localizedDescription)
}
}

The way it works:

Job is a construction creating an asynchronous context. You may name asynchronous capabilities solely from a context (Job closure or one other asynchronous operate).

Asynchronous capabilities must be marked with the key phrase async. The key phrase throws implies that the operate can fail. A typical Swift operate would look this fashion:

func process(completion: @escaping (end result: Outcome?, error: Error?) -> Void) {
...
}

To name an asynchronous operate it’s good to use the await key phrase.

Why would you employ it? Effectively, it makes the code a lot simpler to learn and check. As an alternative of completion blocks with optionals, doable retain cycles and different mess, we’ve an easy-to-read code wrapped in do-catch block and Job construction.

Concurrency in Swift is moderately straightforward to implement, however there’s one essential second. Asynchronous capabilities work in separate threads. It means that you could’t replace the UI, navigate, present popups, and so on.

To do it, it’s good to change to MainActor. There are 3 methods:

await MainActor.run {
// Replace UI
}
@MainActor func iWillRunInMainThread() {
// This operate will run in UI thread
}
Job { @MainActor in
// Capabilities right here will run on UI thread
}

You may select probably the most applicable method relying in your context.

Intervals or ranges could be closed or open. Open intervals don’t embrace edge values, whereas closed intervals embrace the entire vary.

For instance:

let range1 = 1...5 // 1, 2, 3, 4, 5

let range2 = 1..<5 // 1, 2, 3, 4

Notice: previous variations of Swift had separate knowledge varieties for intervals. Later they grew to become one frequent sort — Vary.

Swift permits to make use of intervals in changecase constructions. For instance:

change age {
case 0..<18:
print("You are minor")

case 18...:
print("You are grownup")

default:
print("You were not born but")
}

Intervals can overlap one another. They may also be used with easy values in the identical change. This is the way it works:

  • a change assertion calculates the offered worth, or reads the worth of variable from reminiscence;
  • from prime to backside it checks if the worth from change matches the worth from case;
  • if it doesn’t match mentioned worth, it goes to the following case till it ends. If nothing matched, it executes a default block;
  • if it does match, it executes the code block and terminates the change execution except the fallthrough key phrase is used;
  • if fallthrough is used, it executes the following case block after the matched one. Then it does not do any matches anymore.

When you want a number of values, not an interval, you’ll be able to write them utilizing commas:

case 1, 5, 10:
// ...

A novel, or at the least not frequent characteristic of Swift is enum related values. Every enum case can comprise knowledge.

Let’s have a look at an instance. We need to enable customers to decide on a gender. If the consumer doesn’t establish themselves as feminine or male, we need to enable them to enter one other gender. On the similar time, we need to maintain the awesomeness of enumeration:

enum Gender {
case feminine
case male
case different(gender: String, pronounce: String)
}

Right here’s how we are able to outline a gender:

let gender = Gender.different(gender: "Non-binary", pronounce: "them")

Then we are able to extract these values in a case block:

change gender {
case .feminine:
print("Say 'hello' to her")

case .male:
print("Say 'hello' to him")

case let .different(_, pronounce):
print("Say 'hello' to (pronounce)")
}

When you don’t must deal with all of the circumstances, you need to use a if assertion:

if case let .different(genderName, _) = gender {
print(genderName)
}

Chaining is one in every of my favorite options of recent programming languages. Chaining is particularly helpful if you work with optionals, which is quite common in Swift.

Let’s have a look at an instance — We have now an inventory of things in an non-compulsory variable referred to as gadgets. Every merchandise has an non-compulsory id. We have to ship an inventory of ids to the server.

var gadgets: [Item]? = ...
var ids = [Int]()
if let gadgets = gadgets {
for merchandise in gadgets {
if let itemId = merchandise.id {
ids.append(itemId)
}
}
}

With chaining the above instance can flip into one line of code:

var gadgets: [Item]? = ...
let ids = gadgets?.compactMap { $0.id } ?? []

That is already a giant simplification. Now let’s say that we’ve just one id, the primary one non-nil id.

var gadgets: [Item]? = ...
var id: Int?
if let gadgets = gadgets {
for merchandise in gadgets {
if let itemId = merchandise.id {
id = itemId
break
}
}
}

We are able to simplify this:

var gadgets: [Item]? = ...
let id = gadgets?.compactMap { $0.id }.first

The true energy of chaining is seen if you work with extra complicated constructions. Let’s say that the Merchandise construction has one other construction area – description, which has a area shade.

If it’s good to get an inventory of colors, for instance, to assemble statistics, you are able to do it this fashion:

var gadgets: [Item]? = ...
let colours = gadgets?.compactMap { $0.description }.compactMap { $0.shade } ?? []

Or:

var gadgets: [Item]? = ...
let colours = gadgets?.compactMap { $0.description?.shade } ?? []

Apart from compactMap, you need to use forEach, map, filter and different capabilities. If the left aspect is non-compulsory, it’s good to add ? earlier than ..

Weak variables enable to keep away from retain cycles. Retain cycles is the state when two objects have sturdy references to one another. This forces an object to all the time keep in reminiscence, even when it’s not in use anymore.

A typical instance is the view controller and presenter. Or the view controller and think about mannequin. They should move info to one another. The best implementation is maintaining hyperlinks to one another. Whenever you shut the app display, it doesn’t must maintain them in reminiscence, however ARC (Computerized Reference Counter) releases an object solely when no different objects have sturdy reference to it.

Let’s have a look at an instance:

class MyController: UIViewController {
var viewModel: MyViewModel?

// ...
}

class MyViewModel: NSObject {
var controller: MyController?

// ...
}

It could be higher to make use of protocols, nevertheless it wouldn’t repair the retain cycle downside, so we gained’t use them right here to save lots of time.

Throughout initialisation viewModel and controller will begin referencing one another. When iOS sees that MyController shouldn’t be in dismiss, it’ll attempt to free it, however MyViewModel nonetheless references it. The identical applies to MyViewModelMyController is referencing it. Easy methods to repair it?

class MyController: UIViewController {
var viewModel: MyViewModel?

// ...
}

class MyViewModel: NSObject {
weak var controller: MyController?

// ...
}

Not like default sturdy references, weak references don’t maintain the article. As an alternative, they alter to nil when the article is launched. Within the instance above this may occur after dismiss:

  • iOS will examine if there are sturdy references to MyController
  • there aren’t any sturdy references, the one one is weak
  • iOS releases an occasion of MyController. controller variable of MyViewModel occasion will robotically set to nil
  • since MyController is launched, there will probably be no extra sturdy references to MyViewModel
  • occasion of MyViewModel can be launched

One other instance of weak variables is @IBOutlet references if you use storyboards or xibs.

And another word earlier than we change to the following subject — Typically we carry out an asynchronous operation with a completion handler. For instance:

func loadData() {
community.load { knowledge, error in
if let error = error {
self.present(error)
} else {
self.present(knowledge)
}
}
}

On this instance, the closure has a robust reference to a calling class. If it’s an occasion of UIViewController, it’ll depart it in reminiscence till knowledge is obtained even when the consumer goes again. However we need not present something if the consumer closes the display. So, why will we maintain it in reminiscence and course of the end result?

The weak key phrase will resolve this downside:

func loadData() {
community.load { [weak self] knowledge, error in
if let error = error {
self?.present(error)
} else {
self?.present(knowledge)
}
}
}

When you have plenty of references to self, you need to use a guard assertion:

func loadData() {
community.load { [weak self] knowledge, error in
guard let self = self else { return }
if let error = error {
self.present(error)
} else {
self.present(knowledge)
}
}
}

One other approach to keep away from retain cycles is to make use of the unowned key phrase. It is much like weak, nevertheless it assumes that unowned object will probably be in reminiscence. If the referred occasion was launched, it could trigger a crash.

One other fashionable characteristic is property observers. In older programming languages it doesn’t exist, so many builders who switched to Swift from different languages don’t know tips on how to use it.

Let’s say we’ve a category with a property:

class MyClass {
var prop: Int = 0

func propChanged() {
}
}

We need to know when prop was modified and take some motion. For instance, if we’ve a color construction, containing purple, inexperienced, and blue parts, we have to redraw it when one of many parts is modified.

The apparent approach to do it’s to name propChanged() each time we set a prop. Nevertheless it’s a quick approach to get bugs. Chances are you’ll overlook to name or, different developer might not even know that it’s a necessity.

One other method is to make prop and propChanged non-public and add a operate:

func changeProp(_ newValue: Int) {
prop = newValue
propChanged()
}

It will work, nevertheless it’s plenty of code. You could have dozens of properties. And also you don’t enable to get the worth as now it’s non-public.

Right here’s the suitable method:

class MyClass {
var prop: Int = 0 {
didSet {
propChanged()
}
}
}

didSet is a property observer. It is a common Swift operate that may use the brand new worth of prop. It is referred to as each time the worth is modified, so that you need not fear about it anymore.

There are two observers solely:

  • willSet is named earlier than a brand new worth is ready. prop nonetheless has the previous worth. You should use it to launch previous assets and put together for a brand new worth.
  • didSet is named after a brand new worth is ready.

Property wrappers

Property wrappers enable to do all doable manipulations with knowledge when it’s set or get. One typical instance of that is setting a knowledge vary, like if we have to restrict a price with a spread from 1 to 5. This can be a typical case for ranking. If the choices allowed are to provide 1 to five stars, 6 will probably be an error. When you get it from backend, you’ll be able to assume it was a miscalculation or a nasty rounding.

@propertyWrapper
struct Score {
non-public var quantity = 1

var wrappedValue: Int {
get { return quantity }
set { quantity = max(min(newValue, 5), 1) }
}
}

struct Person {
@Score var ranking: Int
}

var consumer = Person()
consumer.ranking = 10

print(consumer.ranking)
// 5 is printed

In some circumstances displaying an error could also be a greater answer, however there are nonetheless many circumstances the place knowledge processing could also be a good suggestion if you get or set a price to a variable.

I’m certain that for those who’re an skilled Swift developer most of those options, if not all of them. However for those who’re new in Swift, otherwise you didn’t go deep into documentation or studying different individuals’s code, you might not know a few of them, as they’re not utilized in different programming languages.

I hope it was helpful for you. Completely satisfied coding and see you subsequent time!

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments