Swift 4 backwards compatibility - ios

I have the following code in my project in Xcode 8.3.3 (Swift 3.1):
let font = CGFont(provider!)
CTFontManagerRegisterGraphicsFont(font, &error)
But in Xcode 9 Beta (Swift 4), I get the following error:
Value of optional type 'CGFont?' not unwrapped; did you mean to use
'!' or '?'?
The error is because the initializer for CGFont that takes a CGDataProvider now returns an optional.
But when I apply the fix of:
let font = CGFont(provider)
CTFontManagerRegisterGraphicsFont(font!, &error)
The code no longer compiles in Xcode 8.3.3 with Swift 3.1 since font is not an optional and thus doesn't play nicely with the !.
Is there a way to make this work in both versions of Xcode? Is Swift 4 supposed to be backwards compatible (compile with Swift 3 compiler)?

This is a breaking change in Core Graphics not in Swift itself. API has changed, the initializer is now failable.
Use conditional compilation to make your code compile with both 3.1 and 4.0 compiler:
#if swift(>=4.0)
let font = CGFont(provider!)
#else
let font = CGFont(provider)!
#endif
CTFontManagerRegisterGraphicsFont(font, &error)

I ended up using the following method which allowed for backwards compatibility without conditional compilation (idea taken from this blog post):
func optionalize<T>(_ x: T?) -> T? {
return x
}
This way in both Xcode 8 and Xcode 9 I could use:
guard let font = optionalize(CGFont(provider)) else {
return
}
CTFontManagerRegisterGraphicsFont(font, &error)

Related

failed to produce diagnostic for expression, Xcode 11.4, Moya

After updating to Xcode 11.4, I started to get this error
Failed to produce diagnostic for expression please file a bug report
On:
let provider = MoyaProvider<EndPoint>(requestClosure: requestClosure)
I updated Moya but it doesn't look like the issue. Runs fine on previous Xcode versions.
full code:
let requestClosure: MoyaProvider.RequestClosure = {
[unowned self] (endpoint: Endpoint, done: #escaping MoyaProvider.RequestResultClosure) in
guard let request = try? endpoint.urlRequest() else { return }
self.authenticator.authenticate(request, done: { (request) in
done(.success(request))
})
}
provider = MoyaProvider<EndPoint>(requestClosure: requestClosure
I found the issue, Xcode 11.4 compile issues are not verbose enough when it comes to optionals and generics.
Moya version and Swift version were not changed.
An issue with generics:
let requestClosure: MoyaProvider<EndPoint>.RequestClosure
Had to explicitly define the type in the closure, which makes sense, but was not a requirement pre-Xcode version 11.4
let requestClosure: MoyaProvider<EndPoint>.RequestClosure = {
[unowned self] (endpoint: Endpoint, done: #escaping MoyaProvider.RequestResultClosure) in
guard let request = try? endpoint.urlRequest() else { return }
self.authenticator.authenticate(request, done: { (request) in
done(.success(request))
})
}
provider = MoyaProvider<EndPoint>(requestClosure: requestClosure
For me the I was getting that error because one of my property was of type Error and I was assigning a value of type LocalizedError to it. Changing the property type to LocalizedError fixed the problem.
It seems to be an Xcode bug for some cases at least. I renamed a protocol and then renamed it back and the issue was gone.

Conversion errors from Swift 3.0 to Swift 4.2 "Cannot assign value of type '[String]' to type '[AVMetadataObject.ObjectType]?'"

I am new to Swift/iOS development and am working on Converting a Swift 3.0 project to Swift 4.2 in order to support newer devices.
In the process I am getting the following error :
Cannot assign value of type '[String]' to type '[AVMetadataObject.ObjectType]?'
Below are the lines of code throwing the error:
let metadataOutput = AVCaptureMetadataOutput()
metadataOutput.metadataObjectTypes = [convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.upce),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.code39),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.code39Mod43),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.ean13),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.ean8),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.code93),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.code128),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.pdf417),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.aztec),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.interleaved2of5),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.itf14),
convertFromAVMetadataObjectObjectType(AVMetadataObject.ObjectType.dataMatrix)]// Support for AVMetadataObjectTypeQRCode / qr code is removed from the list.
The migrator helps at its best but sometimes the help is misleading.
You can populate the array just with the static members
metadataOutput.metadataObjectTypes = [.upce, .code39, .code39Mod43, .ean13, .ean8, .code93, .code128, .pdf417, .aztec, .interleaved2of5, .itf14, .dataMatrix]

