Why private is unaccessible to the extension? - swift

Here is my ViewController.swift file:
class ViewController: UIViewController {
private var myInt = 10
}
extension ViewController {
func printMyInt() {
print(myInt)
}
}
Although as mentioned in the Swift documentation - Access Levels section:
Private access restricts the use of an entity to the enclosing
declaration, and to extensions of that declaration that are in the
same file. Use private access to hide the implementation details of a
specific piece of functionality when those details are used only
within a single declaration.
Since Swift 4 has been released, I assume that I am able to implement such a thing (it is also mentioned in: What's New in Swift - WWDC 2017 session), however, the complier shows me:
'myInt' is inaccessible due to 'private' protection level
Is it incompatible with what mentioned in the documentation?
As a simple quick solution, I could declare it as:
fileprivate var myInt = 10
but I wonder why is it behaves like this, am I misunderstand what mentioned in the documentation? or is it a "Xcode" bug (used 9.0.1 version)?
Remark: The project has been created in the older Xcode 8 and then migrated to Xcode 9.

In Swift 4, private members are accessible to extensions of that declaration that are in the same file, see SE-0169 – Improve Interaction Between private Declarations and Extensions.
If the project has been created in Xcode 8 (with Swift 3) then
Xcode 9 will open it in "Swift 3 mode" and set the "Swift Language Version" to "Swift 3.2". Therefore the stricter Swift 3 restrictions hold.
To make the private extension visible to extension in the same file,
set the Swift language version to 4 in the build settings. (Of course
that might make more changes in your code necessary.)
Even better, use "Edit -> Convert -> To Current Swift Syntax ..."

Related

Use of unresolved identifier: kCGWindowImageDefault (and other Core Graphics constants)

I'm new to Swift development. I'm writing an app for MacOS (not iOS) in Swift, and am trying to adapt some of the code from this Apple sample Objective-C project for use in my program.
The problem I'm hitting is that certain Apple-defined constants such as kCGWindowImageDefault and kCGWindowListOptionAll are causing XCode to report the compile-time error "Use of unresolved identifier [identifier]".
Somewhat surprisingly, though, if I right-click kCGWidowImageDefault and select "Jump to Definition", XCode does jump to the definition of that constant (in CoreGraphics > CGWindow.h > CGWindowImageOption) -- so XCode does seem to know what that constant is.
Here are the relevant snippets of my ViewController.swift file:
import Cocoa
import CoreGraphics
class ViewController: NSViewController {
...
func myFunction() {
// *** XCode reports the error on kCGWindowImageDefault on this line:
let imageOptions : CGWindowImageOption = kCGWindowImageDefault
...
}
}
The Apple documentation (as linked above) doesn't indicate what needs to be imported for these constants to be used.
XCode does appear to successfully recognize the types I'm using such as CGWindowImageOption -- it's just the constants that it isn't recognizing.
What do I need to do in order to successfully be able to use kCGWindowImageDefault and similar constants in my Swift MacOS program?
kCGWindowImageDefault is only for Objective-C, not Swift. In Swift, CGWindowImageOption is an OptionSet. For the default you simply use an empty option set:
let imageOptions : CGWindowImageOption = []
For CGWindowListOption you can do:
let listOptions: CGWindowListOption = .optionAll
Be sure you look at the Swift reference documentation for these enumerations. You can't use the Objective-C values.

Bulk fix hundreds of "#selector not exposed to Objective-C" errors in Xcode 9 or 9.1

Is there any way to fix multiple errors at once by Adding '#objc' to expose this instance method to Objective-C, I actually had over 200 of these errors, after I done fixed about 70 errors, got tired sick if doing this one by one.
I tried selecting multiple of these errors in issue navigator but dont see a way of Fix at once.
The only way I see is to select one error at a time and hit Fix, ooh this is sickening, please help if you know any way.
You'll want to run the migrator – go "Edit > Convert > To Current Swift Syntax..." in Xcode, and select "Minimize Inference" for the "Swift 4 #objc inference" option (I actually didn't realise until recently that the most of what the migrator does is just automatically applying compiler fix-its).
If you're already in a Swift 4 target, you won't be able to run migration on it. You can fix this by just changing the "Swift Language Version" build setting to "Swift 3.2" for that target, and then running the migrator on it (which will switch the version back to 4).
After the migrator has run, you'll notice that the "Swift 3 #objc inference" build setting has been set to "On" – you'll want to test your program with this build setting to ensure you don't get any runtime warnings about Obj-C entrypoints that aren't marked as #objc, and then change the build setting to "Default" when done, as discussed in this Q&A.
Additionally, after migration, feel free to look over the places where #objc has been added – you may be able to neaten code up by using a single #objc extension of the given class to define a group of methods that you want to expose to Obj-C. If you're looking at a class that requires Obj-C exposure for all its compatible members, then you can mark the class #objcMembers. See this Q&A for more info on that.
You can use the #objcMembers attribute on your whole class or struct.
You need to choose the classes on which you want to apply the #objcMembers attributes.
You can follow this tutorial.
According to Apple doc:
When a Swift class introduces many new methods or properties that require behavior from the Objective-C runtime, use the #objcMembers attribute in the declaration of that class. Applying the #objcMembers attribute to a class implicitly adds the #objc attribute to all of its Objective-C compatible members. Because applying the #objc attribute can increase the compiled size of an app and adversely affect performance, only apply the #objcMembers attribute on declarations when each member needs to have the #objc attribute applied.

