Swift multiple #available checks for bug in 2 versions of SDK - swift

Apple had a bug in the SDK, which I needed to implement a workaround, they have since fixed that bug but not for two versions of the SDK. I would like the workaround to apply to just those two versions, but there does not seem to be a way of specifying that with #available. #available's intent is to check a version or later.
For example, there was a bug in 14.3 and 14.4, but that was fixed in 14.5, I would like the workaround to not be in effect after that. I came up with this, however, I am pretty sure that the second check is not going to work on 14.3. Is there a way to turn off the check after a version, or provide a range of versions? Also, you are not able to provide || or && the #available if or guard statements.
guard #available(iOS 14.3, *), #available(iOS 14.4, *) else { return }
// work around code.
The best I can come up with is this, but that workaround will be there from 14.3 onward:
guard #available(iOS 14.3, *) else { return }
// work around code.

The best answer I can think of is this:
if #available(iOS 14.5, *) {
} else if #available(iOS 14.3, *) {
// workaround code
}
I know it's ugly, but it works. Unfortunately, Swift does not seem to provide a way to give a maximum value for #available.
Edit: A cleaner method, but one that does not use #available:
let versionNumber = (UIDevice.current.systemVersion as NSString).floatValue
guard versionNumber >= 14.3 && versionNumber < 14.5 else { return }
// workaround code
Edit 2: In Swift 5.6 (Xcode 13.3), you can do:
guard #available(iOS 14.3, *), #unavailable(iOS 14.5) else { return }
// workaround code

Related

Using previewCGImageRepresentation in both Xcode 13 and Xcode 12?

I’ve run into an issue where Xcode 13b2 (iOS 15 SDK) changed the Swift return type of AVCapturePhoto.previewCGImageRepresentation(). In Xcode 12.5.1 (iOS 14 SDK), this method returns Unmanged<CGImage>. In 13b2 - 13b4, it returns CGImage?.
I need my code to compile under both Xcode versions, since Xcode 13 has other issues, and can’t be used to submit builds to the App Store. I thought I was clever writing this, but it won’t compile, because it’s not conditional code compilation check but rather a runtime check:
extension AVCapturePhoto {
func stupidOSChangePreviewCGImageRepresentation() -> CGImage? {
if #available(iOS 15, *) {
return self.previewCGImageRepresentation()
} else {
return self.previewCGImageRepresentation()?.takeUnretainedValue()
}
}
}
Another possibility might be to create a user-defined Xcode setting, but I don’t think that can be done conditionally based on Xcode or SDK version.
There might be some unsafe pointer histrionics one can do…
Any other ideas?
You can make a check on the swift compiler version. An exhaustive list is available on wikipedia at the time of writing.
https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
extension AVCapturePhoto {
func stupidOSChangePreviewCGImageRepresentation() -> CGImage? {
#if compiler(>=5.5)
return self.previewCGImageRepresentation()
#else
return self.previewCGImageRepresentation()?.takeUnretainedValue()
#endif
}
}

Having trouble with MusicKit sample app provided by Apple

