preface

At present, Swift language has been updated to 5.x version. I remember that Swift 4.x was still used when the project of new product was approved in March, 2018. During this period, I have been using CocoaPods as a third-party library management tool. Although Swift and Xcode have gone through many versions, the number of pod updates by the author is very few. For a long time, Xcode’s Issue Navigator was filled with warnings ⚠️, with Swift Compiler Warning in particular. Compiler warnings are numerous, and the following are the main components:

  • ‘***’ was never used, consider replacing with ‘_’ or removing it
  • Variable ‘***’ was never mutated, consider changing to ‘let’ constant
  • ‘public’ modifier is redundant for property declared in a public extension

Following the guideline “Don’t ignore compiler warnings,” the repository was recently updated and the warnings disappeared. The confusion over the use of access control specifications caused by this last one is the reason for writing this article.

A case in point

Take Kingfisher’s KingfisherCompatible protocol as an example, the relevant codes in version 4.10.1 are shown in the figure below:

The warning indicates that it is unnecessary to use the public modifier for a property declared in a public-decorated extension. It’s a bit convoluted to read, in other words: an extension decorated with public, whose properties need no longer be decorated with public. Relevant codes of 5.15.8 version after update are shown in the figure below:

As you can see, the extension KingfisherCompatible public modifier has been removed and kf’s has been retained. The 4.10.1 code suggests removing the kF modifier. What’s the difference? With that in mind, let’s look at the official documentation describing the protocol and Extension access control usage specifications.

Description

For Protocols:

Explicitly declaring an access level for a protocol in the definition phase enables the protocol to be used only in a specific access context. When a protocol is defined, the access level of its internal members is the same as that of the protocol by defaultpublic protocol, as is the default access level of its internal memberspublic.

usepublicOther types that implicitly provide default access levels for their internal membersinternal.

For Extensions:

The definition of theclass,structureenumerationType, which can be used anywhere accessibleextensionExtend it.New members in the extension have the same access level as members declared in the original type. For example, extend onepublicinternalThe default access level for new members in the extension isinternal;fileprivatefileprivate;privateprivate.

In this case, the modifier Access level is the same as extension Access by default. That is, the modifier Access level is internal and can be accessed only in Module AccessControl.

Or,By explicitly declaring an extension modifier, its members default to the new access level, and extension members can still explicitly declare access to override the default access level.

For the modifier to explicitly declare public, overwriting the default internal, the error disappears.

You cannot explicitly declare an access level for a protocol-compliant extension because the protocol implementation within the extension already provides the default access level by the protocol itself.

After reading the documentation, we try to answer the question of the above example and removeextension KingfisherCompatiblepublicModifier and removekfIs there any difference between the two ways?

5.15.8 version.

In the figure, extension KingfisherCompatible does not explicitly declare the access level and defaults to internal, so the default access level of kf is also internal. In this case, kf can only be used in the current Module and is not accessible externally. But in the code for the kf explicitly declared public, covers the original internal, as a result, developers can access kf, use KingfisherCompatibleInstance. Kf. SetImage to display images.

4.10.1 version.

The code suggests removing the public modifier for KF. Extension KingfisherCompatible explicitly declares public, so kf’s default access level is also public, which is already publicly accessible, and explicitly declares public redundant for kf.

Testing:

As you can see, the end result is the same. In this case, which criteria should we choose for development?

I think you should choose a way to explicitly declare access levels for members in Extension. This is reflected in many library apis, such as UIView:

One advantage of this approach is clarity. When looking at an API, its applicable version, calling object, and access level are declared in the header, making it easy to see what conditions it can be used for.

Imagine if an API didn’t explicitly declare its access level, its comments were screen-length, we didn’t even know if it was declared in definition or extension, and its access level was so important that we had to swipe our index finger over and over on the mouse wheel. The rapid and even thud sound, always reminding “the future is uncertain”. Sure, if you’re a trackpad user, two-finger swiping saves some effort, but I’m sure the API is extremely unfriendly to developers who use their left hand to hold down the trackpad while their right hand carefully drags the scrollbar.