The use of Swift 3 #objc inference in Swift 4 mode is deprecated?

Briefly, while using Xcode 9 Beta, I have run into the following warning:
The use of Swift 3 #objc inference in Swift 4 mode is deprecated. Please address deprecated #objc inference warnings, test your code with “Use of deprecated Swift 3 #objc inference” logging enabled, and disable Swift 3 #objc inference.**
After some research, I still have no idea how to fix the issue.
I would greatly appreciate any tips on how to fix this issue as well as an explanation of what is going on.
My goal is to grasp a better understanding of what is happening with my code.
I got rid of this warning by changing the "Swift 3 #objc Inference" build setting of my targets to "Default".
From this article:
Before Swift 4, the compiler made some Swift declarations automatically available to Objective-C. For example, if one subclassed from NSObject, the compiler created Objective-C entry points for all methods in such classes. The mechanism is called #objc inference.
In Swift 4, such automatic #objc inference is deprecated because it is costly to generate all those Objective-C entry points. When "Swift 3 #objc Inference" setting is set to "On", it allows the old code to work. However, it will show deprecation warnings that need to be addressed. It is recommended to "fix" these warnings and switch the setting to "Default", which is the default for new Swift projects.
Please also refer to this Swift proposal for more information.
- What is #objc inference? What is going on?
In Swift 3, the compiler infers #objc in a number of places so you wouldn't have to. In other words, it makes sure to add #objc for you!
In Swift 4, the compiler no longer does this (as much). You now must add #objc explicitly.
By default, if you have a pre-Swift 4 project, you will get warnings about this. In a Swift 4 project, you will get build errors. This is controlled via the SWIFT_SWIFT3_OBJC_INFERENCE build setting. In a pre-Swift 4 project this is set to On. I would recommend to set this to Default (or Off), which is now the default option on a new project.
It will take some time to convert everything, but since it's the default for Swift 4, it's worth doing it.
- How do I stop the compiler warnings/errors?
There are two ways to go about converting your code so the compiler doesn't complain.
One is to use #objc on each function or variable that needs to be exposed to the Objective-C runtime:
#objc func foo() {
}
The other is to use #objcMembers by a Class declaration. This makes sure to automatically add #objc to ALL the functions and variables in the class. This is the easy way, but it has a cost, for example, it can increase the size of your application by exposing functions that did not need to be exposed.
#objcMembers class Test {
}
- What is #objc and why is it necessary?
If you introduce new methods or variables to a Swift class, marking them as #objc exposes them to the Objective-C runtime. This is necessary when you have Objective-C code that uses your Swift class, or, if you are using Objective-C-type features like Selectors. For example, the target-action pattern:
button.addTarget(self, action:#selector(didPressButton), for:.touchUpInside)
- Why would I not mark everything #objc?
There are negatives that come with marking something as #objc:
Increased application binary size
No function overloading
Please keep in mind that this is a very high-level summary and that it is more complicated than I wrote. I would recommend reading the actual proposal for more information.
Sources:
https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WritingSwiftClassesWithObjective-CBehavior.html#//apple_ref/doc/uid/TP40014216-CH5-ID86
Migrator cannot identify all the functions that need #objc
Inferred Objective-C thunks marked as deprecated to help you find them
• Build warnings about deprecated methods
• Console messages when running deprecated thunks
I had this warning with "Swift 3 #objc Inference" = "Default" setting. Then I realized that is was set for the Project - not for the target. So, make sure that you have "Default" setting in your target to get rid of the warning.
You can simply pass to "default" instead of "ON". Seems more adherent to Apple logic.
(but all the other comments about the use of #obj remains valid.)
Indeed, you'll get rid of those warnings by disabling Swift 3 #objc Inference.
However, subtle issues may pop up. For example, KVO will stop working.
This code worked perfectly under Swift 3:
for (key, value) in jsonDict {
if self.value(forKey: key) != nil {
self.setValue(value, forKey: key)
}
}
After migrating to Swift 4, and setting "Swift 3 #objc Inference" to default, certain features of my project stopped working.
It took me some debugging and research to find a solution for this.
According to my best knowledge, here are the options:
Enable "Swift 3 #objc Inference" (only works if you migrated an existing project from Swift 3)
Mark the affected methods and properties as #objc
Re-enable ObjC inference for the entire class using #objcMembers
Re-enabling #objc inference leaves you with the warnings, but it's the quickest solution. Note that it's only available for projects migrated from an earlier Swift version.
The other two options are more tedious and require some code-digging and extensive testing.
See also https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md
I'm an occasional iOS dev (soon to be more) but I still couldn't find the setting as guided by the other answer (since I did not have that Keychain item the answer shows), so now that I found it I thought I might just add this snapshot with the highlighted locations that you will need to click and find.
Start in the upper left
Choose the project folder icon
Choose your main project name below the project folder icon.
Choose Build Settings on the right side.
Choose your project under TARGETS.
Scroll very far down (or search for the word inference in search
text box)
You can try to "Pod update" and/or "flutter clean"
I also set this setting in xcode.
The Objective-C interface setting is as follows:
Swift 3 #objc Inference
The use of Swift 3 #objc inference in Swift 4 mode is deprecated. Please address deprecated #objc inference warnings, test your code with “Use of deprecated Swift 3 #objc inference” logging enabled, and then disable inference by changing the "Swift 3 #objc Inference" build setting to "Default" for the "XMLParsingURL" target.
got to the
First step got Build Setting
Search in to Build Setting Inference
change swift 3 #objc Inference Default
enter image description here
The use of Swift 3 #objc inference in Swift 4 mode is deprecated?
use func call #objc
func call(){
foo()
}
#objc func foo() {
}
All you need just run a test wait till finish, after that go to Build Setting, Search in to Build Setting Inference,
change swift 3 #objc Inference to (Default). that's all what i did and worked perfect.
On top of what #wisekiddo said, you can also modify your build settings in the project.pbxproj file by setting the Swift 3 #obj Inference to default like SWIFT_SWIFT3_OBJC_INFERENCE = Default; for your build flavors (i.e. debug and release), especially if you're coming from some other environment besides Xcode

Xcode 7.3 undeclared type for Obj C enum in Swift code

In my project I have Swift extensions over Objective C enums, which worked brilliantly in Xcode 7.2.
But with Xcode 7.3 it fails with "undeclared type" in the Swift file where I extend the Obj C enum.
I've built a sample project and it compiles and works well, but I can't make the existing project accept the extension over the Obj C enum.
UPDATE:
After reinstalling Xcode 7.2 I can confirm that the project compiles and builds successfully.
After it worked in Xcode 7.2, I tried launching this project again in Xcode 7.3 and again the same issue -> the Swift extension over Obj C enums can't be build.
After cleaning and deleting the derived data in Xcode 7.3 I receive also the -Swift.h header missing error because the Swift classes haven't been compiled so a header wasn't created yet.
Code explanation:
My Obj C enum inside "FriendRequestResult.h":
typedef NS_ENUM(NSInteger, FriendStatus)
{
FriendStatusRequestedByUser = 1,
FriendStatusRequestedByOtherUser,
FriendStatusFriends,
FriendStatusBlocked,
FriendStatusNone,
FriendStatusError,
};
Now, as expected in my AppName-Bridging-Header.h I have:
#import "FriendRequestResult.h"
Then, I have the swift extension over the FriendStatus which builds in Xcode 7.2, but fails with "use of undeclared type" in Xcode 7.3:
extension FriendStatus
{
init(stringValue : String?)
{
if let stringValue = stringValue
{
switch stringValue
{
case "REQUESTED_BY_USER": self = .RequestedByUser
case "REQUESTED_BY_OTHER": self = .RequestedByOtherUser
case "FRIENDS": self = .Friends
case "BLOCKED": self = .Blocked
default: self = .None
}
}
else
{
self = .None
}
}
}
Actually this extension over the enum has also some other helper functions, but that should not change the problem in any way.
Of course if the extension of the enum gives the undeclared type, then using this type fails everywhere in the Swift code with the same "undeclared things". Basically the enum is not visible at all for the Swift part of the project, even though the import is made in the bridging header.
This question was substantially edited from its first version.
Solution 1:
Moved the enum in a seaparate header file.
My enum declaration was in the same header as a class header and specifically it was between the #interface and #end of that class.
In Xcode 7.2 it was creating no issues and the parsing of the header was successful, while in Xcode 7.3 they probably optimised something and changed the way it's parsed, so it was seeing my class, but not the enum inside it [maybe it declares it as private if it's inside a class declaration]
Solution 2: Moved the enum declaration outside the #interface #end scope.
To answer your question: "Can I somehow force Xcode to generate the Swift header first? Or can I force Xcode to believe that my enums exist somewhere and that it should validate my extensions?"
I found 2 ways to order file compilation.
Using a target and making it a dependency of your project.
Opening the project.pbxproj file and editing the list manually.
Depending on the level of risk and complexity you are ready to tackle, pick one or the other.
I just tried extending an ENUM and things are working fine here. One issue I had in the past was understanding the name stripping convention between Obj-C and Swift but this doesn't look like the issue you are running into.
The other issue I encounter invariably is maintaining the ##$% Bridging-Header.h file all the time. Are you positive this is up-to-date?

