Swift version build configuration - swift

With Swift v4.2 they introduced the extension Bool.toggle(), I had this extension since earlier and now when I compile with Xcode10 it says Ambiguous use of 'toggle()'. I tried to make it ignore my own extension if Swift version is 4.2 or higher so I used the build configuration swift(>=) which should check if the current swift version is equal or higher than the specified version.
public extension Bool {
#if swift(>=4.2)
// DO NOTHING
#else
mutating func toggle() {
self = !self
}
#endif
}
This should only see the comment DO NOTHING when on Xcode10 and on Xcode9 it should see the extended method. But whats happening is when on Xcode10 it still sees the method and I still get the error "Ambiguous use" and on Xcode9 it sees the method as well. I also changed the Build Setting Swift Language Version to Swift 4.2 and still the compiler sees the method.

Related

Why private is unaccessible to the extension?

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 ..."

Is Xcode9 backwards compatible to Swift 3 (or 3.1)?

I've just started using Xcode9 (beta2). My understanding was that it wouldn't force me to immediately change my code. However, I'm getting a build errors coming up with a library I'm using (Gloss):
1) For the following code:
public func valueForKeyPath(keyPath: String, withDelimiter delimiter: String = GlossKeyPathDelimiter, logger: Logger = GlossLogger()) -> Any? {
I get:
Initializer 'init()' is internal and cannot be referenced from a
default argument value
2) For this code:
internal func flatMap<KeyPrime : Hashable, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime : ValuePrime] {
return Dictionary<KeyPrime,ValuePrime>(elements: try flatMap({ (key, value) in
return try transform(key, value)
}))
}
I'm getting:
Closure tuple parameter '(key: _, value: _)' does not support
destructuring
Both of these caused no problem with Xcode 8.3/Swift 3.1.
I am using Xcode9 without having chosen the option to update to Swift 4. There seems several possibilities:
There are some flags that need to be set to retain Swift 3.1 backwards compatibility
Xcode9 is in beta and just isn't ready fully
Xcode9 will not be completely backwards compatible to Swift 3.1
Thoughts?
Xcode 9 is cross compatible with Swift 3 and 4. However, you need to be on Swift 3.2 which is the minimum for Xcode 9.
You can find, list of languages supported by specific Xcode, from Build Settings of your Xcode Project.
Here is sample reference, how you can see it:
(For Xcode 9, it supports, Swift 3.2 and Swift 4.0)
Here is reference answer, how you can easily switch from Swift 3.0 to 4.0
XCode - upgrade to Swift 4 manually

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.

GCC_VERSION equivalent for Swift code? [duplicate]

I know I can find the version of Swift I'm running right now reverting to a Terminal and typing:
xcrun swift --version
Swift version 1.1 (swift-600.0.57.4)
Target: x86_64-apple-darwin13.4.0
Also, I've been reading about the Preprocessor Macros in Swift, but no luck finding a Swift version constant.
As Swift 1.2 approaches it will be nice to flag old code that only runs on Swift 1.1 (Xcode up to 6.2) or new code that needs Xcode 6.3 (Swift 1.2)
Note: I can also use system() to do something like:
system("xcrun swift --version | grep version > somefile.txt")
Then open somefile.txt, but rather prefer some simpler solution
You can use conditional compilation directives to test for the specific Swift version used to build your project:
#if swift(>=5.0)
print("Hello, Swift 5!")
#elseif swift(>=4.0)
print("Hello, Swift 4!")
#elseif swift(>=3.0)
print("Hello, Swift 3!")
#elseif swift(>=2.2)
print("Hello, Swift 2.2!")
#elseif swift(>=2.1)
print("Hello, Swift 2.1!")
#endif
Finally got a workaround to do this. I'm using the constants prefixed with __ you can observe in your Playground. This would have been easier with some level of reflection, but...
__IPHONE_OS_VERSION_MAX_ALLOWED is 80200, meaning __IPHONE_8_2 for Xcode 6.2 (Swift 1.1) but its value is 80300 (__IPHONE_8_3) in Xcode 6.3 (Swift 1.2)
func isSwift12() -> Bool {
return __IPHONE_OS_VERSION_MAX_ALLOWED == 80300
}
isSwift12()
So now in your library you can fail fast and tell your user Swift's version is not correct using this:
assert(isSwift12(), "Need Swift 12")
Swift will give you a nice:
assertion failed: Need Swift 12: file , line 20
UPDATE WWDC 2015 - Swift 2.0
As stated in Apple's Swift blog, in Swift 2.0 we have #available blocks to check for certain OS versions in our code. An example should be:
if #available(OSX 10.11, *) {
monochromeFilter!.setValue(CIColor(red: 0.5, green: 0.5, blue: 0.5), forKey:kCIInputColorKey)
} else {
// Fallback on earlier versions
}
Swift 3.1 extends the #available attribute to support specifying Swift version numbers in addition to its existing platform versions.
// Swift 3.1
#available(swift 3.1)
func intVersion(number: Double) -> Int? {
return Int(exactly: number)
}
#available(swift, introduced: 3.0, obsoleted: 3.1)
func intVersion(number: Double) -> Int {
return Int(number)
}
From your comment:
I want to check because different versions of Swift have different features
You should not check the version of your programming language in order to use some features or not. This approach is much better:
if (self.respondsToSelector(Selector("yourMethodSelector"))) {
self.yourMethodSelector(test, sender: self)
} else {
//error handling
}
Just check whether a method is available or not.
Open up a command line on your Mac computer
type swift -version, without the double dashes, which doesn't work any more for Xcode 11/Swift 5.2.2
Of course, you'll have to update to latest IDE, which always downloads the latest version of both Xcode, languages, compilers and tools... EVERYTHING is updated. If you do that, that solution works for sure.
For iOS :
var systemVersion = UIDevice.currentDevice().systemVersion;
For OSX :
var systemVersion = NSProcessInfo.processInfo().operatingSystemVersion;
K.