I’m sure developers of all tastes want a quick and easy way to access their apis.

This is an analysis of access control usage only for the KingfisherCompatible protocol, but let’s look at the access control usage specification in more detail.

Access control

Access control, which hides code implementation details and specifies preferred available interfaces by restricting access between your code and other Source files and Modules.

Swift provides default access levels for a variety of scenarios, and if your app is single-target, there is no need to explicitly declare access levels.

The access control model is based on the concepts of Modules and Source files.

The module

A module is a separate code distribution unit that can be imported by another module using the import keyword. A framework, or an application delivered as a stand-alone unit, is called a module.

The source file

A source file is a source file in a module.

For brevity, all parts of code that can use access control, such as properties, types, and functions, are collectively referred to as entities.

The level of access

The five level

Swift provides five different access levels: open, public, Internal, Fileprivate, and private, in descending order of access.

  • open

Open modifies entities that are accessible in all source files of this module and any source files imported into other modules. Open can only modify classes and class members, indicating that it can be subclassed and overridden.

  • public

Public is similar to Open in that it modifies entities that are accessible in all source files in this module and in any source files imported from other modules. The only difference is that entities decorated by public cannot be subclassed or overridden. Such as:

  • internal

An internal modified entity that is only accessible in this module. Without an explicit declaration, the default access level for all entities in the code is internal. So, when you’re developing a single-target app, you don’t have to explicitly declare access levels for entities unless they’re visible. Unless you want to hide code details from other code in this module in specific scenarios, you can use Fileprivate or private.

For example, if you want to define one or more types in your own files, and you are worried that they will have the same name as those defined by others, you can do this:

Of course, our daily development process, should try to avoid the occurrence of the same name, but once necessary, may as well use a use.

  • fileprivate

Fileprivate modified entity accessible only within the current file. Such as:

  • private

An entity modified by private that is accessible only in the current closed declaration and in an extension of the same file as the declaration. Such as:

Use the general

You cannot define the current entity with a lower level of access. Such as:

  • In order tointernal,fileprivateprivateModifier type, cannot define onepublicVariable, because inpublicThis type is not available wherever the variable is used.

  • The access level of a function cannot be higher than its parameter type and return value type, because the types of its components are not available in the context in which such a function is used. Such as:

Default Access level

Without an explicit declaration, the access level for all entities in the code defaults to internal. If you define a public or internal type, its members’ access level defaults to internal. If you define a Fileprivate or private type, its members’ access level defaults to Fileprivate or private.

A tuple type

The access level of a tuple type is the same as that of all its constituent types.

Enumerated type

All cases of an enumeration type have the same access level as that type; you cannot explicitly specify an access level for a single case. In addition, enumeration types with primitive or associated values should have access levels no lower than enumeration types.

Nested types

A nested type has the same level of access as its containing type, except when the containing type is public. Nested types defined in public automatically have an internal access level. You can explicitly declare public for nested types to make them available externally.

subclass

Classes in the current access context, other classes in the current module, and classes decorated with open in different modules can be subclassed. A subclass has no higher access level than its parent class.

If it is in the same module as the subclassed class, you can override any class members (methods, attributes, initializers, subscripts, and so on) that are visible in the current access context. Any class member decorated with open can be overridden if the module is different.

Overrides can give a parent member a higher level of access, and even, to the extent that a parent member has access, a child can call a parent member even if the parent member has a lower level of access than a child member.

Constants, variables, attributes, and subscripts

Constants, variables, and attributes have access levels no higher than their type, and subscripts have access levels no higher than their subscript type or return value type.

Getter and setter

The access levels of constants, variables, properties, and subscripted getters and setters are automatically synchronized with them. You can restrict read-write ranges by giving setters a lower level of access than getters, such as internal(set), Fileprivate (set), or private(set). In certain scenarios, you may even need to use two access levels to control the scope of the setter and getter.