How to Use Obsoleted Syntax of Swift 3 in Swift 4

To record video, while setting video codec as below:
sessionOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecTypeJPEG]
XCode says 'AVVideoCodecTypeJPEG' has been renamed to 'AVVideoCodecType.jpeg' and 'AVVideoCodecTypeJPEG' was obsoleted in Swift 3 and it suggests Replace 'AVVideoCodecTypeJPEG' with 'AVVideoCodecType.jpeg'
After doing that, XCode says 'jpeg' is only available on iOS 11.0 or newer.
The problem is I have to use iOS 10 and want to use Swift 4.
Is there any solution to use features like this in Swift 4 with iOS 10?
I think the right way to solve such issue is to use the new AVVideoCodecType.jpeg and the deprecated one AVVideoCodecJPEG, doing so:
if #available(iOS 11.0, *) {
sessionOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecType.jpeg]
} else {
sessionOutput.outputSettings = [AVVideoCodecKey : AVVideoCodecJPEG]
}

How do I conditionally use a class in Swift that may not be present in the targeted version of the OS?

This answer addresses how to use a Swift class from a framework that may not be linked to an app, but is present in the target OS. Relatedly, but a bit different, I'd like to know how to conditionally use a Swift class that may not be included in the target OS. For example, say I have an app, written in Swift, that targets iOS 7. I want to use WKWebView if I'm on iOS 8, and UIWebView if I'm on iOS 7. This doesn't work:
if NSClassFromString("WKWebView") == nil {
var wv = WKWebView()
} else {
var wv = UIWebView()
}
Because my app targets iOS 7, I get a compile-time error:
Undefined symbols for architecture i386:
"_OBJC_CLASS_$_WKWebView", referenced from:
__TMaCSo9WKWebView in MyWebKitViewController.o
I tried just assigning the WKWebView to an AnyObject property, but then I can’t call methods on it. So how can I get Swift to just check for these things at runtime?
I'm new to Swift so I don't know if this is Swift or not, but I found this on a different question. https://stackoverflow.com/a/25874127/1012144
http://floatlearning.com/2014/12/uiwebview-wkwebview-and-tying-them-together-using-swift/
if (NSClassFromString("WKWebView") != nil) {
// Use WebKit
} else {
// Fallback on UIWebView
}
The code in the OP is nearly correct; it just needed to use != instead of ==:
if NSClassFromString("WKWebView") != nil {
var wv = WKWebView()
} else {
var wv = UIWebView()
}
To make it work, be sure to set iOS Deployment Target to 7.0. Otherwise you get the undefined symbol errors.

Swift: Extracting / downcasting CFType based CoreText types in a CFArray

I am trying to port elements of the CoreAnimationText sample to Swift. I cannot figure out though, how to extract or downcast the elements of CTRun from an array, in order to pass them to functions that expect and act upon the Swift-ified CTRun type. I either get runtime errors or linking errors from the playground snippet below
import CoreText
import QuartzCore
let text = NSAttributedString(string: "hello")
var line: CTLine = CTLineCreateWithAttributedString(text)
var ctRuns:CFArray = CTLineGetGlyphRuns(line)
let nsRuns:Array<AnyObject> = ctRuns as NSArray
nsRuns.count // == 1
// Playground execution failed: error: error: Couldn't lookup symbols:_OBJC_CLASS_$_CTRun
let nsRun = nsRuns[0] as CTRun
nsRun.self
println(nsRun.self)
let anyRuns = nsRuns as AnyObject[]
// can't unwrap Optional null
let anyRun = anyRuns[0] as CTRun
anyRun.self
println(anyRun.self)
let cft:AnyObject = anyRuns[0]
// CTRun is not contstructable with AnyObject
let runGlyphCount = CTRunGetGlyphCount(CTRun(cft));
// Can't unwrap Optional.None
let concreteRuns = anyRuns as CTRun[]
let concreteRun = concreteRuns[0] as CTRun
concreteRun.self
println(concreteRun.self)
Any ideas - am I missing something obvious? From the WWDC sessions and interop guide, I am led to believe that "Swift just takes care of this".
According to dev forums, this functionality is implemented in 6.1.
All your sample lines compile without errors and work as expected in
the latest Xcode, 6.1 (6A1052c) .
Interoperability with CF objects is impoving. You may find another
issues, in such case it'd be reported as bug.

Resources