I am trying to build "Adding Content to Apple Music”, Music Kit sample app provided by Apple, on Xcode 9 beta 3. However I am having 4 errors like this : three “Ambiguous use of 'play()’” errors and one “Ambiguous use of 'pause()’”
Please tell me how to fix this if you already solved this problem.
func beginPlayback(itemCollection: MPMediaItemCollection) {
musicPlayerController.setQueue(with: itemCollection)
//Ambiguous use of 'play()’
musicPlayerController.play()
}
func beginPlayback(itemID: String) {
musicPlayerController.setQueue(with: [itemID])
//Ambiguous use of 'play()’
musicPlayerController.play()
}
// MARK: Playback Control Methods
func togglePlayPause() {
if musicPlayerController.playbackState == .playing {
//Ambiguous use of 'pause()’
musicPlayerController.pause()
} else {
//Ambiguous use of 'play()’
musicPlayerController.play()
}
}
I have found a similar question in the Apple's Dev Forums:
MPMusicPlayerController Swift4 - Ambiguous Use of Play
According to an entry writing a fix to work around the issue, you need to change this line in MusicPlayerManager.swift:
let musicPlayerController = MPMusicPlayerController.systemMusicPlayer
(musicPlayerController's type becomes MPMusicPlayerController & MPSystemMusicPlayerController with this code.)
To:
let musicPlayerController: MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer
(musicPlayerController is explicitly annotated as MPMusicPlayerController.)
In my opinion this is a bug of Swift related to SE-0156 Class and Subtype existentials and you should better send a bug report to Apple or swift.org.

Swift Guard-Let Statement snippet

Why autocomplete for guardlet snippet doesn't work in closures? For example:
DispatchQueue.main.async(execute: {
guardlet //no any suggestions by Xcode
})
Shouldn't I use this control statement in any closure? Or it's just wrong scopes in the snippet?
I'm using Xcode 8.2 (8C38).
It works in Xcode 8.2.1:
Make sure you do not have pending compilation errors in your project.
You can close Xcode and reopen it eventually.
DispatchQueue.main.async(execute: {
guard let x = x where x > 0 else {
// Value requirements not met, do something
}
})
N.B:- Hi,there are no such key available "guardlet" in xcode please write that code"guard let"

SDWebImage and Swift

I've succeeded in incorporating SDWebImage (written in Objective-C) with my Swift project - but its still acting a bit funny. Specifically, its giving me an error in the if statement inside the following Closure:
let completionBlock: SDWebImageCompletionBlock! = {
(image:UIImage!, error: NSError!, cacheType:SDImageCacheType, imageURL:NSURL!) -> Void in
if (image && cacheType == SDImageCacheType.SDImageCacheTypeNone) {
cell.productImageView.alpha = 0.0
UIView.animateWithDuration(1.5, animations: {
cell.productImageView.alpha = 1.0
})
}
}
cell.productImageView.sd_setImageWithURL(imageURL!, placeholderImage:UIImage(named:"Icon120pix.png"), completed: completionBlock)
The error I'm getting on that if statement is: Use of unresolved identifier 'SDImageCacheTypeNone'
This makes no sense because SDImageCacheTypeNone is one of the values defined in the SDImageCacheType typedef.
By the way, if I take that if statement out and leave just the statements inside it, everything works just fine.
So any ideas what I might be doing wrong here?
Have a look at this document in the Enumerations section for a more in depth explanation as to why Swift displays Objective-C enums differently.
Apple Swift Documents
In your code example the way to solve the problem is to use:
SDImageCacheType.None
Instead of
SDImageCacheType.SDImageCacheTypeNone

Winter 2015 / Lecture 10 - Broken Twitter Package

Trying to follow along and code the Smashtag project while watching the Lecture 10 iTunes video.
When I add the dowloaded Twitter package to my Smashtag project, XCode couldn't find the Tweet class when I made reference to it in the TweetTableViewController.
Because of the problem described above, I added the four classes belonging to the Twitter package individually to the project. XCode found the four classes but adding them in this manner generated 11 compile errors.
I'm using XCode Version 6.3 (6D570) which is subsequent to the iOS 8.3 release.
Has anyone else encountered this issue?
Thank you for reading my question.
~ Lee
Possibly not the most-correct (read: best practice) way to do this, but I'm going to chalk it up to doing what it takes to finish the course.
I just went through the list of compile errors and changed the relevant properties to var instead of let. Constants can't be changed and in the new version of Swift they can only be instantiated once. So for the sake of not rewriting too much code, I chose to make certain properties vars instead of lets.
Other bugs I found following the iTunes U course:
The named ‘handler:’ argument needs the name explicitly in a few places.
The simulator will show "TwitterRequest: Couldn\'t discover Twitter account type.” until you go to Settings (inside the simulator) and set the Twitter account. At this point I had to reboot the device, as the call is made in the ViewDidLoad, and thus is only called the first time the view loads. (Alternatively, you could close out the app from the app switcher in the simulator and relaunch that way.)
Here is a gist with corrected code that you can use as a Twitter package that will work with the course and has fixes for the aforementioned bugs, minus the Twitter account setting:
https://gist.github.com/mattpetters/ccf87678ccce0c354398
As Christian R. Jimenez said, "I went to Settings in the Simulated iphone and add my Twitter Account. And everything works perfect." in http://cs193p.m2m.at/cs193p-lecture-10-table-view-winter-2015/. I just added my Twitter Account and tested it, it works!
I had similar problems with the Twitter packages using Swift 2.0 and Xcode 7.2
I'm very new to Swift, so there is a good chance the changes I made are not best practices, but the updated files do work: https://gist.github.com/awaxman11/9c48c0b4c622bffb879f.
For the most part I used Xcode's suggested changes. The two larger changes I made were:
In Tweet.swift I updated the the IndexedKeyword struct's init method to use advanceBy() instead of advance()
In TwitterRequest.swift I updated the signature of NSJSONSerialization to conform to the new error handling system
I've just had a big session fixing the Twitter package files for this same version of Xcode.
It seems that what has broken is that in this version of Swift, constants ('let x...') may only be initialized once, so if a constant is given a value in the declaration ('let x = false'), it may not be changed in the init() function. The Twitter package gives some constants initial values, but then changes the values in the init() function.
My solution to this was to follow the styles suggested in the current version of the Apple Swift language book: declare (many of) the constants as implicitly unwrapped optionals, unconditionally assign a value to them in the init() function (which value may be nil), then test whether any of them are nil, and, if so, return nil from init().
See https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html, click "On This Page" and choose "Failable Initializers"
Also, in TwitterRequest.swift, I needed to add the parameter name 'handler:' in a couple of calls to performTwitterRequest(request, handler: handler).
As an example of constant initialization, in MediaItem.swift:
<< Original Code >>
...
public let aspectRatio: Double = 0
...
init?(data: NSDictionary?) {
var valid = false
if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
if let url = NSURL(string: urlString) {
self.url = url
let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
if h != nil && w != nil && h?.doubleValue != 0 {
aspectRatio = w!.doubleValue / h!.doubleValue
valid = true
}
}
}
if !valid {
return nil
}
}
...
<< Updated code >>
...
public let aspectRatio: Double
...
init?(data: NSDictionary?) {
if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
if let url = NSURL(string: urlString as String) {
self.url = url
let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
if h != nil && w != nil && h?.doubleValue != 0 {
aspectRatio = w!.doubleValue / h!.doubleValue
return
}
}
}
return nil
}
...