We can find some examples from third-party library source code. Such as Kingfisher:

Public keeps getters visible, and private(set) keeps setters invisible. So when an external module USES KFCrossPlatformImageViewInstance. Kf. TaskIdentifier = someValue, will find setter is inaccessible.

The initializer

The access level of a custom initializer can be higher than its type. The access level of the Required initializer must be the same as that of its type. Like function and method parameters, the initializer parameter types have access levels no higher than the initializer itself.

Default initializer

Swift automatically provides a parameterless initializer for structures or base classes that have default values for all attributes but no initializers themselves. The access level of the initializer is, by default, the same as that of the owning type. If the type is decorated with public, the access level of the default initializer is internal, and you can explicitly declare public for the default initializer to make it visible.

Structure type default member initializer

If the access level of any storage property of a structure type is Fileprivate or private, the default access level of the initializer is treated as fileprivate or private.

After age is modified private, the member initializer of type Person becomes invisible.

agreement

inheritance

The access level of the child protocol is no higher than that of the parent protocol.

Comply with the

A type can adhere to a protocol with a lower access level than its own. The context in which a type complies with a particular protocol is the lower of the access levels of both the type and the protocol. For example, a public type complies with an internal protocol and the access level is internal.

When a type complies with the protocol, ensure that the type of the protocol requirement is implemented and the access level is no lower than that of the protocol compliance.

Internal The Access level that complies with the Access protocol is Internal. Therefore, the modifier must be at least Internal.

extension

Private member in the extension

An extension to a class, struct, or enumeration in the same file whose contents can be treated as part of the original type declaration. So:

  • Declared in the primitive typeprivateMember, available in an extension of the same file;
  • Declared in the extensionprivateMember available in another extension of the same file;
  • Declared in the extensionprivateMember available in the original type of the same file.

This feature gives Extension the ability to organize code structure.

We know that developers in system libraries or third-party open source libraries have used this feature to partition code, so that code with the same or similar functionality can be aggregated, making the code structure clearer. In daily development work, especially multi-person collaborative development, in order to create files more quickly and ensure uniform code style, we will customize some common file templates, which may look like this:

As business logic increases, files tend to become progressively Massive, making it harder to find business logic. If you can partition the code early with Extension and annotate it appropriately, you can easily use Jump Bar to locate the target code.

The generic

The access level of a generic type or function is the minimum of its own access level and the access level of its parameter types.

Type the alias

Any type alias you define for access control is treated as a different type from the original. The access level of a type alias is no higher than that of the original type. Rules also apply to type aliases used to satisfy protocol compliance with the associated types.

In the preceding figure, the access level of the alias of Phone type public is higher than that of Object type internal. The access level of the Wine alias private is lower than that of the Object alias internal, which is normal. Winery complies with Factory protocol. The access level of internal for Product is higher than that of private for Wine.

The last

The introduction sequence of The above content is basically The same as The Access Control section of The Swift Programming Language Guide (hereinafter referred to as Guide). The content is mostly presented in The form of translation + examples.

With the Big Sur update, Safari comes with a translation feature. At the beginning, THE author tried Safari translation to save time. However, as far as the Guide translation results are concerned, the author did not feel the convenience brought by Safari translation. The occasional but not unexpected stiff translation in the translation, but increased the difficulty of understanding. The same paragraph of content, translation caused reading disabilities, far more serious than the original English. In view of such poor user experience, THE author finally chose to Translate by himself, and referred to Google Translate and Google Search more for confused concepts.

In the example section, I strive to complete the document with short code. In the process of the overall review, many problems were found, such as incomplete error information, incorrect spelling of names and lack of important notes, etc. The whole article was not completed until it had been revised for several times.

This article is mainly based on my personal understanding of the Access Control section of the Guide. Sample code snippet is excerpted from Kingfisher and written by the author. The author is well aware that his translation and coding skills are limited. If there is any misunderstanding or inaccurate expression in the article, please speak up to clarify any doubts.