How to silence a warning in Swift?

I have a piece of code which is generating lots of warnings (deprecated API)
Using clang* I could do:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
...
#pragma clang diagnostic pop
However this does not work in Swift.
How to do it in Swift?
Note: I don't want to disable the warning globally, nor even file wide, but just disable a specific warning in a specific part of my source code.
I do not want conditional compilation (which is the proposed answer of the supposed duplicate).
I just want to silence a warning WITHOUT using the new APIs.
As of 2021, Xcode 13.0, the consensus is that there is no way to achieve that.
I'll update/edit this answer if Apple add the feature.
Put it in your wish list for WWDC 2022 !
There is no general construct to silence deprecation warnings in Swift, but there is a workaround that can be applied in many cases.
Let's say you have a method getLatestImage() on class Foo which uses deprecated methods/classes.
Use #available as Daniel Thorpe described to silence all the warnings inside the method:
class Foo {
#available(iOS, deprecated: 9.0)
func getLatestImage() -> UIImage? {
...
}
}
Now you would like to call the method getLatestImage() without having a deprecation warning. You can achieve that by first defining a protocol and an extension:
private protocol GetLatestImage {
func getLatestImage() -> UIImage?
}
extension Foo: GetLatestImage {}
And then call the method without a deprecation warning.
If foo is an instance of Foo:
(foo as GetLatestImage).getLatestImage() // no deprecation warning
If you want to call a static property/function of Foo:
(Foo.self as GetLatestImage.Type).someStaticProperty
The result is you have Swift code that uses deprecated API without any deprecation warnings.
Actually, you can suppress these warnings by using #available in the enclosing logical structure (i.e. function/type).
For example, say you have some code which uses the AddressBook framework, but you're building against iOS 9.
#available(iOS, deprecated: 9.0)
func addressBookStatus() -> ABAuthorizationStatus {
return ABAddressBookGetAuthorizationStatus()
}
As of Xcode 7.0.1 this will prevent the inline warnings from being displayed.
I was having the issue for a top level function outside of a class or struct:
#available(*, deprecated)
func GetImage(url: URL) -> UIImage? { ... }
I've talked to an engineer at Apple and they told me that you can hide the implementation by a protocol and mark extensions as deprecated. Let's see how it works:
Create a protocol that has a similar signature for the function you want to wrap.
Use the protocol on any class or struct in an extension.
Mark the extension as deprecated.
Everything that's within the extension does not bring up any deprecation warnings.
protocol ImageStoreProtocol {
func imageFromURL(_ url: URL) -> UIImage?
}
class ImageStore {}
#available(*, deprecated)
extension ImageStore: ImageStoreProtocol {
func imageFromURL(_ url: URL) -> UIImage? {
return GetImage(url: url) // Warning does't show up
}
}
While there’s no way to silence deprecation warnings in Swift for now, technically you can do that for a particular symbol by editing the header file.
Copy the deprecated symbol name
Select File > Open Quickly
Paste the symbol and press Enter
Make sure the Swift icon is disabled in the Open Quickly box
Select File > Show in Finder
Change file permissions to allow editing if necessary
Edit the deprecation macros for the symbol. See surrounding APIs for reference. E.g. replace:
__OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_6, __MAC_10_10, __IPHONE_3_0, __IPHONE_8_0)
with
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
Now there’s one less distracting warning you can do nothing about.
I know, it’s dirty. But if there’s no replacement API available in the current SDK, it should be safe. Once a new version of Xcode comes out, the change will get overwritten and you will see the warning again. Then you can test the new SDK and OS to make sure the deprecated API is still available and did not get a replacement.
Please comment if you can come up with any downsides.